转载

分布式系统主从高可用实践

高可用HA(High Availability) 是分布式系统架构设计中必须考虑的因素之一。本篇文章主要介绍主从模型服务的高可用演进,包括以下几个方面:

  1. 什么是高可用
  2. 主从模型介绍
  3. 主从架构高可用演进过程

什么是高可用

高可用(Hign Availability)是分布式系统架构设计中必须要考虑的因素之一。系统可用性的评估计算公式如下:

  • MTTF: Mean Time To Failure:平均无故障时间
  • MTTR: Mean Time To Repair:平均恢复时间
  • Availability: MTTF / (MTTF + MTTR) * 100%

通常的描述如下表所示:

分布式系统主从高可用实践

主从模型

主从(Master-Slave)模型,核心思想其实就是分而治之。在主从模型中,一般都是读写分离的,读写操作互相不影响且能避免机器出现性能瓶颈。这时只有Master对外提供写能力,Slave只提供读能力,Master和Slave之间进行通过网络进行数据同步。当Master节点宕机或者不可用时,从Slave中选择一台机器晋升为Master继续对外提供服务,从而实现服务的高可用。

而在微服务架构中,一般我们的应用服务都是无状态的,应用服务之间无需数据同步、也无需选主,可通过简单的水平扩展来实现提高服务的可用性。

对于有状态的系统,如果要实现高可用,那么不可避免的就需要采用主从模型,如MySQL、Redis、Hadoop等相关存储类型产品,又比如ZooKeeper和一些MQ产品。

今天我们探讨的就是分布式系统中主从模型高可用的实践。

主从架构高可用演进

任何一个高可用的架构都不是一蹴而就的,系统可用性的提升都是一个持续发现问题、解决问题、循序渐进提升的过程。当业务量较小的时候,一个单体架构的系统就能够满足需求,但是随着业务的增长相应架构会逐渐暴露出可用性等问题,然后去优化解决,最终整个系统架构在能够支撑业务的情况下达到极高的可用性。

架构的演进本质上都是为了解决问题,一般来说主从高可用架构都会经历如下几个阶段:

场景零:初始状态

任何大型系统的发展都伴随着系统架构的演进,系统初始阶段一般都是单应用设计,所以场景零如下图所示:

分布式系统主从高可用实践

此时,服务处于最原始的形态,所有的功能都在一个应用中实现,并且只有一台机器对外提供读写服务。处于 All In One 的阶段。

这个时候所面临的问题就是单点问题,即一旦该机器宕机或者网络隔离无法访问,就会面临着服务不可用的情况。

场景一:最简单的主从模型

为了解决场景一中的单点问题,对系统架构进行了调整和升级,实现了最简单的主从模型。如下图所示:

分布式系统主从高可用实践

图中展示的主从架构模型中,有一台Master机器和两台Slave机器。Master机器对外提供写能力,Slave机器对外提供读能力,Master和Slave通过网络进行数据同步。

在当前主从架构中首先对服务进行了读写分离改造,并且数据有多个冗余副本,可用性有一定提高。但是当出现机器不可用或者出现网络隔离时需要手动切换节点,所以此时所面临的问题是 如何实现自动Failover ,即当Master不可用时,服务如何自动恢复,并继续对外提供服务;当Slave不可用时,如何自动踢出。

场景二:引入控制中心

为了实现服务的自动Failover,我们引入了控制中心,如下图所示Admin节点:

分布式系统主从高可用实践

引入控制中心后,Master和Slave启动时都会在控制中心中进行注册,然后控制中心通过心跳和Master与Slave进行通信,以此来判断节点是否存活。同时控制中心还控制着Master节点的选取,当Master节点宕机后,从Slave节点中进行选主,选择一个Slave节点晋升为Master。最简单的选主方式就是通过注册顺序进行选主,首先服务启动时选择第一个注册的节点晋升为Master,当Master挂掉后节点删除,根据注册顺序依次选择Slave节点晋升成为Master。

此时如果Slave节点故障后,会自动将Slave节点踢出。如果Master节点挂掉后会进行重新选主,在两个Slave节点中选择一个新的Master节点,此时是一主一从,继续对外提供服务;

此时架构所面临的问题就是 脑裂 。脑裂是指在一个高可用系统中,相互连接的节点因为网络隔离断开连接,本来为一个整体的系统,分裂成了两个独立集群。这时两个集群开始争抢共享资源,导致数据不一致、系统混乱等问题。

上面的解释相对比较抽象,我们来分析下控制中心判决Master节点故障的情况,由于控制中心和Master节点是通过心跳来判活,所以当两者之间出现网络隔离时控制中心也会认为Master节点挂掉,所以其实Master节点故障的原因可能是:

  1. 可能Master节点真的网络故障或者死掉。
  2. 可能Master节点只是网络隔离,但是自己确不知道自己被网络隔离开了。

