zoukankan      html  css  js  c++  java
  • 超时,重试,熔断,限流

    2.1 超时(timeout)

    在接口调用过程中,consumer调用provider时,provider在响应时有可能会慢,如果provider 10s响应,那么consumer也会至少10s才响应。如果这种情况频度很高,那么就会整体降低consumer端服务的性能。

    这种响应时间慢的症状,就会像层层波浪一样,从底层系统一直涌到最上层,造成整个链路的超时。所以,consumer不可能无限制地等待provider接口的返回,会设置一个时间阈值,如果超过了这个时间阈值,就不继续等待。

    超时时间,一般看provider正常响应时间是多少,再追加一个buffer即可。

    2.2 重试(retry)

    超时时间是为了保护服务,避免consumer服务因为provider 响应慢而也变得响应很慢,这样consumer可以尽量保持原有的性能。但是也有可能provider只是偶尔抖动,那么超时后直接放弃,不做后续处理,就会导致当前请求错误,也会带来业务方面的损失。

    对于这种偶尔抖动,可在超时后重试一下,重试如果正常返回了,那么这次请求就被挽救了,能够正常给前端返回数据,只不过比原来响应慢一点。

    重试时的一些细化策略:重试可以考虑切换一台机器来进行调用,因为原来机器可能由于临时负载高而性能下降,重试会更加剧其性能问题,而换一台机器,得到更快返回的概率也更大一些。

    2.2.1 幂等(idempotent)

    如果允许consumer重试,那么provider就要能够做到幂等。即,同一个请求被consumer多次调用,对provider产生的影响(这里的影响一般是指某些写入相关的操作) 是一致的。

    而且这个幂等应该是服务级别的,而不是某台机器层面的,重试调用任何一台机器,都应该做到幂等。

     

    2.3 熔断(circuit break)

    重试是为了应付偶尔抖动的情况,以求更多地挽回损失。可是如果provider持续的响应时间超长呢?

    如果provider是核心路径服务,可能会down掉。 如果是一个不那么重要的服务,却因为这个服务一直响应时间长导致consumer里面的核心服务也拖慢。

    单纯超时也解决不了这种情况,因为一般超时时间,都比平均响应时间长一些,现在所有打到provider的请求都超时了,那么consumer请求provider的平均响应时间就等于超时时间了,负载也被拖下来了。而重试则会加重这种问题,使consumer的可用性变得更差。

    因此就出现熔断逻辑——如果检查出来频繁超时,就把consumer调用provider的请求直接短路掉,不实际调用,而是直接返回一个mock的值。等provider服务恢复稳定之后,重新调用。

    降级一般指是我们自身系统出现故障而降级。而熔断一般是指依赖的外部接口出现故障的情况断绝和外部接口的关系。

    例如你的A服务里面的一个功能依赖B服务,这时候B服务出问题返回的很慢。这种情况可能会因为这么一个功能而拖慢了A服务里面的所有功能,因此这时候就需要熔断!即当发现A要调用这B时就直接返回错误(或者返回其他默认值啊啥的),就不去请求B了。
    熔断设计三个模块:熔断请求判断算法、熔断恢复机制、熔断报警

    (1)熔断请求判断机制算法:使用无锁循环队列计数,每个熔断器默认维护10个bucket,每1秒一个bucket,每个blucket记录请求的成功、失败、超时、拒绝的状态,默认错误超过50%且10秒内超过20个请求进行中断拦截。

    (2)熔断恢复:对于被熔断的请求,每隔5s允许部分请求通过,若请求都是健康的(RT<250ms)则对请求健康恢复。

    (3)熔断报警:对于熔断的请求打日志,异常请求超过某些设定则报警

    2.4 降级

    当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。

    • 服务接口拒绝服务:页面能访问,但是添加删除提示服务器繁忙。页面内容也可在Varnish或CDN内获取。
    • 页面拒绝服务:页面提示由于服务繁忙此服务暂停。跳转到varnish或nginx的一个静态页面。
    • 延迟持久化:页面访问照常,但是涉及记录变更,会提示稍晚能看到结果,将数据记录到异步队列或log,服务恢复后执行。
    • 随机拒绝服务:服务接口随机拒绝服务,让用户重试,目前较少有人采用。因为用户体验不佳。

    一般而言都会建立一个独立的降级系统,可灵活且批量的配置服务器的降级功能。当然也有用代码自动降级的,例如接口超时降级、失败重试多次降级等。具体失败几次,超时设置多久,由你们的业务等其他因素决定。

    2.5 限流(current limiting)

    限流模式主要是提前对各个类型的请求设置最高的QPS阈值,若高于设置的阈值则对该请求直接返回,不再调用后续资源。

    这样一个场景,provider是一个核心服务,给N个consumer提供服务,突然某个consumer抽风,流量飙升,占用provider大部分机器时间,导致其他可能更重要的consumer不能被正常服务。

    所以,provider端需要根据consumer的重要程度,以及平时QPS大小,来给每个consumer设置一个流量上线,同一时间内只会给A consumer提供N个线程支持,超过限制则等待或者直接拒绝。

    一般限制的指标有:请求总量或某段时间内请求总量。

    请求总量:比如秒杀的,秒杀100份产品,我就放5000名进来,超过的直接拒绝请求了。

    某段时间内请求总量:比如规定了每秒请求的峰值是1W,这一秒内多的请求直接拒绝了。咱们下一秒再见。

    2.4.1 资源隔离

    provider可对consumer来的流量进行限流,防止provider被拖垮。 同样,consumer 也需要对调用provider的线程资源进行隔离,确保调用某个provider逻辑不会耗光整个consumer的线程池资源。

    2.4.2 服务降级

    降级服务既可以代码自动判断,也可以人工根据突发情况切换。当服务器压力剧增为了保证核心功能的可用性 ,而选择性的降低一些功能的可用性,或者直接关闭该功能。这就是典型的丢车保帅了。 就比如贴吧类型的网站,当服务器吃不消的时候,可以选择把发帖功能关闭,注册功能关闭,改密码,改头像这些都关了,为了确保登录和浏览帖子这种核心的功能。

    2.4.2.1 consumer 端:consumer 如果发现某个provider出现异常情况,比如,经常超时(可能是熔断引起的降级),数据错误,这是,consumer可以采取一定的策略,降级provider的逻辑,基本的有直接返回固定的数据。

    2.4.2.2 provider 端:当provider发现流量激增时,为保护自身的稳定性,也可能考虑降级服务。 比如,1直接给consumer返回固定数据,2需要实时写入数据库的,先缓存到队列里,异步写入数据库。

    3 从宏观角度重新思考

    宏观包括比A -> B 更复杂的长链路。

    长链路就是 A -> B -> C -> D这样的调用环境。

    而且一个服务也会多机部署,A 服务实际会存在 A1,A2,A3 …

    微观合理的问题,宏观未必合理。

    下面的一些讨论,主要想表达的观点是:如果系统复杂了,系统的容错配置要整体来看,整体把控,才能做到更有意义。

    3.1 超时

    如果A给B设置的超时时间,比B给C设置的超时时间短,那么肯定不合理把,A超时时间到了直接挂断,B对C支持太长超时时间没意义。

    R表示服务consumer自身内部逻辑执行时间,TAB表示consumer A开始调用provider B到返回的时间 。

    那么那么TAB > RB + TBC 才对。

    3.2 重试

    重试跟超时面临的问题差不多。

    B服务一般100ms返回,所以A就给B设置了110ms的超时,而B设置了对C的一次重试,最终120ms正确返回了,但是A的超时时间比较紧,所以B对C的重试被白白浪费了。

    A也可能对B进行重试,但是由于上一条我们讲到的,可能C确实性能不好,每次B重试一下就OK,但是A两次重试其实都无法正确的拿到结果。

    N标示设置的重试次数

    修正一下上面section的公式,TAB > RB+TBC * N。

    虽然这个公式本身没什么问题,但是,如果站在长链路的视角来思考,我们需要整体规划每个服务的超时时间和重试次数,而不是仅仅公式成立即可。

    比如下面情况:

    A -> B -> C。

    RB = 100ms,TBC=10ms

    B是个核心服务,B的计算成本特别大,那么A就应该尽量给B长一点的超时时间,而尽量不要重试调用B,而B如果发现C超时了,B可以多调用几次C,因为重试C成本小,而重试B成本则很高。 so …

    3.3 熔断

    A -> B -> C,如果C出现问题了,那么B熔断了,则A就不用熔断了。

    3.4 限流

    B只允许A以QPS<=5的流量请求,而C却只允许B以QPS<=3的qps请求,那么B给A的设定就有点大,上游的设置依赖下游。

    而且限流对QPS的配置,可能会随着服务加减机器而变化,最好是能在集群层面配置,自动根据集群大小调整。

    3.5 服务降级

    服务降级这个问题,如果从整体来操作,

    1,一定是先降级优先级地的接口,两权相害取其轻 

    2,如果服务链路整体没有性能特别差的点,比如就是外部流量突然激增,那么就从外到内开始降级。 

    3如果某个服务能检测到自身负载上升,那么可以从这个服务自身做降级。

    3.6 涟漪

    A -> B -> C,如果C服务出现抖动,而B没有处理好这个抖动,造成B服务也出现了抖动,A调用B的时候,也会出现服务抖动的情况。

    这个暂时的不可用状态就想波浪一样从底层传递到了上层。

    所以,从整个体系的角度来看,每个服务一定要尽量控制住自己下游服务的抖动,不要让整个体系跟着某个服务抖动。

    3.7 级联失败(cascading failure)

    系统中有某个服务出现故障,不可用,传递性地导致整个系统服务不可用的问题。

    跟上面涟漪(自造词)的区别也就是严重性的问题。

    涟漪描述服务偶发的不稳定层层传递,而级联失败基本是导致系统不可用。 一般,前者可能会因为短时间内恢复而未引起重视,而后者一般会被高度重视。

    3.8 关键路径

    关键路径就是,你的服务想正常工作,必须要完整依赖的下游服务链,比如数据库一般就是关键路径里面的一个节点。

    尽量减少关键路径依赖的数量,是提高服务稳定性的一个措施。

    数据库一般在服务体系的最底层,如果你的服务可以会自己完整缓存使用的数据,解除数据库依赖,那么数据库挂掉,你的服务就暂时是安全的。

    3.9 最长路径

    想要优化你的服务的响应时间,需要看服务调用逻辑里面的最长路径,只有缩短最长时间路径的用时,才能提高你的服务的性能。

  • 相关阅读:
    js——获取指定日期的前7天连续日期
    一个div自动充满当前屏幕的解决方法
    Cookie和Session
    Web服务器和浏览器间的工作原理
    软件测试基础概念
    K短路的几种求法
    ID字体
    搭建GitLab的Hexo博客记录
    项目selfcmp开发学习
    牛顿迭代法学习笔记
  • 原文地址:https://www.cnblogs.com/bsszds930/p/12746388.html
Copyright © 2011-2022 走看看