HBase主备双服务高可用之路的探索

Posted 大猿小猿向前冲

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了HBase主备双服务高可用之路的探索相关的知识,希望对你有一定的参考价值。

 作为一个真正的程序员,首先应该尊重编程,热爱你所写下的程序,他是你的伙伴,而不是工具。



HBase主备双服务高可用之路的探索

一、背景

这里的高可用并不是指HBase本身的高可用机制。而是HBase主备双服务的高可用,线上业务依赖于主备HBase集群来提供数据支持,主集群首要的任务时负责数据的读写,备集群只是为了容灾。

对于HBase主备服务高可用方案的调研,团队内部从未停止过探索的步伐。从最初手动切换nginx的域名映射,到统计异常日志占比,然后进行自动的域名切换。那时候我们面临的状况是,主集群大量读写超时、甚至服务不可用,造成业务方接口无法为用户提供正常的线上业务时,HBase运维小伙伴们才能感知到HBase集群的异常状态,手动切换流量至备集群,从而在服务恢复的时间内,造成了无法容忍的损失。

针对旧方案的种种痛点,以及受微服务中熔断概念的启发,最终选择集成了饿了么提供的一个熔断框架——doctor,实现了HBase主集群服务查询异常时,查询流量能够及时、自动、无感知地进行切换到备集群。

二、HBase熔断API目前已实现的功能

    基于happybase封装的HBase读写操作的基本功能API

    错误请求比例到达一定阈值,触发熔断机制,主备集群自动无感知切换

    主备切换后,熔断的恢复机制,将自动感知主集群是否可以正常提供线上服务

    服务异常切换时,精确到单个接口的微信预警

三、关于熔断

一般在微服架构中,有一个组件角色叫熔断器。顾名思义,熔断器起的作用就是在特定的场景下关掉当前的通路,从而起到保护整个系统的效果。

在微服务架构中,一般我们的独立服务是比较多的,每个独立服务之间划分责任边界,并通过约定协议接口来进行通信。当我们的调用链路复杂依赖多时,很可能会发生雪崩效应。

假设有这么一个场景,有A, B, C, D四个独立服务,A会依赖B,C,D;当D发生负载过高或网络异常等导致响应过慢或超时时,很可能A会因此堆积过多的等待链接,从而导致A的状态也转为异常,后面依赖到A的其他服务跟着发生链式反应,这将会导致大面积的服务不可用,即使本来是一些没有依赖到B,C,D的服务。如下图所示:

img

这不是我们希望看到的结果,所以这个时候熔断器可以派上用场。最简单的做法,我们为每个依赖服务配置一个熔断器开关,正常情况下是关闭的,也就是可以正常发起请求;当请求失败(超时或者其他异常)次数超过预设值时,熔断器自动打开,这时所有经过这个熔断器的请求都会直接返回失败,并没有真正到达所依赖的服务上。这时服务A本身仍然是能正常服务的。当然,我们针对失败请求的策略,并没有这么简单粗暴。

四、借鉴HBase熔断切换在有赞团队内的实践

HBase 虽然提供了 HBase Replication 机制,用来实现集群间单方向的异步数据复制,线上虽然部署了双集群,备集群 SSD 分组和主集群 SSD 分组有相同的配置。当主集群因为磁盘,网络,或者其他业务突发流量影响导致某些 RegionServer 甚至集群不可用的时候,就需要提供备集群继续提供服务,备集群的数据可能会因为 HBase Replication 机制的延迟,相比主集群的数据是滞后的,按照我们集群目前的规模统计,平均延迟在 100ms 以内。所以为了达到高可用,业务方只能接受复制延迟,放弃强一致性,选择最终一致性和高可用性。

有赞技术团队对于HBase高可用服务接口的设计,同样使用了熔断的概念,只是其底层的熔断技术依赖于java微服务中的Hystrix框架。其简单的客户端高可用方案原理图如下所示:

HBase主备双服务高可用之路的探索
img

以上文字描述摘选自有赞的技术博客,详情可以参考链接,有赞 HBase 技术实践:读流程解析与优化

五、熔断在我们的HBase接口服务中的应用

