基于MQ的微服务架构
文章目录
2015年我们的团队已基于MQ实现了服务的纵向切分,实现了统一网关,多语言服务接入。正在这时微服务的概念也开始火起来了。
现在把这一套东西分享出来。
纵向切分的目标
- 热部署。服务可插拔,发布一个新的服务只需开启进程即可。
- 服务隔离。故障局部化,取决于服务切分的颗粒。
- 兼容升级。多版本服务共同存在,确定客户端所有老版本都不再使用的情况下,下线老版服务。
- 多语言支持。我们内部的进程有Golang,Python和php。
- 多实例。相同服务可以开启多个实例,增加吞吐量,实现灾难恢复,提高容错能力。
架构图
架构图的核心部分其实就是用MQ实现的RCP。业务进程做为RPC的server,gateway做为RPC的client。
gateway(用Go实现)
一、gateway是由很多个http服务组成的,通过nginx代理做负载均衡(也可以使用Haproxy,lvs之类),gateway只在架构之初需要编写,当它上线后不再需要更改升级和重新部署(除非是有bug),它是一个通用的部分,业务无关的(但是也可以做一些类似token认证,sign检查之类的事情,这些事情比较通用固定)。
下面是一个客户端请求gateway的示例:
|
|
POST参数:
|
|
module:表示要访问的业务对应的后台进程。 version:表示服务的版本,在兼容升级中会用到。 method:表示业务中具体的API方法。 bizContent:表示业务API的参数,是一个JSON字符串。 上面例子,是调用微信小程序模块1.0版本,生成一个SessionKey,换取要用到的Code为"d3JmZGZkc2Y=”
二、gateway和MQ之间的通信
gateway有一个接收消息的线程从进程开启后就马上起来。还有很多处理请求的线程不断的创建和结束。
当gateway收到client上的HTTP请求后,在当前处理线程会发送一个消息到MQ上。
队列为module和version拼接而成,如"server_wxapp_1.0”,
消息体的内容有,method、bizContent、gateway的回应队列名、当前处理线程标识。
当前处理线程进入等待状态,直到另一个进程把结果告诉它,然后把结果写入到HTTP的响应
如果12s后还是等不到结果,会将一个响应超时的错误写入到HTTP的响应
同时gateway的另一个线程一直在等待MQ的回应消息,他收到的消息是不同的处理进程需要的结果,这个线程类似一个死循环(这个线程存在的原因是MQ的消息是异步过程,而http请求是同步的)。
收到的消息包含的内容为:业务进程处理的结果、刚才处理线程的标识。
一但收到消息,马上通过线程通信将结果发送给标识指定的线程
通过下面的时序图了解一次http请求的过程。
业务进程
每个业务进程都有一个固定的队列名,比如"server_wxapp_1.0”。
相同的业务进程可以启动多个实例。多个实例订阅同一个队列,天然形成了一种竞争的关系,达到了负载均衡的作用。
在修复了bug或业务改动后。我们可以先开启新的进程,然后杀掉到老的进程,这样服务就没有真空期了,实现了平滑升级。
我们可让两个不同版本的进程同时存在,比如"server_wxapp_1.0"和"server_wxapp_1.1"同时存在,表示一部分老客户端可以正常使用老的API,新的客户端可以使用新的API。通过这种方式很轻松就可以实现灰度发布。
而这些进程的开启、关闭和故障,并不影响gateway的正常动行,只会影响到本模块、本版本的API。所以在灾难发生时,大部分情况都是服务降级的过程,部分功能暂停(这取决于模块拆分的科学性)。
因为RabbitMQ本身支持不同的开发语言,所以业务进程可以用这些不同的编程语言来开发。
其它
日志功能,通过MQ的队列数据旁听可以监控所有服务器之间消息的内容。方便做日志的统一管理和分析。
gateway超时请求线下排查。对于超时的情况,可以会造成数据的不一致情况,运维过程中要找到这些超时记录,分析原因,并快速修复问题,不允许慢的业务进程存在(要远小于12s)。
RabbitMQ rpc代码参考,http://www.rabbitmq.com/tutorials/tutorial-six-go.html
文章作者 农天狼
上次更新 2017-05-30