本文由公众号“水滴与银弹”号主 Kaito 原创分享,原题“搞懂异地多活,看这篇就够了”,为使文章更好了解,有订正。
1、引言
前几天技术群里有群友问我有没有 IM 分布式系统异地多活方面的文章,我认真想了想,除了微信分享的几篇文章里有提到容灾和异地多活(只是大抵提过,没有具体开展),的确目前还没有系统性的异地多活技术材料可供参考。正好借此机会,整顿了 Kaito 分享的这篇供大家学习。
本文从一个简略的零碎例子开始,从单机架构、主从正本、同城灾备、同城双活,再到异地双活、异地多活,由浅入深、循序渐进地解说了大型分布式系统异地多活容灾架构的技术原理和根本的实现思路,非常适合入门者学习。
学习交换:
- 即时通讯 / 推送技术开发交换 5 群:215477170 [举荐]
- 挪动端 IM 开发入门文章:《新手入门一篇就够:从零开发挪动端 IM》
- 开源 IM 框架源码:https://github.com/JackJiang2…
(本文同步公布于:http://www.52im.net/thread-37…)
2、系列文章
本文是 IM 开发基础知识补课系列文章中的第 10 篇:
《IM 开发基础知识补课(一):正确理解前置 HTTP SSO 单点登陆接口的原理》
《IM 开发基础知识补课(二):如何设计大量图片文件的服务端存储架构?》
《IM 开发基础知识补课(三):疾速了解服务端数据库读写拆散原理及实际倡议》
《IM 开发基础知识补课(四):正确理解 HTTP 短连贯中的 Cookie、Session 和 Token》
《IM 开发基础知识补课(五):通俗易懂,正确理解并用好 MQ 音讯队列》
《IM 开发基础知识补课(六):数据库用 NoSQL 还是 SQL?读这篇就够了!》
《IM 开发基础知识补课(七):支流挪动端账号登录形式的原理及设计思路》
《IM 开发基础知识补课(八):史上最艰深,彻底搞懂字符乱码问题的实质》
《IM 开发基础知识补课(九):想开发 IM 集群?先搞懂什么是 RPC!》
《IM 开发基础知识补课(十):大型 IM 零碎有多难?万字长文,搞懂异地多活!》(* 本文)
以下是文章中也有对于容灾和异地多活的内容:
《疾速裂变:见证微信弱小后盾架构从 0 到 1 的演进历程(二)》
《IM 音讯 ID 技术专题(二):微信的海量 IM 聊天音讯序列号生成实际(容灾计划篇)》
《淘宝技术分享:手淘亿级挪动端接入层网关的技术演进之路》
3、内容概述
在软件开发畛域,「异地多活」是分布式系统架构设计的一座顶峰,很多人常常听过它,但很少人了解其中的原理。
异地多活到底是什么?为什么须要异地多活?它到底解决了什么问题?到底是怎么解决的?
这些疑难,想必是每个程序看到异地多活这个名词时,都想要搞明确的问题。
我已经有幸深度参加过一个中等互联网公司异地多活零碎的设计与施行过程。所以明天,我就来和你聊一聊异地多活背地的的实现原理。
认真读完这篇文章,我置信你会对异地多活架构,有更加粗浅的了解。
4、什么是零碎可用性
要想了解异地多活,咱们须要从架构设计的准则说起。
现如今,咱们开发一个软件系统,对其要求越来越高,如果你理解一些「架构设计」的要求,就晓得一个好的软件架构应该遵循以下 3 个准则。
它们是:
1)高性能;
2)高可用;
3)易扩大。
其中:
1)高性能:意味着零碎领有更大流量的解决能力,更低的响应提早(例如 1 秒可解决 10W 并发申请,接口响应工夫 5 ms 等等);
2)易扩大:示意零碎在迭代新性能时,能以最小的代价去扩大,零碎遇到流量压力时,能够在不改变代码的前提下,去扩容零碎。
而「高可用」这个概念,看起来很形象,怎么了解它呢?
通常用 2 个指标来掂量:
1)均匀故障距离 MTBF(Mean Time Between Failure):示意两次故障的间隔时间,也就是零碎「失常运行」的均匀工夫,这个工夫越长,阐明零碎稳定性越高;
2)故障复原工夫 MTTR(Mean Time To Repair):示意零碎产生故障后「复原的工夫」,这个值越小,故障对用户的影响越小。
可用性与这两者的关系:
可用性(Availability)= MTBF / (MTBF + MTTR) * 100%
这个公式得出的后果是一个「比例」,通常咱们会用「N 个 9」来形容一个零碎的可用性。
从这张图你能够看到,要想达到 4 个 9 以上的可用性,均匀每天故障工夫必须管制在 10 秒以内。
也就是说,只有故障的工夫「越短」,整个零碎的可用性才会越高,每晋升 1 个 9,都会对系统提出更高的要求。
咱们都晓得,零碎产生故障其实是不可避免的,尤其是规模越大的零碎,产生问题的概率也越大。
这些故障个别体现在 3 个方面:
1)硬件故障:CPU、内存、磁盘、网卡、交换机、路由器;
2)软件问题:代码 Bug、版本迭代;
3)不可抗力:地震、旱灾、火灾、和平。
这些危险随时都有可能产生。所以在面对故障时,咱们的零碎是否以「最快」的速度复原,就成为了可用性的要害。
可如何做到疾速复原呢?
这篇文章要讲的「异地多活」架构,就是为了解决这个问题,而提出的高效解决方案。
本文余下的内容,我将会从一个最简略的零碎登程,带你一步步演化出一个反对「异地多活」的零碎架构。
在这个过程中,你会看到一个零碎会遇到哪些可用性问题,以及为什么架构要这样演进,从而了解异地多活架构的意义。
5、单机架构
咱们从最简略的开始讲起。
假如你的业务处于起步阶段,体量十分小,那你的架构是这样的:
这个架构模型非常简单,客户端申请进来,业务利用读写数据库,返回后果,十分好了解。
但须要留神的是,这里的数据库是「单机」部署的,所以它有一个致命的毛病:即一旦遭逢意外(例如磁盘损坏、操作系统异样、误删数据),那这意味着所有数据就全副「失落」了,这个损失是微小的。
如何防止这个问题呢?咱们很容易想到一个计划:备份。
你能够对数据做备份,把数据库文件「定期」cp 到另一台机器上。这样,即便原机器失落数据,你仍旧能够通过备份把数据「复原」回来,以此保障数据安全。
这个计划施行起来尽管比较简单,但存在 2 个问题:
1)复原需工夫:业务需先停机,再复原数据,停机工夫取决于复原的速度,复原期间服务「不可用」;
2)数据不残缺:因为是定期备份,数据必定不是「最新」的,数据残缺水平取决于备份的周期。
很显著:你的数据库越大,象征故障复原工夫越久。依照后面咱们提到的「高可用」规范,这个计划可能连 1 个 9 都达不到,远远无奈满足咱们对可用性的要求。
那有什么更好的计划,既能够疾速复原业务?还能尽可能保障数据完整性呢?
这时你能够采纳这个计划:主从正本。
6、主从正本架构
针对上节中单机架构的问题,你能够在另一台机器上,再部署一个数据库实例,让这个新实例成为原实例的「正本」,让两者放弃「实时同步」。
就像这样:
咱们个别把原实例叫作主库(master),新实例叫作从库(slave)。
这个计划的长处在于:
1)数据完整性高:主从正本实时同步,数据「差别」很小;
2)抗故障能力晋升:主库有任何异样,从库可随时「切换」为主库,持续提供服务;
3)读性能晋升:业务利用可间接读从库,分担主库「压力」读压力。
这个计划不错:不仅大大提高了数据库的可用性,还晋升了零碎的读性能。
同样的思路,你的「业务利用」也能够在其它机器部署一份,防止单点。因为业务利用通常是「无状态」的(不像数据库那样存储数据),所以间接部署即可,非常简单。
因为业务利用部署了多个,所以你当初还须要部署一个「接入层」,来做申请的「负载平衡」(个别会应用 nginx 或 LVS),这样当一台机器宕机后,另一台机器也能够「接管」所有流量,继续提供服务。
从这个计划你能够看出,晋升可用性的要害思路就是:冗余。
没错:放心一个实例故障,那就部署多个实例,放心一个机器宕机,那就部署多台机器。
到这里:你的架构根本已演变成支流计划了,之后开发新的业务利用,都能够依照这种模式去部署。
但这种计划还有什么危险吗?
7、一个容易被可漠视的危险
当初让咱们把视角下放,把焦点放到具体的「部署细节」上来。
依照后面的剖析,为了防止单点故障,你的利用尽管部署了多台机器,但这些机器的散布状况,咱们并没有去深究。
而一个机房有很多服务器,这些服务器通常会散布在一个个「机柜」上,如果你应用的这些机器,刚好在一个机柜,还是存在危险。
如果恰好连贯这个机柜的交换机 / 路由器产生故障,那么你的利用仍旧有「不可用」的危险。
尽管交换机 / 路由器也做了路线冗余,但不能保障肯定不出问题。
部署在一个机柜有危险,那把这些机器打散,扩散到不同机柜上,是不是就没问题了?
这样的确会大大降低出问题的概率。但咱们仍旧不能漫不经心,因为无论怎么扩散,它们总归还是在一个雷同的环境下:机房。
那持续诘问,机房会不会产生故障呢?
一般来讲,建设一个机房的要求其实是很高的,地理位置、温湿度管制、备用电源等等,机房厂商会在各方面做好防护。
但即便这样,咱们每隔一段时间还会看到这样的新闻:
2015 年 5 月 27 日,杭州市某地光纤被挖断,近 3 亿用户长达 5 小时无法访问支付宝;
2021 年 7 月 13 日,B 站局部服务器机房产生故障,造成整站继续 3 个小时无法访问;
2021 年 10 月 9 日,富途证券服务器机房产生电力闪断故障,造成用户 2 个小时无奈登陆、交易;
…
可见:即便机房级别的防护曾经做得足够好,但只有有「概率」出问题,那现实情况就有可能产生(墨菲定律)。尽管概率很小,但一旦真的产生,影响之大可见一斑。
看到这里你可能会想,机房呈现问题的概率也太小了吧,工作了这么多年,也没让我碰上一次,有必要思考得这么简单吗?
但你有没有思考这样一个问题:不同体量的零碎,它们各自关注的重点是什么?
1)体量很小的零碎,它会重点关注「用户」规模、增长,这个阶段获取用户是所有;
2)等用户体量上来了,这个阶段会重点关注「性能」,优化接口响应工夫、页面关上速度等等,这个阶段更多是关注用户体验;
3)等体量再大到肯定规模后你会发现,「可用性」就变得尤为重要。像微信、支付宝这种全民级的利用,如果机房产生一次故障,那整个影响范畴能够说是十分微小的。
所以,再小概率的危险,咱们在进步零碎可用性时,也不能漠视。
剖析了危险,再说回咱们的架构。那到底该怎么应答机房级别的故障呢?
没错,还是冗余。
8、同城灾备架构
想要抵挡「机房」级别的危险,那应答计划就不能局限在一个机房内了。
当初,你须要做机房级别的冗余计划,也就是说,你须要再搭建一个机房,来部署你的服务。
简略起见,你能够在「同一个城市」再搭建一个机房,原机房咱们叫作 A 机房,新机房叫 B 机房,这两个机房的网络用一条「专线」连通。
有了新机房,怎么把它用起来呢?这里还是要优先思考「数据」危险。
为了防止 A 机房故障导致数据失落,所以咱们须要把数据在 B 机房也存一份。最简略的计划还是和后面提到的一样:备份。
A 机房的数据,定时在 B 机房做备份(拷贝数据文件),这样即便整个 A 机房受到重大的损坏,B 机房的数据不会丢,通过备份能够把数据「复原」回来,重启服务。
这种计划,咱们称之为「冷备」。
为什么叫冷备呢?因为 B 机房只做备份,不提供实时服务,它是冷的,只会在 A 机房故障时才会启用。
但备份的问题仍旧和之前形容的一样:数据不残缺、复原数据期间业务不可用,整个零碎的可用性还是无奈失去保障。
所以,咱们还是须要用「主从正本」的形式,在 B 机房部署 A 机房的数据正本。
架构就变成了这样:
这样:就算整个 A 机房挂掉,咱们在 B 机房也有比拟「残缺」的数据。
数据是保住了,但这时你须要思考另外一个问题——如果 A 机房真挂掉了,要想保障服务不中断,你还须要在 B 机房「紧急」做这些事件。
比方:
1)B 机房所有从库晋升为主库;
2)在 B 机房部署利用,启动服务;
3)部署接入层,配置转发规定;
4)DNS 指向 B 机房,接入流量,业务复原。
看到了么?A 机房故障后,B 机房须要做这么多工作,你的业务能力齐全「复原」过去。
你看:整个过程须要人为染指,且需破费大量工夫来操作,复原之前整个服务还是不可用的,这个计划还是不太爽,如果能做到故障后立刻「切换」,那就好了。
因而:要想缩短业务复原的工夫,你必须把这些工作在 B 机房「提前」做好,也就是说,你须要在 B 机房提前部署好接入层、业务利用,期待随时切换。
架构就变成了这样:
这样的话,A 机房整个挂掉,咱们只须要做 2 件事即可:
1)B 机房所有从库晋升为主库;
2)DNS 指向 B 机房,接入流量,业务复原。
这样一来,复原速度快了很多。
到这里你会发现,B 机房从最开始的「空洞无物」,演变到当初,简直是「镜像」了一份 A 机房的所有货色,从最上层的接入层,到两头的业务利用,到最上层的存储。
两个机房惟一的区别是,A 机房的存储都是主库,而 B 机房都是从库。
这种计划,咱们把它叫做「热备」。
“热”的意思是指:B 机房处于「待命」状态,A 故障后 B 能够随时「接管」流量,持续提供服务。
热备相比于冷备最大的长处是:随时可切换。
无论是冷备还是热备,因为它们都处于「备用」状态,所以咱们把这两个计划统称为:同城灾备。
同城灾备的最大劣势在于:咱们再也不必放心「机房」级别的故障了,一个机房产生危险,咱们只需把流量切换到另一个机房即可,可用性再次进步,是不是很爽?(前面还有更爽的 …)
9、同城双活架构
咱们持续来看上节的这个架构。
尽管咱们有了应答机房故障的解决方案,但这里有个问题是咱们不能漠视的:A 机房挂掉,全副流量切到 B 机房,B 机房是否真的如咱们所愿,失常提供服务?
这是个值得思考的问题。
这就好比有两支军队 A 和 B,A 军队历经疆场,作战经验丰盛,而 B 军队只是后备军,除了有军人的根本素养之外,并没有实战经验,战斗教训根本为 0。
如果 A 军队丢失战斗能力,须要 B 军队立刻顶上时,作为指挥官的你,必定也会放心 B 军队是否真的担此重任吧?
咱们的架构也是如此:此时的 B 机房尽管是随时「待命」状态,但 A 机房真的产生故障,咱们要把全副流量切到 B 机房,其实是不敢百分百保障它能够「如期」工作的。
你想:咱们在一个机房内部署服务,还总是产生各种各样的问题,例如:公布利用的版本不统一、系统资源有余、操作系统参数不一样等等。当初多部署一个机房,这些问题只会增多,不会缩小。
另外:从「老本」的角度来看,咱们新部署一个机房,须要购买服务器、内存、硬盘、带宽资源,破费老本也是十分昂扬的,只让它当一个后备军,未免也太「大材小用」了!
因而:咱们须要让 B 机房也接入流量,实时提供服务,这样做的益处有两个。
一是能够实时训练这支后备军,让它达到与 A 机房雷同的作战程度,随时可切换;
二是 B 机房接入流量后,能够分担 A 机房的流量压力。
这才是把 B 机房资源优势,施展最大化的最好计划!
那怎么让 B 机房也接入流量呢?很简略,就是把 B 机房的接入层 IP 地址,退出到 DNS 中,这样,B 机房从下层就能够有流量进来了。
但这里有一个问题:别忘了,B 机房的存储,当初可都是 A 机房的「从库」,从库默认可都是「不可写」的,B 机房的写申请打到本机房存储上,必定会报错,这还是不合乎咱们预期。怎么办?
这时,你就须要在「业务利用」层做革新了。
你的业务利用在操作数据库时,须要辨别「读写拆散」(个别用中间件实现),即两个机房的「读」流量,能够读任意机房的存储,但「写」流量,只容许写 A 机房,因为主库在 A 机房。
这会波及到你用的所有存储,例如我的项目中用到了 MySQL、Redis、MongoDB 等等,操作这些数据库,都须要辨别读写申请,所以这块须要肯定的业务「革新」老本。
因为 A 机房的存储都是主库,所以咱们把 A 机房叫做「主机房」,B 机房叫「从机房」。
两个机房部署在「同城」,物理间隔比拟近,而且两个机房用「专线」网络连接,尽管跨机房拜访的提早,比单个机房内要大一些,但整体的提早还是能够承受的。
业务革新实现后,B 机房能够缓缓接入流量(从 10%、30%、50% 逐步笼罩到 100%)你能够继续察看 B 机房的业务是否存在问题,有问题及时修复,逐步让 B 机房的工作能力,达到和 A 机房雷同程度。
当初:因为 B 机房实时接入了流量,此时如果 A 机房挂了,那咱们就能够「大胆」地把 A 的流量,全副切换到 B 机房,实现疾速切换!
到这里你能够看到:咱们部署的 B 机房,在物理上尽管与 A 有肯定间隔,但整个零碎从「逻辑」上来看,咱们是把这两个机房看做一个「整体」来布局的,也就是说,相当于把 2 个机房当作 1 个机房来用。
这种架构计划:比后面的同城灾备更「进了一步」,B 机房实时接入了流量,还能应答随时的故障切换,这种计划咱们把它叫做「同城双活」。
因为两个机房都能解决业务申请,这对咱们零碎的外部保护、革新、降级提供了更多的可施行空间(流量随时切换),当初,整个零碎的弹性也变大了,是不是更爽了?
那这种架构有什么问题呢?
10、两地三核心架构
还是回到危险上来说。
如上节所述,尽管咱们把 2 个机房当做一个整体来布局,但这 2 个机房在物理层面上,还是处于「一个城市」内,如果是整个城市产生自然灾害,例如地震、旱灾(河南旱灾刚过去不久),那 2 个机房仍旧存在「全局覆没」的危险。
真是防不胜防啊。怎么办?没方法,持续冗余。
但这次冗余机房,就不能部署在同一个城市了,你须要把它放到间隔更远的中央,部署在「异地」。
通常倡议两个机房的间隔要在 1000 公里以上,这样能力应答城市级别的劫难。
假如之前的 A、B 机房在北京,那这次新部署的 C 机房能够放在上海。
依照后面的思路,把 C 机房用起来,最简略粗犷的计划还就是做「冷备」,即定时把 A、B 机房的数据,在 C 机房做备份,避免数据失落。
这种计划,就是咱们常常听到的「两地三核心」。
具体就是:两地是指 2 个城市,三核心是指有 3 个机房。其中 2 个机房在同一个城市,并且同时提供服务,第 3 个机房部署在异地,只做数据灾备。
这种架构计划,通常用在银行、金融、政企相干的我的项目中。它的问题还是后面所说的,启用灾备机房须要工夫,而且启用后的服务,不确定是否如期工作。
所以:要想真正的抵挡城市级别的故障,越来越多的互联网公司,开始施行「异地双活」。
11、伪异地双活架构
这里,咱们还是剖析 2 个机房的架构状况。
咱们不再把 A、B 机房部署在同一个城市,而是离开部署(例如 A 机房放在北京,B 机房放在上海)。
后面咱们讲了同城双活,那异地双活是不是间接「照搬」同城双活的模式去部署就能够了呢?
事件没你想的那么简略。
如果还是依照同城双活的架构来部署,那异地双活的架构就是这样的:
留神看:两个机房的网络是通过「跨城专线」连通的。
此时两个机房都接入流量,那上海机房的申请,可能要去读写北京机房的存储,这里存在一个很大的问题:网络提早。
因为两个机房间隔较远,受到物理间隔的限度。当初,两地之间的网络提早就变成了「不可漠视」的因素了。
北京到上海的间隔大概 1300 公里,即便架设一条高速的「网络专线」,光纤以光速传输,一个来回也须要近 10ms 的提早。
况且:网络线路之间还会经验各种路由器、交换机等网络设备,理论提早可能会达到 30ms ~ 100ms,如果网络产生抖动,提早甚至会达到 1 秒。
不止是提早:远距离的网络专线品质,是远远达不到机房内网络品质的,专线网络常常会产生提早、丢包、甚至中断的状况。总之,不能适度信赖和依赖「跨城专线」。
你可能会问,这点提早对业务影响很大吗?影响十分大!
试想:一个客户端申请打到上海机房,上海机房要去读写北京机房的存储,一次跨机房拜访提早就达到了 30ms,这大抵是机房内网网络(0.5 ms)访问速度的 60 倍(30ms / 0.5ms),一次申请慢 60 倍,来回往返就要慢 100 倍以上。
而咱们在 App 关上一个页面,可能会拜访后端几十个 API,每次都跨机房拜访,整个页面的响应提早有可能就达到了秒级,这个性能几乎惨不忍睹,难以承受。
看到了么:尽管咱们只是简略的把机房部署在了「异地」,但「同城双活」的架构模型,在这里就不实用了,还是依照这种形式部署,这是「伪异地双活」!
那如何做到真正的异地双活呢?
12、真正的异地双活架构
既然「跨机房」调用提早是不容忽视的因素,那咱们只能尽量避免跨机房「调用」,躲避这个提早问题。
也就是说:上海机房的利用,不能再「跨机房」去读写北京机房的存储,只容许读写上海本地的存储,实现「就近拜访」,这样能力防止提早问题。
还是之前提到的问题:上海机房存储都是从库,不容许写入啊,除非咱们只容许上海机房接入「读流量」,不接管「写流量」,否则无奈满足不再跨机房的要求。
很显然:只让上海机房接管读流量的计划不事实,因为很少有我的项目是只有读流量,没有写流量的。所以这种计划还是不行,这怎么办?
此时,你就必须在「存储层」做革新了。
要想上海机房读写本机房的存储,那上海机房的存储不能再是北京机房的从库,而是也要变为「主库」。
你没看错:两个机房的存储必须都是「主库」,而且两个机房的数据还要「相互同步」数据,即客户端无论写哪一个机房,都能把这条数据同步到另一个机房。
因为只有两个机房都领有「全量数据」,能力反对任意切换机房,继续提供服务。
怎么实现这种「双主」架构呢?它们之间如何相互同步数据?
如果对 MySQL 有所理解,你应该晓得,MySQL 自身就提供了双主架构,它反对双向复制数据,但平时用的并不多。而且 Redis、MongoDB 等数据库并没有提供这个性能。所以,你必须开发对应的「数据同步中间件」来实现双向同步的性能。
此外:除了数据库这种有状态的软件之外,你的我的项目通常还会应用到音讯队列(例如 RabbitMQ、Kafka),这些也是有状态的服务,所以它们也须要开发双向同步的中间件,反对任意机房写入数据,同步至另一个机房。
看到了么:这一下子复杂度就上来了,单单针对每个数据库、队列开发同步中间件,就须要投入很大精力了。
业界也开源出了很多数据同步中间件,例如:阿里的 Canal、RedisShake、MongoShake,可别离在两个机房同步 MySQL、Redis、MongoDB 数据。
很多有能力的公司,也会采纳自研同步中间件的形式来做(例如饿了么、携程、美团都开发了本人的同步中间件)。
当初,整个架构就变成了这样:
留神看:两个机房的存储层都相互同步数据的。
有了数据同步中间件,就能够达到这样的成果:
1)北京机房写入 X = 1;
2)上海机房写入 Y = 2;
3)数据通过中间件双向同步;
4)北京、上海机房都有 X = 1、Y = 2 的数据。
这里:咱们用中间件双向同步数据,就不必再放心专线问题(一旦专线出问题,咱们的中间件能够主动重试,直到胜利,达到数据最终统一)。
但这里还会遇到一个问题:两个机房都能够写,操作的不是同一条数据那还好,如果批改的是同一条的数据,发生冲突怎么办?
1)用户短时间内发了 2 个批改申请,都是批改同一条数据;
2)一个申请落在北京机房,批改 X = 1(还未同步到上海机房);
3)另一个申请落在上海机房,批改 X = 2(还未同步到北京机房);
4)两个机房以哪个为准?
也就是说:在很短的工夫内,同一个用户批改同一条数据,两个机房无奈确认谁先谁后,数据产生「抵触」。
这是一个很重大的问题:零碎产生故障并不可怕,可怕的是数据产生「谬误」,因为修改数据的老本太高了。咱们肯定要防止这种状况的产生。
解决这个问题,有 2 个计划。
第一个计划:数据同步中间件要有主动「合并」数据、解决「抵触」的能力。
这个计划实现起来比较复杂,要想合并数据,就必须要辨别出「先后」程序。咱们很容易想到的计划,就是以「工夫」为标尺,以「后达到」的申请为准。
但这种计划须要两个机房的「时钟」严格保持一致才行,否则很容易呈现问题。
例如:
1)第 1 个申请落到北京机房,北京机房时钟是 10:01,批改 X = 1;
2)第 2 个申请落到上海机房,上海机房时钟是 10:00,批改 X = 2。
因为北京机房的工夫「更晚」,那最终后果就会是 X = 1。但这里其实应该以第 2 个申请为准,X = 2 才对。
可见,齐全「依赖」时钟的抵触解决方案,不太谨严。
所以,通常会采纳第二种计划,从「源头」就防止数据抵触的产生。
咱们持续往下学习。。。
13、更好的异地双活架构及施行思路
接上节:既然主动合并数据的计划实现老本高,那咱们就要想,是否从源头就「防止」数据抵触呢?
这个思路十分棒!
13.1 基本思路
从源头防止数据抵触的思路是:在最上层接入流量时,就不要让抵触的状况产生。
具体来讲就是:要在最上层就把用户「辨别」开,局部用户申请固定打到北京机房,其它用户申请固定打到上海 机房,进入某个机房的用户申请,之后的所有业务操作,都在这一个机房内实现,从本源上防止「跨机房」。
所以这时:你须要在接入层之上,再部署一个「路由层」(通常部署在云服务器上),本人能够配置路由规定,把用户「分流」到不同的机房内。
但这个路由规定,具体怎么定呢?
有很多种实现形式,最常见的我总结了 3 类:
1)按业务类型分片;
2)间接哈希分片;
3)按地理位置分片。
13.2 按业务类型分片
这种计划是指,按利用的「业务类型」来划分。
举例:假如咱们一共有 4 个利用,北京和上海机房都部署这些利用。但利用 1、2 只在北京机房接入流量,在上海机房只是热备。利用 3、4 只在上海机房接入流量,在北京机房是热备。
这样一来:利用 1、2 的所有业务申请,只读写北京机房存储,利用 3、4 的所有申请,只会读写上海机房存储。
这样按业务类型分片,也能够防止同一个用户批改同一条数据。
这里按业务类型在不同机房接入流量,还须要思考多个利用之间的依赖关系,要尽可能的把实现「相干」业务的利用部署在同一个机房,防止跨机房调用。
例如,订单、领取服务有依赖关系,会产生相互调用,那这 2 个服务在 A 机房接入流量。社区、发帖服务有依赖关系,那这 2 个服务在 B 机房接入流量。
13.3 间接哈希分片
这种计划就是:最上层的路由层,会依据用户 ID 计算「哈希」取模,而后从路由表中找到对应的机房,之后把申请转发到指定机房内。
举例:一共 200 个用户,依据用户 ID 计算哈希值,而后依据路由规定,把用户 1 – 100 路由到北京机房,101 – 200 用户路由到上海机房,这样,就防止了同一个用户批改同一条数据的状况产生。
13.4 按地理位置分片
这种计划,非常适合与地理位置密切相关的业务,例如打车、外卖服务就非常适合这种计划。
拿外卖服务举例:你要点外卖必定是「就近」点餐,整个业务范围相干的有商家、用户、骑手,它们都是在雷同的地理位置内的。
针对这种特色:就能够在最上层,按用户的「地理位置」来做分片,扩散到不同的机房。
举例:北京、河北地区的用户点餐,申请只会打到北京机房,而上海、浙江地区的用户,申请则只会打到上海机房。这样的分片规定,也能防止数据抵触。
揭示:这 3 种常见的分片规定,第一次看不太好了解,倡议配合图多了解几遍。搞懂这 3 个分片规定,你能力真正明确怎么做异地多活。
总之:分片的外围思路在于,让同一个用户的相干申请,只在一个机房内实现所有业务「闭环」,不再呈现「跨机房」拜访。
阿里在施行这种计划时,给它起了个名字,叫做「单元化」。
当然,最上层的路由层把用户分片后,实践来说同一个用户只会落在同一个机房内,但不排除程序 Bug 导致用户会在两个机房「漂移」。
平安起见,每个机房在写存储时,还须要有一套机制,可能检测「数据归属」,应用层操作存储时,须要通过中间件来做「兜底」,防止不该写本机房的状况产生。(篇幅限度,这里不开展讲,了解思路即可)
当初:两个机房就能够都接管「读写」流量(做好分片的申请),底层存储放弃「双向」同步,两个机房都领有全量数据。当任意机房故障时,另一个机房就能够「接管」全副流量,实现疾速切换,几乎不要太爽。
不仅如此:因为机房部署在异地,咱们还能够更细化地「优化」路由规定,让用户拜访就近的机房,这样整个零碎的性能也会大大晋升。
这里还有一种状况,是无奈做数据分片的:全局数据,例如系统配置、商品库存这类须要强统一的数据,这类服务仍旧只能采纳写主机房,读从机房的计划,不做双活。
双活的重点,是要优先保障「外围」业务先实现双活,并不是「全副」业务实现双活。
至此,咱们才算实现了真正的「异地双活」!
到这里你能够看出,实现这样一套架构,须要投入的老本是微小的:
路由规定、路由转发、数据同步中间件、数据校验兜底策略,不仅须要开发弱小的中间件,同时还要业务配合革新(业务边界划分、依赖拆分)等一些列工作,没有足够的人力物力,这套架构很难施行。
14、异地多活架构
了解了异地双活,那「异地多活」顾名思义,就是在异地双活的根底上,部署多个机房即可。
架构变成了这样:
这些服务依照「单元化」的部署形式,能够让每个机房部署在任意地区,随时扩大新机房,你只须要在最上层定义好分片规定就好了。
但这里还有一个小问题:随着扩大的机房越来越多,当一个机房写入数据后,须要同步的机房也越来越多,这个实现复杂度会比拟高。
所以业界又把这一架构又做了进一步优化,把「网状」架构降级为「星状」:
这种计划必须设立一个「核心机房」,任意机房写入数据后,都只同步到核心机房,再由核心机房同步至其它机房。
这样做的益处是:一个机房写入数据,只须要同步数据到核心机房即可,不须要再关怀一共部署了多少个机房,实现复杂度大大「简化」。
但与此同时:这个核心机房的「稳定性」要求会比拟高。不过也还好,即便核心机房产生故障,咱们也能够把任意一个机房,晋升为核心机房,持续依照之前的架构提供服务。
至此,咱们的零碎彻底实现了「异地多活」!
多活的劣势在于:能够任意扩大机房「就近」部署。任意机房产生故障,能够实现疾速「切换」,大大提高了零碎的可用性。
同时:咱们也再也不必放心零碎规模的增长,因为这套架构具备极强的「扩大能力」。
怎么样?咱们从一个最简略的利用,一路优化下来,到最终的架构计划,有没有帮你彻底了解异地多活呢?
15、本文小结
好了,总结一下这篇文章的重点。
1)一个好的软件架构,应该遵循高性能、高可用、易扩大 3 大准则,其中「高可用」在零碎规模变得越来越大时,变得尤为重要。
2)零碎产生故障并不可怕,能以「最快」的速度复原,才是高可用谋求的指标,异地多活是实现高可用的无效伎俩。
3)晋升高可用的外围是「冗余」,备份、主从正本、同城灾备、同城双活、两地三核心、异地双活,异地多活都是在做冗余。
4)同城灾备分为「冷备」和「热备」,冷备只备份数据,不提供服务,热备实时同步数据,并做好随时切换的筹备。
5)同城双活比灾备的劣势在于,两个机房都能够接入「读写」流量,进步可用性的同时,还晋升了零碎性能。尽管物理上是两个机房,但「逻辑」上还是当做一个机房来用。
6)两地三核心是在同城双活的根底上,额定部署一个异地机房做「灾备」,用来抵挡「城市」级别的灾祸,但启用灾备机房须要工夫。
7)异地双活才是抵挡「城市」级别灾祸的更好计划,两个机房同时提供服务,故障随时可切换,可用性高。但实现也最简单,了解了异地双活,能力彻底了解异地多活。
8)异地多活是在异地双活的根底上,任意扩大多个机房,不仅又进步了可用性,还能应答更大规模的流量的压力,扩展性最强,是实现高可用的最终计划。
16、写在最初
这篇文章我从「宏观」层面,向你介绍了异地多活架构的「外围」思路,整篇文章的信息量还是很大的,如果不太好了解,我倡议你多读几遍。
因为篇幅限度,很多细节我并没有开展来讲。这篇文章更像是讲异地多活的架构之「道」,而真正施行的「术」,要思考的点其实也十分繁多,因为它须要开发弱小的「基础设施」才能够实现施行。
不仅如此,要想真正实现异地多活,还须要遵循一些准则,例如业务梳理、业务分级、数据分类、数据最终一致性保障、机房切换一致性保障、异样解决等等。同时,相干的运维设施、监控体系也要能跟得上才行。
宏观上须要思考业务(微服务部署、依赖、拆分、SDK、Web 框架)、基础设施(服务发现、流量调度、继续集成、同步中间件、自研存储),宏观上要开发各种中间件,还要关注中间件的高性能、高可用、容错能力,其复杂度之高,只有亲自参加过之后才晓得。
我已经有幸参加过存储层同步中间件的设计与开发,实现过「跨机房」同步 MySQL、Redis、MongoDB 的中间件,踩过的坑也十分多。当然,这些中间件的设计思路也十分有意思,有工夫独自分享一下这些中间件的设计思路。
值得揭示你的是:只有真正了解了「异地双活」,能力彻底了解「异地多活」。
在我看来:从同城双活演变为异地双活的过程,是最为简单的。最外围的货色包含:业务单元化划分、存储层数据双向同步、最上层的分片逻辑,这些是实现异地多活的重中之重。
心愿我分享的架构教训,对你有所启发。
附录:更多 IM 技术干货
[1] IM 架构设计的文章:
《浅谈 IM 零碎的架构设计》
《简述挪动端 IM 开发的那些坑:架构设计、通信协议和客户端》
《一套海量在线用户的挪动端 IM 架构设计实际分享 (含具体图文)》
《一套原创分布式即时通讯(IM) 零碎实践架构计划》
《一套高可用、易伸缩、高并发的 IM 群聊、单聊架构方案设计实际》
《从游击队到正规军(一):马蜂窝旅游网的 IM 零碎架构演进之路》
《瓜子 IM 智能客服零碎的数据架构设计(整顿自现场演讲,有配套 PPT)》
《阿里钉钉技术分享:企业级 IM 王者——钉钉在后端架构上的过人之处》
《一套亿级用户的 IM 架构技术干货(上篇):整体架构、服务拆分等》
《一套亿级用户的 IM 架构技术干货(下篇):可靠性、有序性、弱网优化等》
《从老手到专家:如何设计一套亿级音讯量的分布式 IM 零碎》
《企业微信的 IM 架构设计揭秘:音讯模型、万人群、已读回执、音讯撤回等》
《融云技术分享:全面揭秘亿级 IM 音讯的牢靠投递机制》
《IM 开发技术学习:揭秘微信朋友圈这种信息推流背地的零碎设计》
《阿里 IM 技术分享(三):闲鱼亿级 IM 音讯零碎的架构演进之路》
《阿里 IM 技术分享(四):闲鱼亿级 IM 音讯零碎的牢靠投递优化实际》
《阿里 IM 技术分享(五):闲鱼亿级 IM 音讯零碎的及时性优化实际
[2] 其它 IM 技术综合性文章:
《新手入门一篇就够:从零开发挪动端 IM》
《挪动端 IM 开发者必读(一):通俗易懂,了解挪动网络的“弱”和“慢”》
《挪动端 IM 开发者必读(二):史上最全挪动弱网络优化办法总结》
《从客户端的角度来谈谈挪动端 IM 的音讯可靠性和送达机制》
《古代挪动端网络短连贯的优化伎俩总结:申请速度、弱网适应、平安保障》
《挪动端 IM 中大规模群音讯的推送如何保障效率、实时性?》
《挪动端 IM 开发须要面对的技术问题》
《IM 音讯送达保障机制实现(一):保障在线实时音讯的牢靠投递》
《IM 音讯送达保障机制实现(二):保障离线音讯的牢靠投递》
《如何保障 IM 实时音讯的“时序性”与“一致性”?》
《一个低成本确保 IM 音讯时序的办法探讨》
《IM 单聊和群聊中的在线状态同步应该用“推”还是“拉”?》
《IM 群聊音讯如此简单,如何保障不丢不重?》
《谈谈挪动端 IM 开发中登录申请的优化》
《挪动端 IM 登录时拉取数据如何作到省流量?》
《浅谈挪动端 IM 的多点登录和音讯漫游原理》
《齐全自已开发的 IM 该如何设计“失败重试”机制?》
《自已开发 IM 有那么难吗?手把手教你自撸一个 Andriod 版繁难 IM (有源码)》
《适宜老手:从零开发一个 IM 服务端(基于 Netty,有残缺源码)》
《拿起键盘就是干:跟我一起徒手开发一套分布式 IM 零碎》
《IM 音讯 ID 技术专题(一):微信的海量 IM 聊天音讯序列号生成实际(算法原理篇)》
《IM 开发宝典:史上最全,微信各种性能参数和逻辑规定材料汇总》
《IM 开发干货分享:我是如何解决大量离线音讯导致客户端卡顿的》
《零根底 IM 开发入门(一):什么是 IM 零碎?》
《零根底 IM 开发入门(二):什么是 IM 零碎的实时性?》
《零根底 IM 开发入门(三):什么是 IM 零碎的可靠性?》
《零根底 IM 开发入门(四):什么是 IM 零碎的音讯时序一致性?》
《IM 开发干货分享:如何优雅的实现大量离线音讯的牢靠投递》
《IM 扫码登录技术专题(三):通俗易懂,IM 扫码登录性能具体原理一篇就够》
《IM 扫码登录技术专题(四):你真的理解二维码吗?刨根问底、一文把握!》
《了解 IM 音讯“可靠性”和“一致性”问题,以及解决方案探讨》
《IM 开发干货分享:万字长文,详解 IM“音讯“列表卡顿优化实际》
《IM 开发干货分享:网易云信 IM 客户端的聊天音讯全文检索技术实际》
《IM 开发技术学习:揭秘微信朋友圈这种信息推流背地的零碎设计》
本文已同步公布于“即时通讯技术圈”公众号。
同步公布链接是:http://www.52im.net/thread-37…