所以当Master节点只是由于网络隔离导致被控制中心认为不可用时,那么就会存在以下的情况:

  1. Master服务一直可用,但是出现网络隔离的情况,导致控制中心与Master之间的通信一直失败,控制中心认为Master挂掉。
  2. 控制中心认为Master挂掉后进行重新选主,我们假设此时Slave1晋升成了Master1节点。
  3. 重新选主后网络恢复,此时原有Master就会重新加入集群中,导致出现两个Master节点存在。

一旦出现脑裂,那么就势必就造成数据不一致、丢失或者严重引起整个集群不可用等问题。

场景三:引入租约

脑裂问题可以从软件层面和硬件层面两个维度去解决,主要有如下几种思路:

  • 仲裁:当节点出现分歧时,由第三方仲裁者决定谁是Master。
  • 隔离:当不能确定节点状态时,直接将节点隔离,确保不影响集群运行。
  • 冗余:节点间多增加“心跳线”,尽量减少网络隔离带来的影响。

硬件层面的解决方案就是硬件的冗余,比如多个节点之间多布几条网线或者专线,达到网线级别的冗余。这种方案缺点在于成本太高。

软件层面这里我们通过引入租约机制来解决脑裂问题,如下图所示:

分布式系统主从高可用实践

租约的定义是:租约是由颁发者授予的在某一有效期内的承诺。租约有以下几个特点:

  1. 颁发者一旦发出租约,无论接收方是否收到,也无论后续接收方处于何种状态,只要租约不过期,颁发者一定遵守承诺,按承诺的时间、内容执行。
  2. 接收方在租约有效期内可以使用颁发者的承诺,但是一旦租约过期,接收方必须放弃授权,需要重新申请租约。
  3. 租约机制有很高的容错能力。

在我们引入租约机制后,大致的选主流程为:Master节点申请租约,在租约时间范围内承认其Master的角色,当租约过期后需要续约,维持自己Master的角色;如果租约过期后没有续约,那么就退出Master角色,重新选主。更加详细的租约机制读者可自行查阅。

图中Master和Slave节点上方都有一条红线代表租约的有效期时间,当租约过期后Master节点会继续申请租约。如果这个时候Master节点因为是网络隔离导致不可用了会出现什么情况呢?可以预见的是,如果Master节点被网络隔离,租约过期后肯定不能续约,那么就会退出Master角色,重新选主;重新选主后即使网络恢复原有Master节点也蜕化为了Slave节点,不会导致脑裂问题。

再来看一个极端的例子,如果是Master节点刚申请完租约成功后就因为网络原因被隔离或者挂掉,这种情况怎么处理?由于此时是在租约过期后才会触发重新选主,所以在这段租约时间内,写服务不可用了(读服务依然可用,因为Slave节点没挂),只能等待租约过期重新选主;这就是租约会导致的问题:服务会有一个最大不可用时间,取决于租约时间。工程中,常选择的租约时长是10s级别。

这里其实笔者看了下公司内部以及开源的一些中间件系统(一些,特定场景),发现有些系统没有刻意去解决脑裂问题,交流了下发现设计原因如下:

  1. 脑裂出现的概率极低
  2. 最大程度的满足可用性,如缓存中间件服务,脑裂后会对其中一个Master降级,同时清除掉该Master中的数据来解决一致性问题。

脑裂出现的概率极低,但是对于不能丢数据的服务一旦出现就是致命的。现在各个大厂都会在机房之间布多条专线,硬件级别冗余来保证可靠性。

现在系统架构存在的问题和场景一的问题一致: admin控制中心的单点问题

场景四:引入中心控制备机

通过租约或者硬件冗余解决了脑裂问题后,应用服务就能够达到较高的可用性,但是现在admin控制节点又面临着场景零时会遇到的问题:admin中心控制节点的单点问题。

分布式系统主从高可用实践

如图所示,依然是场景零的解决方案,通过引入admin中心控制备机来解决控制中心的单点问题。

当然,现在也会面临着场景零所面对的相同问题——主备问题:如何实现控制中心的自动failover。

另外在当前架构下,除了中心控制备机自身自动failover的问题,还存在少数的admin判决master太过于暴力。

虽然我们引入了中心控制备机,一定程度解决了admin的可用性问题。但实际上对服务存活情况的判决还是由主admin这单一节点来决定的。这时如果是admin节点自己出现负载高,以至于处理Master的心跳被延迟了,可能会导致admin误判Master没有发送心跳,重新选主。

另外就是如今的服务都会要求多机房部署,如果对于服务的存活判断是由一个admin决定,那么出现admin节点与服务机器跨机房部署时,对于其他机房甚至异地机房服务的判活极容易出现误判,网络的抖动或者服务不稳定原因就可能会引起频繁的选主。

场景五:引入控制中心多点仲裁机制