与微服务中的熔断概念类比,我们也可以把我们的主备HBase集群看做是两个独立的服务,而我们的业务方则需要依赖这一个HBase服务,对外提供自己的服务。这里稍微有一点不一样的地方是,我们HBase服务的角色是由两个集群来担任,正常情况下,只有一个集群来承担起HBase服务的功能。HBase熔断切换的简单示例如下:

HBase主备双服务高可用之路的探索
hbase-rongduan
  • 正常状态下APP的请求通过熔断器只会落在主集群上

  • 当发生例如超时异常时,在指定的窗口期内,错误的请求数达到一定的阈值,熔断器就会认为,HBase主集群处于非正常状态,此时,在服务的最小恢复时间内,所有的请求通过熔断器,会落在备集群中。而熔断器与主集群的通信链路则是被锁定的。

  • 过了指定的服务的最小恢复时间,还未到达服务的最大恢复时间时,APP的请求会随机落在主备集群,当主集群的请求依旧异常时,熔断器会继续锁住与主集群的通信链路。直至时间达到服务的最大恢复时间,熔断器才会继续尝试把请求落在主集群上。

六、HBase熔断工作的流程图

此处,我们以get请求举例,用流程图来演示我们的HBase查询熔断与主备切换机制。

Untitled Diagram

七、滚动计数RollingNumber

如果想要更深入地理解主备熔断切换的设计理念,那么,需要优先理解一下滚动窗口计数,以及阈值判断相关的一些内容。doctor熔断框架的设计中,依赖于滑动窗口时间内的滚动计数,来进行阈值计算,从而判断当前服务的健康状况。

1. 滚动计数的概念

滚动计数的行为类似于一个拥有固定长度的先进先出队列,或者时间戳序列上的滑动窗口。一个滚动计数的值是队列元素的和,时钟结束时,最后一个元素的值将滚动到先前的位置,传递了一个时间粒度,这个时间粒度,默认1s。下面将借助一个小例子,具体来说明这种机制。

示例中我们使用的滑动窗口长度为4,移位的时间戳粒度为1s。总的时间周期是20s。

  1. 初始时创建一个填充4个0元素的列表,这是整个滚动计数行为的最开始的状态。

[0000]
  1. 在第一个滑动窗口的时间周期(4s)内,假如第一秒内处理了3个请求,第二秒内处理了2个请求,第三秒内处理了5个请求,第四秒内处理了4个请求,那么,此时列表中的状态如下,需要计算的指标从上至下依次为,请求的总数失败请求的总数失败请求所占的比例

[3254]
+--- 14 ---+
+---  7 ---+
+--- 0.5 ---+

# 则这一个滑动窗口的时间周期内的请求数总和为14,假如失败的请求总数为7,那么,此时间周期内的失败比例为7 / 14 = 0.5
  1. 假如在第五秒处理的请求为4,滑动窗口需要前移一个时间粒度,此时列表中的状态如下:

3, [2544]
   +--- 15 ---+
   +---  3 ---+
   +--- 0.2 ---+

# 则这一个滑动窗口的时间周期内的请求数总和为15,假如失败的请求总数为3,那么,此时间周期内的失败比例为3 / 15 = 0.2
  1. 依次类推,第m个时间周期内

32544,..., 6, [8244], 5 ... (<= time passing 20s)
                      +--- 18 ---+
                                          +--- 6 ---+
                                        +--- 0.33 ---+
# 则第m个滑动窗口的时间周期内的请求数总和为18,假如失败的请求总数为6,那么,此时间周期内的失败比例为6 / 18 = 0.33

八、深入理解熔断在我们HBase接口服务中的工作机制

1. HBase熔断机制工作的核心参数

读写阈值判定的配置示例

