为了实现高可用性,微服务一般部署在多机房,只要部署到多机房就万无一失了?考虑如下问题:
1 多机房负载均衡
当服务部署在多个机房时,最简单的就是遵循用户就近访问原则,比如北方用户访问联通机房,南方用户访问电信机房。
为了实现负载均衡,还会在每个机房分别部署四层负载均衡器VIP以及七层负载均衡器Nginx。 比如来自北方用户的请求:
- 通过DNS解析到联通机房下任一VIP
- 然后通过VIP,把请求转发给联通机房下任一Nginx
- Nginx再把请求转发给联通机房下任一Tomcat容器
以实现各个机房内高并发访问下的负载均衡。
这是最理想情况,实际部署时经常会遇到:
- 某机房流量较大,但该机房服务器不足以支撑线上流量
- 某机房服务有问题,需切一部分流量到另外一个机房
因此实际部署时,有时并不能完全遵循就近访问原则,而是要根据需要调配流量,达到各个机房流量均衡。
可以通过两种方法来切换流量:
- 在DNS解析时,把一部分北方用户请求解析到电信机房的VIP反之亦然
- 在Nginx转发请求时,把一部分电信机房的Tomcat容器配置到联通机房的Nginx的upstream里或把一部分联通机房的Tomcat容器配置到电信机房的Nginx的upstream里
2 多机房数据同步
想要实现服务部署到多机房,供用户访问是有前提的,即每个机房的数据都一样,这就要求多机房间数据必须保持同步。
高并发访问的服务,数据通常都会有两层存储即缓存层和数据库层
要保证多机房的数据一致,不仅要保证数据库层的数据一致,还需要保证缓存层的数据一致,应该如何实现呢?
1.主从机房架构
以一个机房为主机房,所有写请求都只发给主机房:
- 主机房的处理机更新本机房的缓存和DB
- 其他机房的缓存也通过主机房的处理机更新
- DB通过MySQL的binlog实现数据同步
- 主从机房数据同步方案
这样做有一个很大的风险,若主机房出现问题,就没法更新缓存和DB,所以有了下个方案。
2.独立机房架构
联通和电信机房都有写请求,通过消息同步组件把各自机房的写请求同步一份给对方机房,相当于每个机房都有全量写请求。
- 每个机房的处理机接收到写请求后更新各自的缓存
- 只有一个机房会更新DB,其他机房DB通过MySQL的binlog同步
该架构的优势是任一机房异常,都不影响其它机房数据更新,因为每个机房都是全量写消息,所以每个机房可以更新自己缓存,并从DB主库同步数据。
消息同步组件是如何实现的呢? 消息同步组件是把一个机房写请求发给其它机房,实现原理如下:
- reship 把本机房的写请求分发一份给其它机房
- collector 从其它机房读取写请求,再把请求转发给本机房处理机
如何
实现消息同步功能
MQ实现
- 采用MQ的实现方案
联通机房的写请求写入到联通机房的MQ,然后联通机房的reship就从MCQ读,写到电信机房MQ。 电信机房collector就能从电信机房MQ读取到写请求,再写到电信机房的另一个MQ,电信机房的队列机就会从这个MQ读出写请求,然后更新缓存。
这种方案缺点:
- 流程比较长,需要多次与MQ交互,当有大量写请求时,不仅要扩容reship和collector,还需要扩容MQ以确保能够承受大量读取和写入。
更简单方案如下:
RPC调用实现
联通机房写请求会调用联通机房reship RPC=》调用电信机房collector RPC=》调用电信机房的处理机RPC,达到把联通机房写请求同步给电信机房处理机
多机房数据一致性
还要确保同步后的数据是一致的,因为同步过程各种原因都会导致各机房间数据不一致,需要确保数据一致性。 不同业务对数据一致性要求不同,金融类业务要求多机房间数据必须强一致,其它类型业务则没这么高要求,只需要能达到最终一致即可。 常见的是通过消息对账机制来保证最终一致性。
系统会给每次写请求生成一个全局唯一requestId,联通机房写请求:
- 调用联通机房的处理机RPC修改缓存和DB
- 调用联通机房的reship RPC,reship RPC再调用电信机房的collector RPC同步写请求,电信机房的collector RPC最后会调用电信机房的处理RPC来更新缓存。整个过程requestId始终保持向下传递,无论是处理成功或失败都记录一条包含requestId和机房标记的处理日志,并写到Elasticsearch集群。然后通过一个定时线程,每隔1分钟去扫描Elasticsearch集群上的日志,找出包含同一个requestId的不同机房的处理日志,然后验证是否在各个机房请求都处理成功了,如果有的机房某一阶段处理失败,则可以根据日志信息重试该阶段直到成功,从而保证数据的最终一致性。