系统高可用之健康检查和健康度量那些事
一、前言
随着人们的生活水平的不断提高,人们对身体健康越来越重视,很多人都做过体检,一般公司都会有一年一度的体检福利,健康体检是家喻户晓了。
随着互联网的快速发展,同类同质产品之间的竞争越来越大,产品之间一个重要的差异就是用户体验。影响用户体验的,除了产品设计因素外,技术层面也是一个重要的影响因素,主要体现在服务的可用性和响应速度。提升服务可用性和响应速度如此重要,为了实现这样的目标,必须要有相应的手段,其中健康检查就是保障服务可用性和快速响应一个非常重要的前提。
健康检查有哪些项目、指标和方法呢?此文带你一一揭晓。
二、什么是健康检查
健康体检是指通过医学手段和方法对受检者进行身体检查,了解受检者健康状况、早期发现疾病线索和健康隐患的诊疗行为。而系统的健康检查是利用技术手段检测网络、主机、应用、服务等一系列对象是否健康或可用的过程。
三、为什么需要做健康检查
互联网产品对用户体验提出了很高的要求,但常常由于技术侧原因,发生服务响应慢或者服务不可用等一系列影响用户体验的问题,导致业务中断,影响收入,公司品牌和口碑也会受到巨大的负面影响。
影响服务不可用和响应慢的因素很多,可能是服务硬件损坏、光纤被挖断,可能是请求量过大导致数据库 CPU 负载、磁盘 IO 过高,又可能是某同学埋了雷,新上线的功能第一次运行就发生了 OOM……
要保证系统高可用,我们应该怎么做呢?有人说,系统节点冗余消除单节点故障不就行了吗。说的没错,消除单节点是系统高可用的常用手段。消除单节点有一个很重要的前提是发现问题节点,把问题节点踢除或者把流量切换到其他正常节点。
如何“发现问题节点”,就是系统健康检查需要做的事情。
四、如何做健康检查
谈论如何做健康检查前,首先要弄明白的是要检查的对象究竟是谁。对象可以网络连接,可以是一个小小的功能组件,可以是一个进程,可以是服务集群,也可以是机房单元。所以,要做到“高可用”,首先要弄清楚要做哪层面的高可用,哪些对象可能存在单点问题,要把“对象”搞清楚。
那么,健康检查如何做呢?通常有两种方式:主动和被动。
4.1 主动模式
由检查方作为主动方,定时主动发起健康检查请求,请求的报文内容或者格式通常是独立设计的,被健康的对象作简单自检后返回响应。举个例子:
配置间隔 2000 豪秒定时向后台 web 服务器 http://(ip:port)/check.do 接口发送检查请求,如果连续失败次数达到 fall=5 次,服务器被认为宕机,如果连续成功次数达到 rise=2 次,服务器被认为是 up 健康状态。当然了,响应状态码必须是 2xx 或者 3xx 才被认为是健康状态。
4.2 被动模式
被动健康检查不设计独立的健康检查请求,而是以正常连接情况或者业务请求的响应作为指标来衡量检查对象的健康状态。例如 nginx 官方开源版本的被动健康检查配置:
Nginx 是基于连接探测,如果在 30s 内尝试连接 3 次失败,则认为后端 web 服务不可用。
4.3 消除单点
上面谈到,要实现高可用就要消除单点故障,最简单直接的方案加备服务节点,通过定时心跳健康检查发现主服务节点宕机后,备服务节点把主的工作接管过来,客户端把请求流量切换到备服务节点。
主服务节点与备服务节点之间通过专用的心跳线进行健康检查,由于网络分区等原因它们可能无法收到对方心跳,这时备节点会认为主节点已宕机,主节点也认为备节点已宕机,但其实主从两节点状态都是正常的,客户端能正常访问到主从两节点,出现“双写”,这种现象在业界称为“脑裂(split-brain)”。
出现脑裂会导致数据混乱的灾难事件发生,影响业务的正确性,这时引入第三方机构进行仲裁可以有效避免脑裂的发生。出现脑裂会导致数据混乱的灾难事件发生,影响业务的正确性,这时引入第三方机构进行仲裁可以有效避免脑裂的发生。
4.4 第三方仲裁
既然主从双方无法确认对方的存活,出现争议时可以由第三方仲裁节点做出决定,到底谁是主由它说了算,第三方仲裁节点一般是由 Zookeeper 这种高可用方案来实现。
五、健康检查例子
5.1 网络设备
Keepalived 是一款保证集群高可用的服务软件,其功能类似于 heartbeat,用于防止单点故障。但是它一般不会单独出现,而是与其它负载均衡技术(如 LVS、HAProxy、Nginx)一起工作来达到集群的高可用。
它的健康检查也包含两个方面,一个是 Keepalived 组件之间的健康检查(通过 VRRP 心跳报文),如下图所示
另一个是 Keepalived 组件与本地负载均衡组件的健康检查,配置如下:
其中,应用的健康检查方式通过自定义脚本实现。
Keepalived 组件之间通过 VRRP 协议进行健康检查,如果主服务器宕机,备服务器通过 VRRP 协议选举成为新的主服务器,把虚拟 IP 从旧的主服务器上争抢过来,实现高可用。
VRRP 报文是封装在 IP 报文上的,支持各种上层协议,网络设备通常也是使用 VRRP 协议实现主备高可用切换,如交换机、路由器、防火墙等。
当网络设备发生故障时,VRRP 机制能够选举出新的网络设备承担数据流量,从而保障网络的可靠通信。
5.2 网络连接
移动设备连接互联网通过 NAT 方式,移动 App 的 PUSH 推送需要与服务器保持长连接,但大部分移动网络运营商都在连接一段时间没有数据交互时,会淘汰 NAT 列表中的对应连接,造成连接中断。为了保持网络连接的“健康”可用,我们可以在连接建立后,App 与服务器互相定期发送 Ping Pong 心跳信息来保持连接的持续有效。
以上是应用层的连接健康检查方案,操作系统也支持底层网络的连接健康检查即 Keepalive。TCP Keepalive 可以在连接无活动一段时间后,发送一个空的探测报文,使 TCP 连接不会被客户端或者防火墙等中间网络设备关闭。Linux 可以通过以下三个参数对 Keepalive 的间隔、频率和阈值和进行配置:
5.3 主机与进程
主机之间的可达性可以通过 Ping 命令进行识别,Ping 命令使用的是 ICMP 协议,它能识别从客户端到目标主机整个路径的网络连通性。Ping 通常用于手工测试某台主机是否启动和网络是否联通。
ICMP 是网络层协议,与具体进程是没关系的,无法通过 Ping 识别进程是否存在。但进程有端口,有进程信息,可以通过 telnet 端口或 ps 命令检测进程是否存在。进程可能会由于内存不足被 kill 或者其他原因异常关闭,可以通过 cron 定时脚本检测识别后自动拉起,这种方案对老破旧项目中只能单实例部署的应用的可用性提升非常有效。
5.4 中间件-RocketMQ
NameServer 是 RocketMQ 的路由中心,NameServer 中维护着 Producer 集群、Broker 集群、 Consumer 集群的服务状态和路由信息。当有新的 Consumer 加入集群时,除了上报自身信息外,还获取各个 Broker 的地址、Topic、队列等信息,这样就能知道它消费的 Topic 消息存储到哪个 Broker 和队列上。
NameServer 可以部署多个,NameServer 之间相互独立不互通。Producer、Broker、Consumer 服务启动时需要指定多个 NameServer,服务的信息会同时注册到多个指定的 NameServer 上,达到高可用。
每个 Broker 节点与所有 NameServer 会保持 TCP 长连接,每隔 30s 给 NameServer 发送心跳报文,告诉 NameServer 自己还活着。而每个 NameServer 每隔 10s 检查一下各个 Broker 的最近一次心跳时间,如果发现某个 Broker 超过 120s 都没发送心跳报文,就认为这个 Broker 已经宕机了,会关闭对应的网络连接 channel,并将其从路由信息里移除。
5.5 应用层 - Spring Boot Actuator
一个服务实例或者进程会通过定期的心跳包向其他服务来报告它的存活,但有这个心跳包还是不够的,不足以反映它的健康状况。比如磁盘空间不足了,服务已经无法再写数据了,但它还能响应心跳包;服务依赖 Redis,但 Redis 服务出了问题连接不上,但它还能响应心跳包;服务的某些功能依赖分布式存储服务,但分布式存储服务不可用了,但它依然能响应心跳包。我们可以看到,要确定一个服务实例是否存活并且“健康”,还是有很多方面需要考虑的。Spring Boot Actuator 能比较好的解决这个问题,它能反映整个服务的健康状况,包括它所依赖的子系统的健康状况。
Spring Boot Actuator 是 Spring Boot 的一个子项目,Actuator 提供 Endpoint(端点)给外部应用程序进行访问和交互。Actuator 包括许多功能,比如健康检查、审计、指标收集等等,可帮助我们监控和管理 Spring Boot 应用程序。Health 就是其中一个 Endpoint,它提供了关于 Spring Boot 应用的基本健康情况信息,允许其他云服务或者 k8s 等定时检测到应用的健康状况,对异常情况及时作出响应。
假如某微服务应用使用到了 MySQL、Amazon S3、Elastic Search、DynamocDB 这些资源系统,它的健康检查结果就应该包含所有这些子系统的健康状况:
Actuator 的健康检查由 HealthIndicator 接口实现,HealthIndicator 接口只有一个 health()方法,返回值是 Health 健康对象。
Health 对象有状态 status 和 details 两个字段,status 默认有 UNKNOWN、UP、DOWN 和 OUT_OF_SERVICE 四个值,用户可以自定义和扩展,details 是一个 KV 结构,用户可以随意自定义要返回的数据值。@JsonInclude(Include.NON_EMPTY)public final class Health extends HealthComponent {
Actuator 内置了很多常用的 HealthIndicator:
用户可以根据实际情况自定义,比如:
默认情况下 health 的状态是启用且对外开放的,通过 http://locahost:8080/actuator/health 就可以查询到应用的健康状态:{“status”: “UP”},这是一个汇总的状态,详细的健康信息可以通过配置项 management.endpoint.health.show-details=always 打开,一个完整的包含 details 的健康检查信息如下:
汇总的健康状态由 HealthAggregator 汇总而成的,汇总的算法是:所有子系统的健康状态按 DOWN、OUT_OF_SERVICE、UP、UNKNOWN 这个顺序进行排序取最前面一个状态值。
比如 ehCache 是 UP,MySQL 是 UNKNOWN,diskSpace 是 OUT_OF_SERVICE;那么排序下来就是:OUT_OF_SERVICE、UP、UNKNOWN,取第一个就是 OUT_OF_SERVICE,即服务不可用。
六、总结
高可用是一个很复杂的工程问题,它是由一系列的子问题构成,健康检查和健康度量只是其中一个。业务要保持连续不中断,系统要保证持续运行,就要保证全链路所有参与的节点都是高可用的,避免出现单点故障。
如何及时发现不健康或故障的节点并告警,如何在节点出现不健康或故障时及时 failfast/failover 避免发生雪崩效应,健康检查在其中扮演着非常重要的作用。
作者:vivo 互联网服务器团队-Chen Jianbo
版权声明: 本文为 InfoQ 作者【vivo互联网技术】的原创文章。
原文链接:【http://xie.infoq.cn/article/11139d214ef0aab0f92781096】。文章转载请联系作者。
评论