在40岁老架构师尼恩的50+读者群中,一直在指导大家简历,指导大家职业升级。
前几天,指导了一个40岁老伙伴拿到年薪100W offer,小伙伴的优势在异地多活。
在简历指导的过程中,尼恩发现: 异地多活的概念、异地多活的架构、非常重要。
而且,异地多活的架构非常重要,3月份出了两个大的线上事故,B站刚崩,唯品会又崩了。
在这里,尼恩给自己的 Future Super Architect Community (未来 超级 架构师) 社区的小伙伴, 积累一些 异地多活的架构方案和素材。 这些资料的主要的目标: 方便在架构指导的时候,作为参考资料。
在尼恩的公众号技术自由社群中,之前有过两篇异地多活的方案介绍文章:
《B站刚崩,唯品会又崩:亿级用户网站的架构硬伤与解决方案》
《100Wqps异地多活,得物是怎么架构的?》
当然,好知识不能独享, 这份方案,顺便通过尼恩的自媒体渠道公布给大家,为大家做架构提供参考资料。
也一并把这个方案作为系统高可用架构参考答案,收入咱们的《尼恩Java面试宝典PDF》V143,供后面的小伙伴参考,提升大家的 3高 架构、设计、开发水平。
《尼恩 架构笔记》《尼恩高并发三部曲》《尼恩Java面试宝典》的PDF,请到文末公号【技术自由圈】取
2023-11月左右,P0级(宕机几个小时)故障频发:
语雀一个月前崩了,接着阿里云崩,阿里云崩完、滴滴崩…而且都是宕机几个小时,影响面广,例如由于滴滴平台不能提供服务,有同学说:
滴滴影响实在是太大了,整个飞机上人数少了有一半。
所以,像阿里云、滴滴这样的大平台,一旦出现大的质量事故,造成的经济损失非常巨大。
P0事件频发,的确是给各个互联网大厂敲响了质量警钟。
1. 滴滴平台事故
这次故障影响相当之大,持续非常之久。从昨晚10点一直影响到今天早上9点。
2023-11-28 22:27 有网友反馈,滴滴打车车子未到,司机电话说到了。
22:50 尝试访问,依然502,搜索失败,之后不断尝试… 依旧是502错误。
从全线产品线出问题来看,可能是数据库批量挂掉,恢复数据完成时间不可控。
当然,也有可能是系统升级导致基础架构崩溃,比如云平台崩溃,问题难定位。
2. 阿里云崩溃
回到11月12日双十一刚过,阿里就迎来了一次P0级事故,阿里云直接崩溃,影响范围极大,阿里系的钉钉,淘宝,闲鱼,语雀,高德地图等重磅应用全线崩溃,并且还影响到数以万计的客户,那些使用了阿里云OSS服务的公司,也难以幸免。
这次事故被定级为P0事故了,最严重的那种,而距离上一次阿里云发生P0事故,还不到一年的时间,在去年的12月,阿里云香港地区就发生了严重的故障,导致服务中断了超过12小时,而现在一年时间都不到,又发生了类似事件。
3. 语雀严重故障
10 月 23 日下午两点左右,语雀(在线文档编辑与协同工具)发生服务器故障,在线文档和官网目前均无法打开。
当日 15 时,语雀发布官方声明称,“目前因网络故障,出现无法访问的情况。此故障不会影响用户在语雀存储的数据,不会引起数据丢失,我们正在紧急恢复中,再次抱歉给你带来的损失。”
据语雀公告,这次事故是由于新的运维升级工具 bug导致的:导致华东地区生产环境存储服务器被误下线。受其影响,语雀数据服务发生严重故障,造成大面积的服务中断。为了尽快恢复服务,语雀和数据存储运维团队全力进行数据恢复工作,但受限于恢复方案、数据量级等因素,整体用时较长。从下午2:15开始联系硬件团队尝试将下线机器重新上线,但是因存储系统使用的机器类别较老,无法直接操作上线,重新调整恢复方案,中间经过新建存储系统、数据恢复、数据校验、团队联调等过程,最终在 22 点恢复语雀全部服务,历时近8个小时。
这次故障教训是深刻的,技术风险保障和高可用架构设计非常重要,确保数据备份、系统容错能力,如增加存储系统的异地灾备,实现快速恢复,并进行定期的容灾应急演练,缩小运维动作灰度范围。今后,我们也要加强运维工具的质量保障与测试,杜绝此类运维 bug 再次发生。
4. 更早:唯品会大事故
6 月 5 日,唯品会发布关于 329 机房宕机故障处理公告。官方在公告中称,南沙机房重大故障影响时间持续 12 个小时,导致公司业绩损失超亿元,影响客户达 800 多万。公司让对应部门的直接管理者承担此次事故责任,基础平台部负责人予以免职处理。
5. 大家都崩,美团不崩
这么多大厂产品都崩,大家发现没有,美团的产品,竟然不崩
没怎么听到没有产品崩溃的消息。
不出事就是好事, 说明人家平时的工作做好了,做到位了。
那么,美团是怎么做高可用架构的呢?
这里,来了一篇美团内部的架构师,写的他们的高可用架构的文章。
通过文章,看看他的高可用架构, 是多巧夺天工!
文章来源: 美团技术公众号
作者:晓静,美团技术专家
每个系统都有它最核心的指标。比如在收单领域:进件系统第一重要的是保证入件的准确性,第二重要的是保证上单效率。清结算系统第一重要的是保证准确打款,第二重要的是保证及时打款。我们负责的系统是美团点评智能支付的核心链路,承担着智能支付100%的流量,内部习惯称为核心交易。因为涉及美团点评所有线下交易商家、用户之间的资金流转,对于核心交易来说:第一重要的是稳定性,第二重要的还是稳定性。
稳定重要性
作为一个平台部门,我们的目标分为三个阶段:第一阶段是迅速支持业务发展;第二阶段是把握住一个明确的发展方向;第三阶段是在观察市场趋势的基础上,引领一个大方向。
虽然目标宏伟,但现实情况是,从2017年初的每天数十万订单,到年底时,日订单量已经激增至700万,系统面临着前所未有的挑战。支付渠道在不断增加,交易链路在延长,系统复杂性也在不断提升。从最初的POS机到后来的二维码支付产品,如小白盒、小黑盒、秒付等,产品的多样化,使得系统的定位也在不断调整。而系统对于变化的适应速度,就像是与兔子赛跑的乌龟。
由于业务的高速增长,即使系统没有进行任何更新升级,也可能会突然出现事故。事故发生的频率越来越高,而系统自身的升级也常常面临诸多困难。基础设施升级、上下游升级,往往会引发“蝴蝶效应”,系统可能会在毫无预兆的情况下受到影响。
核心交易的稳定性问题实质上是如何实现系统的高可用性。
可用性指标
业界高可用的标准是按照系统宕机时间来衡量的:
可用性标准
因为业界的标准是后验的指标,考虑到对于平时工作的指导意义,我们通常采用服务治理平台OCTO来统计可用性。计算方法是:
美团可用性计算
可用性分解
业界系统可靠性还有两个比较常用的关键指标:
平均无故障时间(Mean Time Between Failures,简称MTBF):即系统平均能够正常运行多长时间,才发生一次故障。
平均修复时间(Mean Time To Repair,简称MTTR):即系统由故障状态转为工作状态时修理时间的平均值。
对于核心交易来说,可用性最好是无故障。然而,在出现故障时,判断其影响的因素除了时间,还包括影响的范围。将核心交易的可用性问题分解则为:
可用性分解
用STAR法则举一个场景:
情境(situation)
我们要设计一个系统A,其功能是:通过我们美团点评的POS机,使用系统A连接银行进行支付,同时我们会提供一些优惠活动,如满减、积分等。
任务(task)
分析一下对于系统A的显性需求和隐性需求:
接收上游传递的参数,参数中包含商家信息、用户信息、设备信息、优惠信息。
生成单号,将交易的订单信息落库。
敏感信息要加密。
要调用下游银行的接口。
要支持退款。
要把订单信息同步给积分核销等部门。
要能给商家一个查看订单的界面。
要能给商家进行收款的结算。
基于以上需求,分析一下怎样才能让里面的最核心链路“使用POS机付款”稳定。
行动(action)
分析一下:需求1到4是支付必需的链路,可以将其集成在一个子系统内,称之为收款子系统。需求5到8各自独立,每个都可以作为一个子系统来开发,具体情况取决于开发人员数量、维护成本等因素。
需要注意的是,需求5到8与收款子系统之间没有功能上的依赖,只有数据上的依赖,即它们都需要依赖生成的订单数据。
收款子系统是整个系统的核心,对稳定性有极高的要求。其他子系统出现问题,不能影响到收款子系统。
因此,我们需要在收款子系统和其他子系统之间建立一个解耦,统一管理向其他系统提供的数据。这里称之为“订阅转发子系统”,只要确保这个系统不会影响收款子系统的稳定性即可。
粗略架构图如下:
架构图
结果(result)
从上述架构图可以看出,收款子系统和退款子系统、结算子系统、信息同步子系统、查看订单子系统之间没有直接依赖关系,达到了消除依赖的目的。收款子系统不需要依赖数据订阅转发子系统,而是数据订阅转发子系统需要依赖收款子系统的数据。我们通过控制依赖,使数据订阅转发子系统从收款子系统拉取数据,而不是收款子系统向数据订阅转发子系统推送数据。这样,即使数据订阅转发子系统出现问题,收款子系统也不会受到影响。
再说数据订阅转发子系统拉取数据的方式。例如,数据存储在MySQL数据库中,通过同步Binlog来获取数据。如果采用消息队列进行数据传输,就会对消息队列中间件产生依赖。如果我们设计一个灾备方案:当消息队列出现问题,直接通过RPC调用传输数据。对于这个消息队列,就实现了降低依赖的效果。
外部调用包括对其他系统的调用和基础组件的调用。外部调用的特点是返回时间不可预测,如果将其包含在事务中,会导致事务变得庞大。过大的数据库事务会占用所有数据库连接,导致与该数据库相关的所有服务陷入等待状态,进而使得连接池满负荷,多个服务崩溃。如果处理不好这个问题,危险程度极高,达到五颗星。下面的图显示出外部调用时间的不可控:
大事务问题
解决方法:
检查各个系统的代码,看看事务中是否包含RPC调用、HTTP调用、消息队列操作、缓存操作、循环查询等耗时操作。这些操作应该放在事务之外,理想情况下,事务内只处理数据库操作。
对大事务添加监控报警。大事务发生时,会收到邮件和短信提醒。针对数据库事务,一般分为1s以上、500ms以上、100ms以上三种级别的事务报警。
建议不要使用XML配置事务,而应采用注解方式。原因在于,XML配置事务的可读性不强,切面配置通常过于泛滥,容易导致事务过大,而且对于嵌套情况的规则处理不好。
大事务排除措施
对外部系统和基础组件如缓存、消息队列等的依赖关系。设想一下,如果这些被依赖的对象突然出现问题,我们系统的响应时间将由内部耗时、依赖方超时时间以及重试次数决定。如果超时时间过长或重试次数过多,系统可能会长时间无法返回,导致连接池被耗尽,甚至导致系统崩溃;反之,如果超时时间过短,会导致499错误增加,从而降低系统的可用性。
举个例子:
依赖例子
以服务A为例,它依赖于两个服务的数据来完成某项操作。在正常情况下,没有问题。但是,如果服务B在您不知情的情况下,响应时间变长甚至停止服务,而客户端的超时时间设置过长,那么完成此次请求的响应时间就会变长。在这种情况下,如果发生意外,后果将会非常严重。
Java的Servlet容器,无论是Tomcat还是Jetty都是多线程模型,都用Worker线程来处理请求。这个可配置有上限,当你的请求打满Worker线程的最大值之后,剩余请求会被放到等待队列。等待队列也有上限,一旦等待队列都满了,那这台Web Server就会拒绝服务,对应到Nginx上返回就是502。如果你的服务是QPS较高的服务,那么在这种情况下,你的服务也会受到影响。如果上游服务没有合理设置超时时间,故障会继续向上扩散。这种故障逐级放大的过程,就是所谓的服务雪崩效应。
解决方法:
首先要调研被依赖服务自己调用下游的超时时间是多少。调用方的超时时间要大于被依赖方调用下游的时间。
统计这个接口99%的响应时间是多少,设置的超时时间在这个基础上加50%。如果接口依赖第三方,而第三方的波动比较大,也可以按照95%的响应时间。
如果系统服务的重要性较高,可以按照默认的重试次数,通常是重试三次。否则,可以考虑不进行重试。
慢查询会降低应用的响应性能和并发性能。在业务量增加的情况下造成数据库所在的服务器CPU利用率急剧攀升,严重的会导致数据库不响应,只能重启解决。关于慢查询,可以参考技术博客之前的文章《MySQL索引原理及慢查询优化》。
慢查询
解决方法:
将查询分为实时查询、近实时查询和离线查询。实时查询可以直接穿透数据库,其他的查询则不走数据库,可以使用Elasticsearch来实现一个查询中心,处理近实时查询和离线查询。
读写分离。写走主库,读走从库。
索引优化。过多的索引会影响数据库的写入性能,而索引不足则会导致查询速度变慢。DBA建议一个数据表的索引数不超过4个。
不允许出现大表。MySQL数据库的一张数据表当数据量达到千万级,效率开始急剧下降。
在依赖的服务不可用时,服务调用方应通过一些技术手段,提供有损服务,以确保业务的柔性可用性。如果系统没有熔断机制,由于代码逻辑问题上线引发故障、网络问题、调用超时、业务促销导致调用量激增、服务容量不足等原因,服务调用链路上的一个下游服务出现故障,就可能导致接入层的其他业务无法使用。下图是对无熔断影响的鱼骨图分析:
无熔断
解决方法:
自动熔断:可以使用Netflix的Hystrix或者美团点评自己研发的Rhino来做快速失败。
手动熔断:确认下游支付通道抖动或不可用,可以手动关闭通道。
自己不作死要做到两点:第一自己不作,第二自己不死。
关于不作,我总结了以下7点:
不当小白鼠:只用成熟的技术,确保系统的稳定不受技术问题的影响。
职责单一化:防止职责的交织削弱或影响其完成主要职责的能力。
流程规范化:降低人为因素带来的影响。
过程自动化:让系统更高效、更安全的运营。
容量有冗余:为应对竞争对手系统崩溃导致用户转向我们、大型促销活动等突发情况,以及防灾考虑,系统容量至少要有2倍以上的冗余。
持续的重构:持续重构是保证代码长期稳定运行,避免一动就出问题的有效方法。
漏洞及时补:美团点评有安全漏洞运维机制,提醒督促各个部门修复安全漏洞。
安全漏洞
关于不死,地球上有五大不死神兽:能在恶劣环境下停止新陈代谢的“水熊虫”;可以返老还童的“灯塔水母”;在硬壳里休养生息的“蛤蜊”;水、陆、寄生样样都成的“涡虫”;有隐生能力的“轮虫”。它们的共通特征用在系统设计领域上就是自身容错能力强。这里“容错”的概念是:使系统具有容忍故障的能力,即在产生故障的情况下,仍有能力将指定的过程继续完成。容错即是Fault Tolerance,确切地说是容故障(Fault),而并非容错误(Error)。
容错
在开放的网络环境中,外部系统经常会遇到各种有意或无意的恶意攻击,例如DDoS攻击和用户重复尝试。尽管我们的团队成员都是精英,但我们仍然需要采取措施来保护系统,以免受到上游疏忽的影响。毕竟,没有人能够保证其他同事不会编写一个无限次重试的代码,如果下游返回不符合预期。这些内部和外部的巨量调用,如果不加以保护,往往会扩散到后台服务,最终可能引起后台基础服务宕机。下图是对无限流影响的问题树分析:
无限流
解决方法:
通过对服务端的业务性能进行压测,可以分析出一个相对合理的最大QPS值。
流量控制中用的比较多的三个算法是令牌桶、漏桶、计数器。可以使用Guava的RateLimiter来实现。其中SmoothBurstry是基于令牌桶算法的,SmoothWarmingUp是基于漏桶算法的。
核心交易这边采用美团服务治理平台OCTO做thrift截流。该平台支持接口粒度配额、单机/集群配额、指定消费者配额、测试模式工作以及及时的报警通知。在测试模式下,系统只会报警并不会真正进行限流。关闭测试模式后,超过限流阈值的请求将触发异常处理。限流策略可以随时关闭。
可以使用Netflix的Hystrix或者美团点评自己研发的Rhino来做特殊的针对性限流。
隔离是指将系统或资源分割开,这样在系统出现问题时,可以限制故障的扩散和影响范围。
服务器物理隔离原则
① 内外有别:内部系统与对外开放平台应有所区别对待。
② 内部隔离:从上游到下游按通道从物理服务器上进行隔离,并将低流量服务合并。
③ 外部隔离:按渠道隔离,渠道之间互不影响。
线程池资源隔离
注意:尽管线程池提供了线程隔离,客户端底层代码也必须要有超时设置,不能无限制地阻塞导致线程池一直饱和。
信号量资源隔离
总的来说,隔离技术是一种非常重要的技术,它可以帮助我们设计出更稳定、更健壮的系统。无论是在服务器的设计,还是在线程池和信号量的管理中,都需要运用到隔离技术。通过合理的隔离,我们可以有效地防止故障的扩散,提高系统的稳定性和可靠性。
发现分为事前发现、事中发现和事后发现。事前发现的主要手段是压测和故障演练;事中发现的主要手段是监控报警;事后发现的主要手段是数据分析。
你的系统是否适合全链路线上压测呢?一般来说,全链路压测适用于以下场景:
① 针对链路长、环节多、服务依赖错综复杂的系统,全链路线上压测可以更快更准确的定位问题。
② 有完备的监控报警,出现问题可以随时终止操作。
③ 有明显的业务峰值和低谷。低谷期就算出现问题对用户影响也比较小。
全链路线上压测的目的主要有:
① 了解整个系统的处理能力
② 排查性能瓶颈
③ 验证限流、降级、熔断、报警等机制是否符合预期并分析数据反过来调整这些阈值等信息
④ 发布的版本在业务高峰的时候是否符合预期
⑤ 验证系统的依赖是否符合预期
全链路压测的简单实现:
① 采集线上日志数据来做流量回放,为了和实际数据进行流量隔离,需要对部分字段进行偏移处理。
② 数据着色处理。可以用中间件来获取和传递流量标签。
③ 可以使用影子数据表来隔离流量,但需要注意磁盘空间,建议如果磁盘剩余空间不足70%时采用其他方式隔离流量。
④ 外部调用可能需要Mock。实现上可以采用一个Mock服务随机产生和线上外部调用返回时间分布的时延。
压测工具上,核心交易这边使用美团点评开发的pTest。
压测工具对比
故障的快速发现是保证系统稳定运行的重要环节。只有快速发现故障,才能快速定位问题,从而快速解决问题,减少故障对系统的影响。全链路线上压力测试是故障快速发现的重要手段之一。它通过模拟用户的操作行为,对系统进行压力测试,从而发现系统可能存在的问题。全链路线上压力测试不仅可以发现系统的问题,还可以帮助我们了解系统的性能,排查性能瓶颈,验证系统的各种机制是否正常工作。因此,全链路线上压力测试是系统维护的重要工具,对于保证系统的稳定运行起着至关重要的作用。
定位需要靠谱的数据。所谓靠谱就是和要发现的问题紧密相关的,无关的数据会造成视觉盲点,影响定位。所以对于日志,要制定一个简明日志规范。另外系统监控、业务监控、组件监控、实时分析诊断工具也是定位的有效抓手。
简明日志规范
要解决,提前是发现和定位。解决的速度取决于我们所采用的自动化程度,是完全自动化、半自动化还是手工操作。核心交易的目标是构建一个高可用的系统。我们的宗旨是:“避免重复造轮子,充分利用现有资源。”为此,我们打造了一个集成平台,其主要任务是:“专注于核心交易的高可用性,实现更好、更快、更高效的解决方案。”
在美团点评内部,有许多用于发现、定位和处理问题的系统和平台。然而,如果每次都需要打开不同的链接或登录不同的系统,势必会影响到解决问题的速度。因此,我们需要将这些系统和平台集成起来,实现问题的一站式解决。希望达到的效果举例如下:
解决问题
Hystrix通过实现断路器模式来监控故障。当断路器检测到调用接口出现长时间等待时,它会采用快速失败策略,向上返回一个错误响应,从而达到防止阻塞的目的。在这里,我们将重点介绍Hystrix的线程池资源隔离和信号量资源隔离。
线程池资源隔离
线程池资源隔离
优点
使用线程可以完全隔离第三方代码,请求线程可以快速放回。
当一个失败的依赖再次变成可用时,线程池将清理,并立即恢复可用,而不是一个长时间的恢复。
可以完全模拟异步调用,方便异步编程。
缺点
线程池的主要缺点是它增加了CPU,因为每个命令的执行涉及到排队(默认使用SynchronousQueue避免排队),调度和上下文切换。
对使用ThreadLocal等依赖线程状态的代码增加复杂性,需要手动传递和清理线程状态(Netflix公司内部认为线程隔离开销足够小,不会造成重大的成本或性能的影响)。
信号量资源隔离
开发者可以使用Hystrix限制系统对某一个依赖的最高并发数。这个基本上就是一个限流策略,每次调用依赖时都会检查一下是否到达信号量的限制值,如达到,则拒绝。
信号量资源隔离
优点
缺点
比较一下线程池资源隔离和信号量资源隔离
线程隔离是和主线程无关的其他线程来运行的;而信号量隔离是和主线程在同一个线程上做的操作。
信号量隔离也可以用于限制并发访问,防止阻塞扩散,与线程隔离的最大不同在于执行依赖代码的线程依然是请求线程。
线程池隔离适用于第三方应用或者接口、并发量大的隔离;信号量隔离适用于内部应用或者中间件;并发需求不是很大的场景。
隔离对比
Hystrix的线程池资源隔离和信号量资源隔离都是用于限制对共享资源的并发访问,防止故障扩散。线程池资源隔离适用于外部应用或接口、并发量大的场景,而信号量资源隔离适用于内部应用或中间件,并发需求不是很大的场景。在实际应用中,我们可以根据具体需求选择合适的资源隔离策略。
Rhino是美团点评基础架构团队研发并维护的一个稳定性保障组件,提供故障模拟、降级演练、服务熔断、服务限流等功能。和Hystrix对比:
内部通过CAT(美团点评开源的监控系统,参见之前的博客“深度剖析开源分布式监控CAT”)进行了一系列埋点,方便进行服务异常报警。
接入配置中心,能提供动态参数修改,比如强制熔断、修改失败率等。
架构和高级开发不一样: 架构的问题是open的、开发式的、没有标准答案的。
架构之路,注定是充满了坎坷。
在做架构过程中,或者在转型过程中,如果遇到复杂的场景,确实不知道怎么做架构方案,确实找不到有底的方案,怎么办? 可以以来找40岁老架构尼恩求助.
就在前几天,一个小伙伴遇到了一个 电商网站的黄金链路架构, 开始找不到思路,但是经过尼恩 10分钟语音指导,一下就豁然开朗。
so,大家如果遇到架构问题,甚至架构难题,可以找尼恩来交流,来求助。
《吃透8图1模板,人人可以做架构》
《10Wqps评论中台,如何架构?B站是这么做的!!!》
《阿里二面:千万级、亿级数据,如何性能优化? 教科书级 答案来了》
《峰值21WQps、亿级DAU,小游戏《羊了个羊》是怎么架构的?》
《100亿级订单怎么调度,来一个大厂的极品方案》
《2个大厂 100亿级 超大流量 红包 架构方案》
… 更多架构文章,正在添加中
《响应式圣经:10W字,实现Spring响应式编程自由》
这是老版本 《Flux、Mono、Reactor 实战(史上最全)》
《Spring cloud Alibaba 学习圣经》
《分库分表 Sharding-JDBC 底层原理、核心实战(史上最全)》
《一文搞定:SpringBoot、SLF4j、Log4j、Logback、Netty之间混乱关系(史上最全)》
《Linux命令大全:2W多字,一次实现Linux自由》
《TCP协议详解 (史上最全)》
《网络三张表:ARP表, MAC表, 路由表,实现你的网络自由!!》
《Redis分布式锁(图解 - 秒懂 - 史上最全)》
《Zookeeper 分布式锁 - 图解 - 秒懂》
《队列之王: Disruptor 原理、架构、源码 一文穿透》
《缓存之王:Caffeine 源码、架构、原理(史上最全,10W字 超级长文)》
《缓存之王:Caffeine 的使用(史上最全)》
《Java Agent 探针、字节码增强 ByteBuddy(史上最全)》
4000页《尼恩Java面试宝典 》 40个专题
以上尼恩 架构笔记、面试题 的PDF文件更新,▼请到下面【技术自由圈】公号取 ▼