想一个问题,在高并发中,如何部署奇数个节点?
两地三中心,是有钱的公司,为保障数据安全和高可用,一个常见的需求,通常指的是 “同城双活,异地备份”。
2 + 1 = 3,从描述上来看,就知道它们之间是有阶级属性的。
异地备份的机房,level 上自然就比同城双活的两个机房低了一个档次,否则也不会沦为备胎。辩证地看待这个问题,我们就能够自如地处理感情上脚踏多只船的问题。
1. 部署结构
为了描述方便,我们把同城的两个机房,称为 A 和 B。把可怜的备份机房,称作机房 C。
同城的两个机房,距离上自然就近了一些。我们可以用图直观地表示一下这个距离差异。
所以这个备份机房,非常的没有存在感。实际上,它也非常的有自知之明,只把自己放在一个备份的场景,能够接受非常大的请求延迟和比较长的数据不一致窗口。
这么算下来,就只剩下 A 和 B 两位陪你玩了,此之为双活。
2. 奇数节点的意义
双活的意思,是两个机房要同时对外提供服务。运行在不同机房的服务,分为两种,一种是有状态的,一种是无状态的。
无状态的服务,由于自身并不存储数据,只是作为传话筒,处理上自然行云流水,没什么值得好讨论的。
难搞的是有状态的服务。即使它像鱼一样记忆只有 5 秒,这部分记忆依然会对整个系统提出了高标准的要求------
我们需要有个集中的地方来存储这些数据。
大家都是搞技术的,那就举例几个常见的组件。
Zookeeper 动物园,需要做集中的配置中心或者分布式协调工作
Redis Cluster 需要处理一些全局的缓存数据
ElasticSearch 进行数据存储
无数个案例告诉我们,要部署这些服务,得部署奇数个节点才行。为什么不能部署偶数个?因为有个问题,那就是脑裂。
我们拿 Zookeeper 来说,假如我们部署了 6 个节点,那么你要两个集群能够可用,需要至少 4 个存活才行。你要是设置成了 3 个,那它就会出现问题。
如下图,在 6 个节点的场景中,A 和 B 机房网络产生了闪断。A 机房的三个节点发现不能再连接 B 机房的节点,于是它们三个自己组个集群,并写入了 a = 100, b = 300 万条数据;同理,B 机房也组了个局,写入了 a = 100, b = 600 两条记录。
而且它们都写成功了。
一个集群变成了两个,并写入了不同的数据。那我到底以谁的数据为准呢?真是要了命。
这就是脑裂问题,我们不能把集群要求的最小节点设置成 3,而是起码要为 4。
所以你看不管是 ES 还是 raft 协议,不管是 paxos 和 zab,都推荐部署奇数个节点,然后把最小可用集群节点设置成 (n / 2 + 1) --- 此所谓有一半以上节点投票才成,且有更好的容错性。
3. 如何部署奇数个节点
那这个问题该如何解决呢?
假如是同城三活,那么我们只需要在每个机房部署一个节点就可以了。但即使是双活,都是公司非常有钱才能搞得起。现在搞个三活,你大概率会赢得老板一个心虚的白眼。
当然也可以采用 2 + 2 + 1 的模式。
找不到一个专用的机房部署一套集群,但找几个第三方的服务器,部署一下我们的几个服务节点倒是可以的。
听起来很美好,但实际上不会这么做。因为这批第三方的服务器,对带宽、延迟 、安全、稳定的要求,一点都不低。
还是老老实实地在两个中心玩吧,野花野草闻着香,但大概率有毒。
实际上,即使是姐妹花,A 和 B 总是有些差异。只要我们别把 A 和 B 看得太对等,问题就好处理。
如上图,在 A 机房部署 3 个节点,在 B 机房部署 2 个节点。只要你这么部署了,在你的脑子里,A 就是要 B 的 level 高一些,虽然你对外宣称它们是一样的。
就像你脚踏两只船,你和 2 人说都很爱 ta。但一旦 ta 俩有冲突,你还是会毫不犹豫地选一个。
这就是考验。
我们切回上图,看一下几种情况。在这种部署情况下,当发生脑裂,B 机房的 2 个节点是无法提供服务的,所以也不会有异常数据进入。
当 B 机房整个发生问题,A 机房还是能够正常运行。
当 A 机房整个发生问题,B 机房此时只有 2 个节点,不满足最小的 3 个节点。这个时候该怎么办呢?
没错,我们手动启动一个。你看节点 6 的边框是虚线的,也就意味着它是一个待命状态,随时待命转正,完全地接管 A 的工作。
代价也是有的,毕竟 A 才是你心中的 No.1。ta 离你而去,给你造成了困扰。自己的选择,就是含着泪,你也得把 B 给顶上去。
相信我,不过是小时段的阵痛,你很快会再次进入双活的世界。到时候你是把 B 当作 No.1,还是继续换回 A,都没有问题。
而且这选择很没意义。
比起谁的 level 高,你想要双活的根本原因,那就是谁都不相信。所以,就把未来交给薛定谔的猫吧----
谁让你是个多情又多疑的程序员呢。
评论