• 微服务9:服务治理来保证高可用 Hello


    ★微服务系列

    微服务1:微服务及其演进史
    微服务2:微服务全景架构
    微服务3:微服务拆分策略
    微服务4:服务注册与发现
    微服务5:服务注册与发现(实践篇)
    微服务6:通信之网关
    微服务7:通信之RPC
    微服务8:通信之RPC实践篇(附源码)
    微服务9:服务治理来保证高可用

    1 微服务带来的挑战

    在第2篇《微服务2:微服务全景架构 》中,我们曾经分析过微服务化后所面临的挑战,有过如下的结论:

    1.1 分布式固有复杂性

    微服务架构是基于分布式的系统,而构建分布式系统必然会带来额外的开销。
    性能: 分布式系统是跨进程、跨网络的调用,受网络延迟和带宽的影响。
    可靠性: 由于高度依赖于网络状况,任何一次的远程调用都有可能失败,随着服务的增多还会出现更多的潜在故障点。因此,如何提高系统的可靠性、降低因网络引起的故障率,是系统构建的一大挑战。
    分布式通信: 分布式通信大大增加了功能实现的复杂度,并且伴随着定位难、调试难等问题。
    数据一致性: 需要保证分布式系统的数据强一致性,即在 C(一致性)A(可用性)P(分区容错性) 三者之间做出权衡。这块可以参考我的这篇《分布式事务》。

    1.2 服务的依赖管理和测试

    在单体应用中,通常使用集成测试来验证依赖是否正常。而在微服务架构中,服务数量众多,每个服务都是独立的业务单元,服务主要通过接口进行交互,如何保证它的正常,是测试面临的主要挑战。所以单元测试和单个服务链路的可用性非常重要。

    1.3 有效的配置版本管理

    在单体系统中,配置可以写在yaml文件,分布式系统中需要统一进行配置管理,同一个服务在不同的场景下对配置的值要求还可能不一样,所以需要引入配置的版本管理、环境管理。

    1.4 自动化的部署流程

    在微服务架构中,每个服务都独立部署,交付周期短且频率高,人工部署已经无法适应业务的快速变化。有效地构建自动化部署体系,配合服务网格、容器技术,是微服务面临的另一个挑战。

    1.5 对于DevOps更高的要求

    在微服务架构的实施过程中,开发人员和运维人员的角色发生了变化,开发者也将承担起整个服务的生命周期的责任,包括部署、链路追踪、监控;因此,按需调整组织架构、构建全功能的团队,也是一个不小的挑战。

    1.6 更高运维成本

    运维主要包括配置、部署、监控与告警和日志收集四大方面。微服务架构中,每个服务都需要独立地配置、部署、监控和收集日志,成本呈指数级增长。服务化粒度越细,运维成本越高。

    2 迫切的治理需求

    正是因为有这些弊端,所以对微服务来说,有了更迫切的服务治理需求,以弥补弊端产生的问题。
    可以看看下面的这张图,这是一个典型的微服架构,他包含4层的Load Balance,7层的GateWay,计算服务,存储服务,及其他的一些中间件系统。
    实际上,但凡有需要微服务化的系统,都是具备一定规模了。一般会有很多模块构成,相应的部署节点也会非常多,这样故障的概率就会大幅增加,比如磁盘故障、网络故障,机器宕机,触发一些内核bug或者是运行环境漂移等。
    本质上也是微服务细粒度拆分后提升了出问题的概率,正如上面说的,分布式系统有它固有的复杂性,相比于单体服务错误会显著的增多,需要高可用方案来保证复杂通信链路的健壮性。
    image

    3 如何进行服务可用性治理

    我们有很多种方法对服务进行治理来保障服务的高可用。但总的来说有4类:

    • 流量调控:方法主要是金丝雀发布(灰度发布)、ABTesting、流量染色。
    • 请求高可用:方法主要有超时重试、快速重试以及负载均衡。
    • 服务的自我保护:主要包括限流、熔断和降级。
    • 应对故障实例:主要分为异常点驱逐和主动健康检查。

    3.1 流量调控

    3.1.1 金丝雀发布、ABTesting

    image
    流量调度中典型的金丝雀场景,你可以先放行一部分流量到一个新的服务实例中,这个新的服务实例只有你的研发和测试团队可以接入。可以在上面试用或者测试,直到你确认你的服务是健康的,没有bug的,再把流量逐渐的迁移过去。
    这个的好处是减少发布新功能存在的风险,而且全程是无停服发布,对用户是透明无感知的,大大提高了可用性

    3.1.2 流量染色

    image
    流量染色也是一种典型的场景。如果你想让不同的用户群体(比如这边的Group A、Group B、Group C)使用的功能也是不同的,那流量染色是一个不可缺少的功能。
    它可以把符合某些特征的用户流量调控到对应的服务版本中。比如GroupA是学生群体,对应到V1版本,GroupB是老人群体,对应到V2版本。需要注意的是,如果是一条完整的链路,那链路上的各个服务包括数据存储层都应该有不同的版本,这样才能一一对应。

    3.2 请求高可用

    3.2.1 超时

    image
    假设你有两个服务,服务A和服务B,服务A向服务B请求数据。但是B服务由于非常繁忙,在给定的时间周期内(红色时间线)都无法响应。而这个红色时间线是A服务固定的超时时间,如果这个时间之后还没有等到B服务的响应,A服务就不等了,去执行其他的任务。
    这个其实就是一个超时的基本概念。它的意义在于可以避免一些长时间的无意义的等待,因为这个时候下游可能是处于故障或者有请求堆积,短时间内可能是无法返回正常的结果的。
    因此,服务A在超时之后,可以及时释放自己的一些资源,比如线程或者是请求相关的其他资源。
    在实践中,超时时间的设置通常要比正常的请求时间稍微大一些(正常的返回时间可以根据平响进行分析),这样可以避免请求还没有来得及返回就触发超时。当然这个超时时间也不能设置太大。如果太大的话,在服务B出现异常的时候,服务A不能够及时的释放资源,会导致请求堆积,降低自身服务的吞吐能力。

    3.2.2 重试

    image
    跟上面一样,A服务向B服务获取数据,由于B服务非常繁忙,在给定的超时时间内无法获得响应数据。于是A设置了重试机制,在超时时间结束之后,重新获取一下B服务的数据,这时候B服务已经不忙碌了,很快就把正常数据响应给A服务。
    可以看到,重试的意义在于可以提升一次请求的成功率。通常重试不仅可以配合超时,也可以配合一些其他种类的失败。
    比如B服务5xx错误了,但可能是有概率的错误,所以重试一次就可能获取到想要的结果。当然重试也有一些注意事项,避免重试带来其他灾难。

    • 重试尽量避开之前已经选择过的失败实例,因为这个时候再重试,大概率还是错误的,意义不是特别大。
    • 其次重试的次数也不能太多,否则很容易对服务B造成数倍的压力,导致服务B发生一些雪崩。
    • 对于多次重试,我们通常可以配合一些类似于退避重试的策略来减缓对服务B的压力。退避策略说明算法

    3.2.3 快速重试(backup request)

    image
    同上面一样,服务A向服务B发起一个正常的请求,服务B工作繁忙,A服务在给定的超时时间之前都无法获得响应。按照之前的做法就是等超时时间达到的时候,再发起一次请求。
    但是可以在超时时间到达之前做一次更智能的处理,比如超时时间线的中间点,再请求一次服务B。可以看到,重试其实就是一次backup request。正是由于它在正常的超时之前就触发了,所以我们叫它快速重试。
    这次快速重试,刚好服务B可能已经缓过来。他就收到了这个请求,给服务A返回了这个结果。
    服务A在正常的超时触发之后,就是红色这条线,他也会发起一次正常的标准重试,这个时候服务B也有可能会再给他返回一次信息,快速重试的返回和正常返回,他们的时序是有可能不一定的。通常我们的处理方法是服务A先收到哪一个回复,就以哪个为准。后面收到了就会被抛弃掉。
    这边需要注意的是,多一次重试会有一定的额外资源开销,所以在使用的时候,需要注意快速重试和正常重试合理设置,避免总的重试次数过多导致服务可用性反受影响。

    3.2.4 负载均衡

    image
    假设服务B有多个实例,对于服务A的请求,我们希望它是比较均匀的流向B服务的各个实例,这样才能真正做到负载均衡,提供更稳定的服务。比较常用的一些策略比如随机轮询、RR顺序轮询等。

    我们在实际的生产实践中会有一些比较高级的负载均衡策略,比如说Least Conn、Least Request,他会观测你后端集群中连接数、请求数最少的实例进行分配。
    还有如LA负载均衡策略,它可以动态计算后端的压力,比如说,可以根据qps和延迟来算一个权重。那些qps很高,延迟又很低的后端实力,我们可以认为它是一个比较优秀的后端实例,处理能力比较强,我们会给它比较高的权重,反之就给一个稍微低一些的权重。

    另外一个比较常用的负载均衡策略就是一致性哈希。一致性哈希指的是说保证相同来源的请求能够落入相同的后端实例。这在一些后端实例有缓存,或者是有一些类似的场景的话,可以大幅提升请求的性能。

    3.3 服务的自我保护

    3.3.1 限流

    image
    正常一个长期稳定运行的服务,他们的请求是正常波形状且符合预期的,你可以观察他的流量峰值、平均值来判断服务真正的吞吐。
    image
    如果你的服务突然遇到持续性的、高频率的、不符合预期的突发流量。你需要检查一下服务是否有被错误调用、恶意攻击,或者下游程序逻辑问题。参考我的这一篇,就是典型的下游疯狂调用。
    这种超出预期的调用经常会造成你的服务响应延迟,请求堆积,甚至服务雪崩。而雪崩会随着调用链向上传递,导致整个服务链的崩溃,对我们的系统造成很大的隐患。
    限流通常指的是上游服务(服务B)这一侧,任何服务处理能力总是有限的,所以在超过他的处理能力之后,我们需要一些保护行为来避免服务过载。通常的做法就是让服务B快速返回失败来进行自我保护,否则大量请求在服务B堆积,在请求队列里阻塞,会造成大量资源损耗,也导致正常的请求无法被有效处理。
    一些常见的限流方法,比如QPS限流、连接数限流,并发请求数限流等,都可以有效的对服务进行保护,下面列举几种常见的限流算法。

    1. 时间窗:简单易用
      image
      时间窗实现非常简单,大概原理是我们有一个统计的时间窗,比1分钟之内,我们只允许通过1000个请求。但是这种方法有一个比较大的缺陷,就是不太稳定。比如刚好在前10秒,就来了1000个请求,而后面50秒就不能够再接受任何请求了,非常的不均匀。所以在要求严谨的生产环境中比较少使用。

    2. 漏桶算法:定速流出
      image
      对于漏桶来说,由于它的出水口的速度是恒定的,也就是消化处理请求的速度是恒定的,所以它可以保证组件以恒定的速率来处理请求,这对一些对处理速度或者资源有严格要求的系统是非常实用的。

    3. 令牌桶:定速流入
      image
      令牌桶配了一个蓄水池,这个蓄水池通常如果请求比较少的话,那么它一直往蓄水池里面放水,就会导致这个蓄水池的容量会稍微多一些。那这样的话,在接下来的一个瞬时的流量高峰,它可以允许系统经过比这个平均速率更高一些的请求高峰。所以它有一定的弹性,在实践中也得到了非常广泛的使用。

    3.3.2 熔断和降级

    image
    假设服务B非常繁忙,对于服务A正常的请求未能及时的返回结果,一直在Pending。而服务A在触发超时时间之后,按照重试策略又发了一次请求,服务B依然没有给返回。服务A经过多次重试,觉察到服务B有一些异常,他就自己做决策,认为服务B可能已经无法提供服务了,这个时候继续发出重试请求,可能意义不太大。所以它主动发起熔断(注意,是服务A发起的,因为B服务可能已经死了),就是一段时间内不再请求服务B。

    A既然发起了熔断,那他总得返回对应的信息给用户,不能让用户或者更下游的服务一直等待。

    这时候的处理方式就是fall back 到默认的处理信息,比如跳到一个预设的函数,指定返回默认设置的静态信息。或者对返回值进行降级,比如说是之前请求成功的一些信息,或者一些缓存的旧值,这样比什么都不返回好很多。
    笔者这边的做法是制定一个特定的返回结构(包含状态码、错误信息、flag),带有特定信息标志,让前端可以识别出是正常的返回还是熔断自动返回。

    3.4 实例故障后的离群检测

    3.4.1 异常点驱逐

    image
    当集群中的某个服务实例发生故障的时候,其实我们最优先的做法是先进行驱逐,然后再检查问题,处理问题并恢复故障。所以,能否快速的对异常实例进行驱逐,对提高系统的可用性很重要。
    下面的是ServiceMesh中Istio的异常驱逐配置,表示每秒钟扫描一次上游主机,连续失败 2 次返回 5xx 错误码的所有主机会被移出负载均衡连接池 3 分钟,并且上游被离群的主机在集群中占比不应该超过10%。

    outlierDetection:
          consecutiveErrors: 2
          interval: 1s
          baseEjectionTime: 3m
          maxEjectionPercent: 10
    

    说明:
    驱逐: 一段时间内出现多次失败,屏蔽该实例一段时间
    恢复: 屏蔽时间之后,再尝试请求该实例

  • 相关阅读:
    ElasticSearch 2 (23)
    ElasticSearch 2 (22)
    微信小程序框架模板部署:mpvue2.x+typescript+webpack3.x
    mpvue添加对scss的支持
    mpvue 封装axios请求方法
    Vue中qs插件的使用
    在微信小程序中使用less/sass
    微信小程序封装request请求
    VSCode --tsc : 无法加载文件
    Vue项目中的RSA加解密
  • 原文地址:https://www.cnblogs.com/wzh2010/p/16151105.html
Copyright © 2020-2023  润新知