网站首页 > 精选教程 正文
这个话题源自于我司一个项目的技术架构升级,由原来的模块编程调整为微服务编程,鹏哥所在的研发部负责提供升级的解技术支持。之前系统由4个模块组成,约定4个模块是通过Activity MQ解耦,但是模块之间难免会有一些需要等待对方返回才能下一步的调用,之前的解决方案是使用Activity MQ 的同步调用,现在系统升级改为使用RabbitMQ代替原来的ActivityMQ,异步的替换没有啥问题,但是同步的时候负责的开发就有点懵了,因为我的同事并没有提供rabbit mq 同步调用的方案,项目升级因此中断而造成了延误。
中午正好跟项目的负责人一起吃饭,他一直在跟我吐槽我们研发部对他们项目升级支持不够,没有提供MQ同步方法的支持,鹏哥听了火冒三丈,脑子一热直接怼了过去:MQ 产生的意义是什么,MQ的出现就是为了解耦,你们把他用在同步调用上,还怎么实现解耦,同步调用直接rest api 调用不就行了,使用MQ多此一举。对面项目经理瞬间无话,至此切换下一个话题。
吃完饭回来,冷静下来之后,想想MQ支持同步其实也不是没有好处,比如:
- 多实例间负载均衡
- 服务发现治理
- 快速服务横向扩容
幸亏当时对方经理对rabbitMQ不是很熟,不然丢人估计就丢大了,哈哈。
好了废话不多说,我们言归正卷。rabbit 实现同步调用其实依赖与其私有队列和发送确认,既利用声明一个不指定名称的队列,靠rabbit 来生成一个唯一的队列名,同时将这个队列名赋值给消息头里面的reply_to字段,以告诉生产者,这是一个返回信息。
原理很简单,我们来看一个具体的案例。我们先假设服务A需要调用服务B来完成A的业务逻辑,加入服务B有两个节点。
多实例间负载均衡
我们先来想一下,模块编程时代,我们是如何实现负载均衡的:我们会在服务B外层架一个负载均衡服务器,如nginx 等,然后再A调用B的时候,其实是通过nginx 代理的方式,路由到其中一个B服务的节点上,如下图:
?
这种行业的标准解决方案已经被业界无数次的验证可行性,这里就不多赘述了。现在我们来看一下MQ是怎么实现负载均衡的。这其实MQ天然的特性,并不是因为使用了同步调用才有的。在多个消费者监听同一个队列的时候,rabbit mq 使用简单的轮训以实现消费者的均衡,这种方式巧妙的将负载粗暴的平均到每一个消费实例上。rabbit mq在同步调用案例中,负载均衡的示意图如下:
?
服务发现治理
在微服务模式下,我们来想一下eureka的作用。服务A需要调用服务B的一个接口,但是它并不知道这个服务B在那台服务上,也不知道服务B有多少台可以工作的实例,这个时候eureka就出场了。首先服务A和服务B都注册到eureka服务器上,并把eureka的服务列表copy一份到服务器的本地,这样当A需要调用服务B的接口的时候,就可以根据服务路由表快速的找到可以提供服务的实例。
那模块编程时代,这个功能靠什么实现呢?rabbit mq 有一次拯救了我们。服务A在调用B的接口的时候,并不需要知道提供服务的是谁,只需要往对应的queue上发送消息即可,rabbit mq 负责将消息推送给监听这个queue的消费者。
快速服务横向扩容
扩容这个问题,如果是使用Nginx 作为代理服务器的话,每次扩容都需要修改Nginx的配置,将新的服务器信息添加到Nginx配置信息中去。这种方式给每一次的扩容带来工作量和风险。rabbit mq 在对待这个问题上也是天然的支持,当我们发现服务B 当前实例不能满足现在的请求压力的时候,我们只需要将B新部署实例连上对应的队列即可,无需修改任何配置。当我们不需要的这么多实例的时候,也可以直接停掉即可,也不用修改任何配置。
一个基于Spring boot的例子
说了这么理论,现在我们来实现我们吹过的牛。使用Spring boot + rabbit 来实现一个分布式系统的雏形。
新建一个client的项目,配置DirectExchange 和一个Queue,并绑定在一起。
?最基本的定义方法。
?
为了方便测试,我们提供一个rest api 负责发送和接受同步返回的消息。发送MQ的时候我用使用convertSendAndReceive 代替convertAndSend 来实现同步的调用,并接收返回值。
?
好了。client 端就完成了,现在我们来编写服务端。
?
编写一个receiver,负责监听client端定义的queue。并将Process 方法添加返回值。
?
你也许会好奇,为什么我没有配置rabbit的任何信息,这是因为,你要你在POM中引入了 rabbit的jar,并且没有修改默认的rabbit的用户名和密码,Spring boot 会默认帮你链接到rabbit上的。
?
?
使用docker 启动一个 MQ的实例,如果不会Docker 启动MQ 鹏哥会提供另外一篇博客来简单介绍一下。
?
分别启动 client 和Server
?
使用Postman 请求一下client 的API来模拟一个请求,同时client 会去向 Server 发送一个同步请求。
?
client端日志:
?
server 端日志:
?
至此我们使用 rabbit mq 模拟了一个同步的调用,下边我们来模拟我们的高可用和可伸缩。
修改服务端的启动配置,以便我们可以启动多个实例。
在启动完一个实例之后,修改端口号,再次启动,你会发现已经起了两个实例。?
我们来启动三个服务端实例,来模拟负载均衡。
?
使用postman 请求6次刚才的接口,我们在client 端 的日志里面看到了6个请求的返回值。
?
同时均匀的在服务端的3个实例上,分别承载了两次调用。
?
?
?
至此我们演示完了这个简易分布式系统的所有的功能。
项目代码地址:https://github.com/linghuxiong/spring-boot-demo/tree/master/spring-boot-rabbit-rpc
猜你喜欢
- 2024-10-27 kubernetes运维面试:k8s 集群如何做高可用?
- 2024-10-27 高可用架构的6大常规方案 高可用的概念
- 2024-10-27 一篇文章教会你如何搭建高可用高并发系统
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- nginx反向代理 (57)
- nginx日志 (56)
- nginx限制ip访问 (62)
- mac安装nginx (55)
- java和mysql (59)
- java中final (62)
- win10安装java (72)
- java启动参数 (64)
- java链表反转 (64)
- 字符串反转java (72)
- java逻辑运算符 (59)
- java 请求url (65)
- java信号量 (57)
- java定义枚举 (59)
- java字符串压缩 (56)
- java中的反射 (59)
- java 三维数组 (55)
- java插入排序 (68)
- java线程的状态 (62)
- java异步调用 (55)
- java中的异常处理 (62)
- java锁机制 (54)
- java静态内部类 (55)
- java怎么添加图片 (60)
- java 权限框架 (55)
本文暂时没有评论,来添加一个吧(●'◡'●)