READ_DOCTOR_CONF = dict(
            # Metrics settings.
            METRICS_GRANULARITY=1,  # sec
            METRICS_ROLLINGSIZE=10,
            # Health settings.
            HEALTH_MIN_RECOVERY_TIME=10,  # sec
            HEALTH_MAX_RECOVERY_TIME=2 * 10,  # sec
            HEALTH_THRESHOLD_REQUEST=5 * 1,  # per `INTERVAL`
            HEALTH_THRESHOLD_TIMEOUT=0.01,  # percentage per `INTERVAL`
            HEALTH_THRESHOLD_SYS_EXC=0.01,  # percentage per `INTERVAL`
            HEALTH_THRESHOLD_UNKWN_EXC=0.01,  # percentage per `INTERVAL`
     )
WRITE_DOCTOR_CONF = dict(
            # Metrics settings.
            METRICS_GRANULARITY=3,  # sec
            METRICS_ROLLINGSIZE=20,
            # Health settings.
            HEALTH_MIN_RECOVERY_TIME=20,  # sec
            HEALTH_MAX_RECOVERY_TIME=2 * 60,  # sec
            HEALTH_THRESHOLD_REQUEST=10 * 1,  # per `INTERVAL`
            HEALTH_THRESHOLD_TIMEOUT=0.5,  # percentage per `INTERVAL`
            HEALTH_THRESHOLD_SYS_EXC=0.5,  # percentage per `INTERVAL`
            HEALTH_THRESHOLD_UNKWN_EXC=0.5,  # percentage per `INTERVAL`
     )

核心参数解读

  • METRICS_GRANULARITY:滚动计数中窗口移位的时间粒度

  • METRICS_ROLLINGSIZE:滚动计数中滑动窗口的长度

  • HEALTH_MIN_RECOVERY_TIME:服务发生异常时的最小恢复时间

  • HEALTH_MAX_RECOVERY_TIME:服务发生异常时的最大恢复时间

  • HEALTH_THRESHOLD_REQUEST:此参数用于控制是否需要进行错误阈值计算

  • HEALTH_THRESHOLD_TIMEOUT:发生超时异常的请求数与请求总数的比值,超过此设定,将触发主备服务的熔断切换

  • HEALTH_THRESHOLD_SYS_EXC:发生系统异常的请求数与请求总数的比值,超过此设定,将触发主备服务的熔断切换

  • HEALTH_THRESHOLD_UNKWN_EXC:发生未知异常的请求数与请求总数的比值,超过此设定,将触发主备服务的熔断切换

2. 判断接口是否健康的策略

判断当前接口是否健康的详细策略

  • 如果当前api(例如:getRow的一个操作)在读/写主集群时严重出错,则会直接去从备集群中获取结果,在配置中设定的服务最小恢复时间内,进一步的请求不会再操作主集群。

  • 如果主集群恢复健康,但是熔断器此时并不知道主集群已经恢复正常了,它的恢复机制是:在配置中设定的服务恢复的最小和最大时间之间,请求通过熔断器,随机去操作主备集群,如果期间访问主集群的请求,有一次发生异常,熔断器就会锁住与主集群的通信链路,余下所有请求将会访问备集群,继续去等待最小间隔,然后开启随机访问模式,直至达到配置设定的服务最大的恢复时间,熔断器认为主集群已恢复上线,之后的请求又会继续操作主集群。

错误阈值说明

  • 接口错误包括系统错误与操作的超时

  • 阈值是百分比,错误请求数/总的请求数

  • 当一个滑动窗口时间内的请求计数大于配置设定的THRESHOLD_REQUEST时,才会触发进一步的错误阈值检查,然后,如果错误阈值大于设定的比例时,才会触发最终的熔断切换。

3. 健康检查

  • 每一次请求经过熔断器,都会触发健康检查。

九、总结

上述便是对HBase熔断思想所做的一个由浅入深的解释,用于实现业务方访问HBase时,对于主备HBase集群的状态切换无感知。即使主集群处于异常状态,我们依旧可以为业务方提供正常的HBase服务。


以上是关于HBase主备双服务高可用之路的探索的主要内容,如果未能解决你的问题,请参考以下文章

可视化服务高可用探索

HeartBeat基础配置(实现Web服务双机热备)

HeartBeat基础配置(实现Web服务双机热备)

使用heartbeat+monit实现主备双热备份系统

阿里HBase高可用8年“抗战”回忆录

基于DevOps 微服务以及k8s的高可用架构探索与实现