场景四中引发了两个问题,我们先来看第一个:控制中心的主备问题。在场景二中我们通过引入控制中心节点解决了应用服务的主备问题,但是为了解决控制中心的主备问题,我们不能再引用一个更高层次的控制中心,否则架构演进上就会出现死循环。

所以控制中心只能通过自己来解决主备问题,通常的做法是依赖外部的选主算法,如paxos或者raft等协议。即控制中心自己实现paxos或者raft等选主算法,来解决自身的主备问题。

其实这里大家也都清楚了,我们一直讨论的admin的角色,就是日常使用的Zookeeper所扮演的角色。在这里可以直接将admin集群用zk集群替代。

解决了自身的选主和自动failover后,对于第二个问题:少数admin判决master太过暴力,这时引入控制中心多点仲裁机制,如下图所示:

分布式系统主从高可用实践

多点仲裁机制通俗点讲就是少数服从多数的机制。就是由多个admin共同判决服务是否存活,少数服从多数。如上图,每一个admin节点都会分别和应用节点建立连接,对其中任何一个节点是否存活的判断,都需要大多数admin节点“达成一致”。这时候如果master节点和其中一台admin节点因为网络隔离断开,因为另外两台admin节点还保持连接,所以还是会认为master节点存活。

如果是每个admin都和所有的应用节点建立连接,当节点数量过多时每个admin都会维护海量连接,存在非常大的性能开销。所以这里Zookeeper采用的是共享Session的机制,一个应用节点只会和一个admin节点建立连接,服务和admin建立连接初始化Session后,admin集群就会共享这个Session,即使与服务连接的admin节点故障,如果应用服务短时间内携带该Session的信息请求其他admin节点,还是会认为是同一个连接。

现在的架构就能够满足绝大多数的场景了,但是当服务数量进一步增多时,还会面临如下两个问题:

  1. 网络风暴:当服务数量较多时,admin集群的一次广播会发送大量的网络请求,就可能会导致网络风暴。网络风暴的概念请读者自行查阅,这里举一个会产生网络风暴的例子,例如使用admin控制中心来实现分布式锁,所有服务都竞争去创建子节点,节点序号最小的服务获得锁,其他节点等待。如果这时所有节点都监听最小节点的删除事件,那么当最小节点释放锁时,就需要广播消息给所有等待的节点,就会导致瞬间的广播风暴。比较好的实践是每个等待的节点只监听比他序列号小的那个节点的删除事件。
  2. admin控制中心瓶颈:为了保证admin集群的高吞吐和低延时,admin的数据需保存在内存中,也就限制了存储的数据量不会太大。当管理的服务过多时,大量的连接和元数据也会成为admin自身瓶颈。

场景六:引入控制中心数据分布方案

场景五架构中存在着网络风暴和admin控制中心瓶颈两个问题,网络风暴可以在实践中通过好的实现方案来规避,在实现分布式锁是案例中也有提到。

对于admin控制中心瓶颈的问题,我们通过引入控制中心数据分布方案来解决,如下图所示:

分布式系统主从高可用实践

此时的架构中,实际上就是进行数据分片,由多个集群共同管理元数据。如上图将单一的admin集群进行了拆分,形成了两个admin集群,同时通过super admin来进行协调和数据的同步。通过集群拆分和数据分片达到数据分布的效果,减少admin自身的压力。

当前架构中我们使用了super admin节点来进行协调和数据的同步。还有一种数据分片方案,是redis集群采用的,使用gossip协议进行节点间的数据同步,有兴趣的大家可自行查阅。

这种架构下,就能够有效的支撑业务量较大的场景。但是也存在着如下问题:

  1. 复杂性很高:实现方案比较复杂,且对公司的运维水平有较大考验。
  2. 需要考虑更高层级的隔离:如果admin cluster1或者super admin节点因网络隔离、挂掉等比较极端的情况,如何实现故障的隔离,不会导致整个服务的不可用。

场景七:按照功能或者组织拆分,物理隔离

如果admin集群是提供了全地域或者全组织的协调服务,那么当其出现故障后,就会导致全国所有地域或者整个公司服务都不可用的情况。

当公司内部服务到达一定规模后,就应该按照功能或者组织进行拆分,并且跨地域部署单独的集群,集群物理隔离。不仅有助于提高整个系统的稳定性,即使故障也能够将故障范围控制在单一地域和集群。此时架构如下所示:

分布式系统主从高可用实践

此时的架构能够很好的支撑各自业务场景,但是对于公司的运维能力有着极大的考验。 在这个架构下,各大公司都会有一套完善的运维监控体系,包括并不限于对机器状态指标的监控,对节点状态同步的监控,对数据快照大小的监控,对观察者数量的监控。

原文  https://juejin.im/post/5dca9f3bf265da4d3761e7b5
正文到此结束
Loading...