Server failure detection
Am I alive?
在众多的服务器当中如何判定一台机器是否是存活的状态呢?
大部分监控系统会在同一个机房或者相同网段部署一台监控节点(尽量排除网络问题),然后用这个监控节点服务器去周期性ping要监控的服务器,如果ping不通就认为这台服务器挂了。
如果这个监控节点挂了,可能就会有问题,那这个服务就要部署多台。另外这几台机器同时也要从监控系统中获取要监控的所有服务器列表。随着服务器数的不断增多,服务监控节点的任务会越来越多,负载越来越大。这时候可能就要做sharding了。
pingmesh
Can we get network latency between any two servers at any time in large-scale data center networks? 如何在一个大规模数据中心中做到在任意时间监控任意两台服务器的数据延迟呢?这是微软pingmesh系统产生的背景。pingmesh支持任意两台服务器之间的互ping,
具体可以参考这篇论文:https://conferences.sigcomm.org/sigcomm/2015/pdf/papers/p139.pdf
SWIM
Gossip
Gossip是一种去中心化、容错并保证最终一致性的协议。
Background:分布式环境 Gossip是为了解决分布式遇到的问题而设计的。由于服务和数据分布在不同的机器上,节点之间的每次交互都伴随着网络延迟、网络故障等的性能问题。可见,分布式系统会比单机系统遇到更多的难题。
如CAP理论所描述的,CAP三个因素在分布式的条件下只能满足两个。对于分布式系统来说,分区容忍性是其的基本要求。因为分布式系统的设计初衷就是利用集群多集的能力去处理单机无法解决的问题。分区容忍性(可扩展性)通过通过scale up和scale out实现的,也就是通过升级硬件或者增加机器来提升分布式系统的性能。这么说,可扩展性和可用性是相关联的。可扩展性好的系统,其可用性一般会比较高。所以分布式系统的所有问题基本都是在一致性和可用性之间进行协调和平衡。在工程实践中的经验如下:
一般来说,交易系统类的业务对一致性的要求比较高,一般会采用ACID模型来保证数据的强一致性,所以其可用性和扩展性就比较差。而其他大多数业务系统一般不需要保证强一致性,只要最终一致就可以了,它们一般采用BASE模型,用最终一致性的思想来设计分布式系统,从而使得系统可以达到很高的可用性和扩展性。基于 gossip 协议,我们可以做服务器的故障检测。
SWIM 全称:Scalable Weakly-consistent Infection-style Process Group Membership Protocol
SWIM:最终一致性 前面提到Gossip解决的问题就是在分布式环境下信息高效分发的问题,这个问题的解决决定着系统的一致性程度。而Gossip协议是基于一种叫做SWIM的协议( S calable W eakly-consistent I nfection-style Process Group M embership Protocol)。SWIM是一种无中心的分布式协议,各个节点之间通过p2p实现信息交流同步各节点状态的方法。看名字也知道这是一种弱一致性的实现。SWIM协议给每个进程组成员在本地维护一个成员表,记录该组存活的进程。该协议通过失效检测器(Failure Detector)和传播组件(Dissemination Component)来完成工作。
SWIM的失效检测器会检测失效的节点并将失效节点的更新信息发送给传播组件。SWIM的传播组件通过多播(multicast)的形式将失效信息传播给组内的其他成员。
协议的可扩展性体现在:新成员的加入和退出也以同样的方式进行多播通信。而在基本的时间周期内进行失效检测能够保证在限定的时间范围内完成完备性检查,即每个失效的进程都能最终被检测到(最终一致性)。通过多播方式传输协议消的问题在于效率不好也不可靠,通过在ping和ack消息中捎带成员更新信息能够降低丢包率和减少传输时延。这种传播方式被称为可传导的方式(Infection-style)。
memberlist
hashicorp 开源了一个基于gossip协议的lib,个人感觉非常好用。https://github.com/hashicorp/memberlist,我们给监控系统 agent 加入了 member
模块,用于做全网服务器的故障检测,每台服务器每个周期只会随机选取3台服务器进行UDP(TCP)的探活,如果有一台不通,会请求其他机器帮忙去试探这台机器是否真的死亡了,同时交换 alive 的 member 列表,最终每台服务器有一个全量的存活列表。
这样设计的优点:
- 完全无中心
- 不用知道全部的服务器列表
- 每台机器不用ping其他所有的机器,负载更小
- 只要有一台机器能到,就会认为是存活的,探活更加灵活
在开发中遇到的一个有意思的事情,下面是官方的demo代码:
/* Create the initial memberlist from a safe configuration.
Please reference the godoc for other default config types.
http://godoc.org/github.com/hashicorp/memberlist#Config
*/
list, err := memberlist.Create(memberlist.DefaultLocalConfig())
if err != nil {
panic("Failed to create memberlist: " + err.Error())
}
// Join an existing cluster by specifying at least one known member.
n, err := list.Join([]string{"nodes.test.com"})
if err != nil {
panic("Failed to join cluster: " + err.Error())
}
// Ask for members of the cluster
for _, member := range list.Members() {
fmt.Printf("Member: %s %s\n", member.Name, member.Addr)
}
// Continue doing whatever you need, memberlist will maintain membership
// information in the background. Delegates can be used for receiving
// events when members join or leave.
传递的 nodes.test.com
如果配置了3条A记录,memberlist
会自动帮你join这3条记录,而不是随机解析出来的1条记录。
另外,一定要添加一个key,防止恶意节点加入集群,当然这个功能memberlist
也已经实现了。
References: