电话之于短信、微信的一个很大的不同点在于,前者更加及时,有更快速直接的反馈;而后面两个虽然称之为instant message,但经常时发出去了就得等对方回复,等多久是不确定的。打电话能明确知道对方在不在,我所表达的信息是否已经传达;而短信或者微信,只知道消息发出去了,但对方是否收到,或者是否查看就不清楚了。

  在通过网络通信的环境下,也是很难知道一个消息对方是否已经处理,因为要知道对方是否处理,依赖于对方的回复(ack),但即使对方没有回复,也不能说明对方就没有处理,也许仅仅是对方回复的那条消息丢失了

  很多时候,一个进程需要判断另外一个进程是否还在工作,如何判断呢?判断是否准确呢,能否保证一致性呢?本文尝试回答这些问题。

  本文中,节点通常就是指一个进程,一个提供服务的进程。后文中,只要不加以强调,进程和几点是同一个意思。

  本文地址:http://www.cnblogs.com/xybaby/p/8710421.html

进程的状态

  一个进程是否crash(在本文中,只要进程是非预期的终止,就成为crash),在单机环境下是很好判断的,只要查看还有没有这个进程就行了。这里有两个问题:

  第一:分布式环境下能否准确判断进程crash?

  第二:是否需要明确一个进程是否crash?

  对于第一个问题,答案是几乎不能的,后面详细分析。

  而第二个问题,有的时候是无需明确一个进程是否已经crash,而需要明确的是该进程是否持续对外提供服务,即使没有crash,如果不按程序的预期工作了(比如进程死循环、死锁、断网),那么也可以说这个服务挂了,“有的人活着,他已经死了”。分布式环境中,我们关心的是,节点(进程)是否对外提供服务,真死(crash)假死(活着但不工作)都是死(从系统的角度看)。

  我们称一个对外提供服务的进程处于active状态,否则处于none-active状态。

Failure detection

  如何判断一个进程是否处于active状态,最简单明了的方式就是:每隔一段时间就和这个进程通通信,如果目标进程在一定的时间阈值内回复,那么我们就说这个进程是active的,否则就是none-active的。这种定时通信的策略我们统称为心跳。

  心跳有两种方式:第一种是单向的heartbeat;第二种是ping pong(ping ack)

  在后文中,被检测的进程称之为target,而负责检测的进程称之为detector

  第一种方式,target进程需要告知detector进程自己的存活性,只需要定时给detector发消息就行了,“hi, duddy, I am Ok!”。detector无需给target回复任何消息,detector的任务是,每隔一定时间去检查一下target是否有来汇报,没有的话,detector就认为target处于none-active状态了

  

  而第二种方式,ping pong或者ping ack,更为常见:

  detector给target发消息:hi,man,are you ok?

  target回复detector:Yes, I am ok!

  然后detector、target定时重复上面两步

  

  detector负责发起检测:如果target连续N次不回复消息,那么detector就可以认为target处于none-active状态了。

  这两种方式,区别在于谁来主动发消息,而相同点在于:谁关心active状态,谁来检测。就是说,不管是简单的heartbeat,还是ping ack,都是detector来检测target的状态。在实际中,ping ack的方式会应用得多一些,因为在ack消息中也可以携带一些信息,比如后文会提到的lease。

gunicorn failure detection

  gunicorn是一个遵循python wsgi的http server,使用了prefork master-worker模型(在gunicorn中,master被称为Arbiter),能够与各种wsgi web框架协作。在本文,关注的是Arbiter进程对Worker进程状态的检测。

  既然Worker进程是Arbiter fork出来的,即Arbiter是Worker进程的父进程,那么当Worker进程退出的时候,Master是可以通过监听signal.SIGCHLD信号来知道子进程的结束。但这还不够,gunicorn还有另外一招,我在《gunicorn syncworker 源码解析》中也有所提及:

  (1)gunicorn为每一个Worker进程创建一个临时文件

  (2)worker进程在每次轮训的时候修改该临时文件的属性

  (3)Arbiter进程检查临时文件最新一次修改时间是否超过阈值,如果超过,那么就给Worker发信号,kill掉该Worker

  不难看出,这是上面提到的第一种检测方式(单向的heartbeat),worker进程(target)通过修改文件属性的方式表明自己处于active状态,Arbiter(detector)进程检测文件属性来判断worker进程最近是否是active状态。

  这个检测的目的就是防止worker进程处于假死状态,没有crash,但是不工作。

tcp keepalive

  在计算机网络中,心跳也使用非常广泛。比如为了检测目标IP是否可达的ping命令、TCP的keepalive。

A keepalive (KA) is a message sent by one device to another to check that the link between the two is operating, or to prevent the link from being broken.  

  在TCP协议中,是自带keepalive来保活的,有三个很重要的选项(option)

tcp_keepidle :对一个连接进行有效性探测之前运行的最大非活跃时间间隔
tcp_keepintvl :两个探测的时间间隔
tcp_keepcnt :关闭一个非活跃连接之前进行探测的最大次数

  三个选项是这么配合的:如果一条连接上tcp_keepidle 这么长时间没有发消息之后,则开始心跳检测,心跳检测的时间间隔是tcp_keepintvl ,如果连续探测tcp_keepcnt 这么多次,还没有收到对应的回应,那么主动关闭连接。

  这三个参数的默认值都比较大,就是说需要较长时间才能检测网络不可达,因此一般情况下,程序会将三个参数调小。

  可以看到,这是典型的ping ack探测方式。

应用层心跳

  如果我们使用TCP最为传输层协议,那么是否就可以依赖TCP的keepalive来检查连接的状态,或者说对端进程的active状态呢?

  答案是不能的,因为,TCP只能保证说链接是通的,但并不能表明对端是可用的,比如说对端进程处于死锁状态,但链接仍然是通的,tcp keepalive会持续收到回应,因此传输层的心跳无法发现进程的不可用状态。所以我们需要应用层的心跳,如果收到应用层的心跳回复,那么对端肯定不会是none-active的。

Failure detector与consensus

  前面提到,通过心跳,是无法准确判断target是否是crash,但有的情况下我们又需要明确知道对方是crash了?还是说只是网络不通?毕竟这是两个不同的状态。

  而且target的crash还分为crash-stop,crash-recovery两种情况。crash-stop是说一个进程如果crash了,那么不会重新启动,也就不会接受任何后续请求;而crash-recovery是说,进程crash之后会重启(是否在同一个物理机上重启没有任何关系)。

completeness vs accuracy

  如果目标是检测出target的crash状态,那么检测算法有两个重要的衡量标准:

Completeness: every process failure is eventually detected (no misses)

Accuracy: every detected failure corresponds to a crashed process (no mistakes)

  前者(completeness)是说,如果一个进程挂掉,那么一定能被检测到;后者(accuracy)是说,如果detector认为target进程挂掉了,那么就一定挂掉了,不会出现误判。

  对于crash-stop模型,只能保证completenss,不能保证accurary。completeness不难理解,而accuracy不能保证是因为网络通信中, 由于延时、丢包、网络分割的存在,导致心跳消息(无论是单向的heartbeat还是ping ack)无法到达,也就没法判断target是否已经crash,也许还活得好好的,只是与detector之间的网络出了问题。

  那如果是crash-recovery模型呢,通过简单的心跳,不仅不能保证accuracy,甚至不能保证completeness,因为target进程可能快速重启,当然增加一些进程相关的信息还是能判断crash-recovery的情况,比如target进程维护一个版本号,心跳需要对比版本号。

  总之,网络环境下,很难保证故障检测的准确性(accuracy)。

lease

  接下来,考虑一个很常见的场景,在系统中有两类进程:一个master和多个slave,master给slave分配任务,master需要通过心跳知道每个slave的状态,如果某个slave处于none-active状态,那么需要将该slave负责的任务转移到其他处于active状态的slave上。master也充当了detector的角色,每个slave都是被检测的target。

  在这个场景中,有两点需要注意,第一,使用了心跳作为探测方式,而心跳探测只能保证completeness(完整性),而不能保证accuracy(准确性)。第二,slave负责的任务是否是可重复的?即同一份任务是否可以同时在多个slave上进行。failure detection的不准确性对slave任务的重新分配有至关重要的影响。

  如果任务是可重复的,比如幂等的运算,那么当master通过心跳判断slave处于none-active状态的时候,只需要将任务重新分配给其他slave就行,不用管该slave是否还存活着。MapReduce就是这样的例子,master在worker(MapReduce中的slave进程)进程失活(甚至只是运算进度太慢)的时候,将该worker负责的任务(task)调度到其他worker上。因此可能出现多个worker负责同一份任务的情况,但这并不会产生什么错误,因为任务的计算结果需要回报给master,master保证对于一份任务只会采纳一份计算结果,而且同一份任务,不管是哪个worker执行,结果都是一致的。

  但如果任务只能由一个节点来执行呢,由于心跳检测的不准确性,那么将任务从本来还在工作的节点重新调度到其他节点时,就会出现严重的问题。比如在primary-secondary副本控制协议中,primary的角色只能由一个节点来扮演,如果同时有两个primary,那么就出现了脑裂(brain-split),从这名字就能听出来问题的严重性。因此,即使在心跳检测出现误判的情况下,也要保证任务的唯一性,或者说,需要detector与target之间达成共识:target不要对外提供服务了。这个时候Lease机制就是很不错的选择,因为Lease机制具有很好的容错性,不会受到网络故障、延迟的影响。

  关于lease机制,我在《带着问题学习分布式系统之数据分片》中有简单介绍,这里再简单提一下Lease在GFS中的使用。

  GFS采用了primary-seconday副本控制协议,primary决定了数据修改的顺序。而primary的选举、维护是由master节点负责的,master与primary的心跳中,会携带lease信息。这个lease信息是master对primary的承诺:在lease_term这个时间范围内,我不会选举出新的primary。那么对于primary,在这个时间内,执行primary的职责,一旦过了这个时间,就自动失去了primary的权利。如果primary节点本身是ok的,并且与master之间网络正常,那么在每次心跳的时候,会延长这个lease_term,primary节点持续对外服务。一旦超过lease_term约定的时间,master就会选出新的primary节点,而旧的primary节点如果没有crash,在恢复与master的心跳之后,会意识到已经有了新的primary,然后将自己降级为secondary。

关于detector

  从上面GFS的例子,可以看到master是一个单点,也就是说由一个节点来负责所以其它节点的failure detection,这就是集中式的故障检测。如下图所示:

  由于detector是单点,因此压力会比较大。更为严重的问题,在使用了lease机制的系统中,一旦detector故障,所以节点都无法获取lease,也就无法提供服务,整个系统完全不可用。

  因此,detector的高性能、高可用非常重要,以一个集群的形式提供failure detection功能。

references

buffalo cse486 failure_detectors.pdf

gunicorn.org

Failure_detector

Leases: An Efficient Fault-Tolerant Mechanism for Distributed File Cache Consistency

  

