dubbo 熔断,限流,降级

1 、写在前面

1.1 名词解释前端

consumer表示服务调用方 数据库

provider标示服务提供方,dubbo里面通常就这么讲。缓存

下面的A调用B服务,通常是泛指调用B服务里面的一个接口。安全

 

1.2 拓扑图框架

大写字母表示不一样的服务,后面的序号表示同一个服务部署在不一样机器的实例。异步

2 、从微观角度思考 

2.1 超时(timeout)ide

在接口调用过程当中,consumer调用provider的时候,provider在响应的时候,有可能会慢,若是provider 10s响应,那么consumer也会至少10s才响应。若是这种状况频度很高,那么就会总体下降consumer端服务的性能。这种响应时间慢的症状,就会像一层一层波浪同样,从底层系统一直涌到最上层,形成整个链路的超时。因此,consumer不可能无限制地等待provider接口的返回,会设置一个时间阈值,若是超过了这个时间阈值,就不继续等待。这个超时时间选取,通常看provider正常响应时间是多少,再追加一个buffer便可。性能

2.2 重试(retry)优化

超时时间的配置是为了保护服务,避免consumer服务由于provider 响应慢而也变得响应很慢,这样consumer能够尽可能保持原有的性能。可是也有可能provider只是偶尔抖动,那么超时后直接放弃,不作后续处理,就会致使当前请求错误,也会带来业务方面的损失。那么,对于这种偶尔抖动,能够在超时后重试一下,重试若是正常返回了,那么此次请求就被挽救了,可以正常给前端返回数据,只不过比原来响应慢一点。重试时的一些细化策略:重试能够考虑切换一台机器来进行调用,由于原来机器可能因为临时负载高而性能降低,重试会更加重其性能问题,而换一台机器,获得更快返回的几率也更大一些。ui

2.2.1 幂等(idempotent)

若是容许consumer重试,那么provider就要可以作到幂等。即,同一个请求被consumer屡次调用,对provider产生的影响(这里的影响通常是指某些写入相关的操做) 是一致的。并且这个幂等应该是服务级别的,而不是某台机器层面的,重试调用任何一台机器,都应该作到幂等。

2.3 熔断(circuit break)

重试是为了应付偶尔抖动的状况,以求更多地挽回损失。但是若是provider持续的响应时间超长呢?若是provider是核心路径的服务,down掉基本就无法提供服务了,那咱们也没话说。 若是是一个不那么重要的服务,却由于这个服务一直响应时间长导consumer里面的核心服务也拖慢,那么就得不偿失了。单纯超时也解决不了这种状况了,由于通常超时时间,都比平均响应时间长一些,如今全部的打到provider的请求都超时了,那么consumer请求provider的平均响应时间就等于超时时间了,负载也被拖下来了。而重试则会加剧这种问题,使consumer的可用性变得更差。所以就出现了熔断的逻辑,也就是,若是检查出来频繁超时,就把consumer调用provider的请求,直接短路掉,不实际调用,而是直接返回一个mock的值。等provider服务恢复稳定以后,从新调用。

2.3.1 简单的熔断处理逻辑

目前咱们框架有经过注解使用的熔断器,你们能够参考应用在项目中。

2.4 限流(current limiting)

上面几个策略都是consumer针对provider出现各类状况而设计的。而provider有时候也要防范来自consumer的流量突变问题。这样一个场景,provider是一个核心服务,给N个consumer提供服务,忽然某个consumer抽风,流量飙升,占用了provider大部分机器时间,致使其余可能更重要的consumer不能被正常服务。因此,provider端,须要根据consumer的重要程度,以及平时的QPS大小,来给每一个consumer设置一个流量上线,同一时间内只会给A consumer提供N个线程支持,超过限制则等待或者直接拒绝。

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 服务降级

服务降级这个问题,若是从总体来操做,

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

二、若是服务链路总体没有性能特别差的点,好比就是外部流量忽然激增,那么就从外到内开始降级。 

三、若是某个服务能检测到自身负载上升,那么能够从这个服务自身作降级。

3.6 涟漪

A -> B -> C,若是C服务出现抖动,而B没有处理好这个抖动,形成B服务也出现了抖动,A调用B的时候,也会出现服务抖动的状况。

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

因此,从整个体系的角度来看,每一个服务必定要尽可能控制住本身下游服务的抖动,不要让整个体系跟着某个服务抖动。

3.7 级联失败(cascading failure)

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

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

涟漪描述服务偶发的不稳定层层传递,而级联失败基本是致使系统不可用。 通常,前者可能会由于短期内恢复而未引发重视,然后者通常会被高度重视。

3.8 关键路径

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

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

数据库通常在服务体系的最底层,若是你的服务能够会本身完整缓存使用的数据,解除数据库依赖,那么数据库挂掉,你的服务就暂时是安全的。

3.9 最长路径

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

相关文章
相关标签/搜索