实时消息RTM| 多活架构中的数据一致性问题
一,容灾方案
之前介绍过RTM的系统架构设计,其中有说到我们的容灾设计的是双活和多活。有很多小伙伴问到为什么不采用主从架构设计,以及多活容灾怎么做到数据一致性等问题。这里我们针对容灾和多活设计进行详细介绍。
容灾的方案有很多,最常见的就是主备容灾,最初版本我们也采用了主机+冷备机容灾模式:Client正常与Master连接进行通信,正常情况下只有主机提供服务;在主机出故障时,仲裁服务切换主备,原来的主机下线变成备机,原备机变成主机后为Client提供服务。
一主机一备机的模型设计简单,并且具有不错的可用性——毕竟主备两台机器同时不可用的概率极低,相信很多后台系统也采用了类似的容灾策略。
主备容灾存在一些明显的缺陷,比如备机闲置导致有一半的空闲机器;比如主备切换的时候,备机在瞬间要接受主机所有的请求,容易导致备机过载。既然一主一备容灾存在这样的问题,为什么还有大量的公司采用这种容灾模型?事实上,架构的选择往往跟项目背景有关,大多数情况项目需要的机器数少,机器冗余不是主要问题;其次主备架构简单稳定,可以快速开发,快速上线。
由于RTM系统对并发的要求非常高,同时由于项目对设备的使用率要求较高,所以我们采用了多活架构。
多活架构中每台机器都能为客户端提供服务,如果其中一台宕机或发生故障,Client只需要连接相同区域内其他的服务即可。多活架构可是使得机器的利用率最大化,降低服务运营的成本,同时对于运维来说也相对简单,当发生流量峰值时只需要平行增加机器即可,当流量较低时,可以减少机器;动态增加或减少机器对于成本的控制非常有效。多活架构看似非常美好,对于技术人员来说使用多活架构需要解决的技术难题非常多,其中一个就是分布式数据一致性问题。
二,一致性算法
一致性就是数据保持一致,在分布式系统中,可以理解为多个节点中数据的值是一致的。
1,为什么需要一致性
a)数据不能存在单个节点(主机)上,否则可能出现单点故障。
b)多个节点(主机)需要保证具有相同的数据。
c)一致性算法就是为了解决上面两个问题。
2,一致性的分类
a)强一致性
说明:保证系统改变提交以后立即改变集群的状态。
模型:
Paxos
Raft(muti-paxos)
ZAB(muti-paxos)
b)弱一致性
说明:也叫最终一致性,系统不保证改变提交以后立即改变集群的状态,但是随着 时间的推移最终状态是一致的。
模型:
DNS系统
Gossip协议
具体的一致性算法的介绍大家可以自行搜索,其中的一些思想非常值得去深究,都是大牛们智慧的结晶。
RTM系统中有个非常经典的场景,一个群组里面有N个人分布在不同的区域,这时候有些人同时对群组的某一个相同属性进行修改;属性的修改需要通知群里的所有人,这个时候如果没有数据一致性的保证,有极大的可能会导致群里的N个人中一部分人收到的数据去其他人收不到的不一样,如果是这样对于业务来说是致命的。
多个人在不同地点同时对属性A进行修改,由于网络的波动延时,导致Client-D、E、F收到的顺序完全不同;最终呈现的属性值也不同。因此我们参考了Paxos算法的一些思想:
P1: 一个Acceptor必须接受它收到的第一个议案。
P2: 如果一个值为v的议案被选定了,那么被选定的更大编号的议案,它的值必须也是v。
P2a: 如果一个值为v的议案被选定了,那么Acceptor接受的更大编号的议案,它的值必须也是v。
P2b: 如果一个值为v的议案被选定了,那么Proposer提出的更大编号的议案,它的值必须也是v。
P2c: 在所有Acceptor中,任意选取半数以上的Acceptor集合,我们称这个集合为S。Proposal新提出的议案(简称Pnew)必须符合下面两个条件之一:
1)如果S中所有Acceptor都没有接受过议案的话,那么Pnew的编号保证唯一性和递增即可,Pnew的值可以是任意值。
2)如果S中有一个或多个Acceptor曾经接受过议案的话,要先找出其中编号最大的那个议案,假设它的编号为N,值为V。那么Pnew的编号必须大于N,Pnew的值必须等于V。
算法的推导过程理解起来还是比较困难,我们大致总结了其核心点:一致性算法不是算法保证数据一致,他保证的是你数据写入顺序的一致。如果数据写入顺序一致,最终的结果肯定是一样的。
比如上面我们提到的经典场景,我们只要保证所有客户端收到的属性的顺序是一致的,不论属性是213,还是312,只要大家都是这个顺序,那么最终每个客户端上的最终属性值肯定是一样的。
三,总结
任何架构和算法最终都是服务于实际的业务,并不是一种架构胜任所有,应当从多角度、多维度找到适合的方案才是最优解。