Hey,man,are you ok? -- 关于心跳、故障监测、lease机制的更多相关文章

  1. 正确理解IM长连接的心跳及重连机制,并动手实现(有完整IM源码)

    1.引言 说道“心跳”这个词大家都不陌生,当然不是指男女之间的心跳,而是和长连接相关的.顾名思义就是证明是否还活着的依据. 什么场景下需要心跳呢?目前我们接触到的大多是一些基于长连接的应用需要心跳来“ ...

  2. 您还有心跳吗?超时机制分析(java)

    注:本人是原作者,首发于并发编程网(您还有心跳吗?超时机制分析),此文结合那里的留言作了一些修改. 问题描述 在C/S模式中,有时我们会长时间保持一个连接,以避免频繁地建立连接,但同时,一般会有一个超 ...

  3. 利用PCA进行故障监测

    利用PCA进行故障监测,传统的统计指标有两种:Hotelling-T2和平方预测误差(Squared prediction error, SPE).T2统计量反映了每个主成分在变化趋势和幅值上偏离模型 ...

  4. 理解WebSocket心跳及重连机制(五)

    理解WebSocket心跳及重连机制 在使用websocket的过程中,有时候会遇到网络断开的情况,但是在网络断开的时候服务器端并没有触发onclose的事件.这样会有:服务器会继续向客户端发送多余的 ...

  5. 从零开始实现简单 RPC 框架 9:网络通信之心跳与重连机制

    一.心跳 什么是心跳 在 TPC 中,客户端和服务端建立连接之后,需要定期发送数据包,来通知对方自己还在线,以确保 TPC 连接的有效性.如果一个连接长时间没有心跳,需要及时断开,否则服务端会维护很多 ...

  6. 简易RPC框架-心跳与重连机制

    *:first-child { margin-top: 0 !important; } body > *:last-child { margin-bottom: 0 !important; } ...

  7. Netty 之 Netty生产级的心跳和重连机制

    https://blog.csdn.net/z69183787/article/details/52625095 最近工作比较忙,但闲暇之余还是看了阿里的冯家春(fengjiachun)的github ...

  8. Netty生产级的心跳和重连机制

    今天研究的是,心跳和重连,虽然这次是大神写的代码,但是万变不离其宗,我们先回顾一下Netty应用心跳和重连的整个过程: 1)客户端连接服务端 2)在客户端的的ChannelPipeline中加入一个比 ...

  9. zookeeper 故障重连机制

    一.连接多个服务器,用逗号隔开 如果在连接时候zk服务器宕机 To create a client session the application code must provide a connec ...

随机推荐

  1. JavaScript奇技淫巧

    单行写一个评级系统 var rate = 3; "★★★★★☆☆☆☆☆".slice(5 - rate, 10 - rate); CSS调试黑科技,所有元素加 随机色的outlin ...

  2. 解决PhpMyadmin1440秒未活动自动退出

    解决方法如下:#vim phpMyAdmin/libraries/config.default.php找到如下位置$cfg['LoginCookieValidity'] = ;    将1440修改成 ...

  3. Android开发——设置界面的创建

    前言: 最近忙着搞项目,难得有时间,便来整理搞项目中学习到的知识 使用之前,先介绍一下android这种的五种数据储存方式,分别为文件储存,SharePrefence,SQL,使用ContentPro ...

  4. Java中的代理模式--静态代理和动态代理本质理解

    代理模式定义:为其他对象提供了一种代理以控制对这个对象的访问. 代理模式的三种角色: Subject抽象主题角色:抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求. Real ...

  5. 错误代码和UNICODE编程

    程序错误处理 一般错误返回的数据类型有VOID BOOL HANDLE PVOID LONG/DWORD 返回值哪些代表成功和错误需查文档 错误码和解释存放在WinError.h中 使用GetLast ...

  6. Unity3D UGUI强制刷新Layout(布局)组件

    UGUI的Layout布局组件确实节省了我们很多代码 如果不使用Layout组件 那么光在计算UI的布局上就要花费很大的功夫 特别是动态生成其组件的时候 当然,Layout组件在大多数时候是非常好用的 ...

  7. 通过Beego将之前实现的短url项目实现

    正好通过这个小例子对之前了解的beego框架的基本内容进行一个简单的应用 实现的完整代码地址:https://github.com/pythonsite/go_simple_code/tree/mas ...

  8. org.hibernate.PersistentObjectException: detached entity passed to persist

    简单地来看,将一个游离的对象要被持久化(save)时报错. 我们知道要持久化对象时候,通常Hibernate会根据ID生成策略自动生成ID值,但是这个对象ID已经有值,所有抛错.这个错误会出现在配置如 ...

  9. Mycat 配置说明(server.xml)

    server.xml 几乎保存了所有mycat需要的系统配置信息,包括 mycat 用户管理.DML权限管理等,其在代码内直接的映射类为SystemConfig 类. user 标签 该标签主要用于定 ...

  10. android中activity.this跟getApplicationContext的区别

    转载: http://www.myexception.cn/android/1968332.html android中activity.this和getApplicationContext的区别 在a ...