作者:刘泉禄
整体介绍
本文所说的“柔性服务”次要是指 consumer 端的负载平衡和 provider 端的限流两个性能。在之前的 Dubbo 版本中,负载平衡局部更多的思考的是公平性准则,即 consumer 端尽可能平等的从 provider 中作出抉择,在某些状况下体现并不够现实。而限流局部只提供了动态的限流计划,须要用户对 provider 端设置动态的最大并发值,然而该值的正当选取对用户来讲并不容易。咱们针对这些存在的问题进行了改良。
负载平衡
在本来的 Dubbo 版本中,有五种负载平衡的计划供选择,他们别离是 “Random” , “ShortestResponse” , “RoundRobin”,”LeastActive” 和 “ConsistentHash”。
其中除 “ShortestResponse” 和 “LeastActive” 外,其余的几种计划次要是思考抉择时的公平性和稳定性。对于 “ShortestResponse” 来说,其设计目标是从所有备选的 provider 中抉择 response 工夫最短的以进步零碎整体的吞吐量。然而存在两个问题:
- 在大多数的场景下,不同 provider 的 response 时长没有非常明显的区别,此时该算法会进化为随机抉择。
- response 的工夫长短有时也并不能代表机器的吞吐能力。对于 “LeastActive” 来说,其认为应该将流量尽可能调配到以后并发解决工作较少的机器上。然而其同样存在和 “ShortestResponse” 相似的问题,即这并不能独自代表机器的吞吐能力。
基于以上剖析,咱们提出了两种新的负载平衡算法。一种是同样基于公平性思考的单纯 “P2C” 算法,另一种是基于自适应的办法 “adaptive”,其试图自适应的掂量 provider 端机器的吞吐能力,而后将流量尽可能调配到吞吐能力高的机器上,以进步零碎整体的性能。
成果介绍
对于负载平衡局部的有效性试验在两个不同的状况下进行的,别离是提供端机器配置比拟平衡和提供端机器配置差距较大的状况。
应用办法
应用办法与本来的负载平衡办法雷同。只须要在 consumer 端将 “loadbalance” 设置为 “p2c” 或者 “adaptive” 即可。
代码构造
负载平衡局部的算法实现只须要在本来负载平衡框架内继承 LoadBalance 接口即可。
原理介绍
P2C 算法
Power of Two Choice 算法简略然而经典,次要思路如下:
- 对于每次调用,从可用的 provider 列表中做两次随机抉择,选出两个节点 providerA 和 providerB。
- 比拟 providerA 和 providerB 两个节点,抉择其“以后正在解决的连接数”较小的那个节点。
adaptive 算法
代码的 github 地址 [ 1]
相干指标
- cpuLoad
cpuLoad = cpu 一分钟均匀负载 * 100 / 可用 cpu 数量。该指标在 provider 端机器取得,并通过 invocation 的 attachment 传递给 consumer 端。
- rt
rt 为一次 rpc 调用所用的工夫,单位为毫秒。
- timeout
timeout 为本次 rpc 调用超时残余的工夫,单位为毫秒。
- weight
weight 是设置的服务权重。
- currentProviderTime
provider 端在计算 cpuLoad 时的工夫,单位是毫秒
- currentTime
currentTime 为最初一次计算 load 时的工夫,初始化为 currentProviderTime,单位是毫秒。
- multiple
multiple=(以后工夫 – currentTime)/timeout + 1
- lastLatency
- beta
平滑参数,默认为 0.5
- ewma
lastLatency 的平滑值
lastLatency=betalastLatency+(1 – beta)lastLatency
- inflight
inflight 为 consumer 端还未返回的申请的数量。
inflight=consumerReq – consumerSuccess – errorReq
- load
对于备选后端机器 x 来说,若间隔上次被调用的工夫大于 2*timeout,则其 load 值为 0。
否则
load=CpuLoad(sqrt(ewma) + 1)(inflight + 1)/(((consumerSuccess / (consumerReq +1) )*weight)+1)
算法实现
仍然是基于 P2C 算法。
- 从备选列表中做两次随机抉择,失去 providerA 和 providerB
- 比拟 providerA 和 providerB 的 load 值,抉择较小的那个。
自适应限流
与负载平衡运行在 consumer 端不同的是,限流性能运行在 provider 端。其作用是限度 provider 端解决并发工作时的最大数量。从实践上讲,服务端机器的解决能力是存在下限的,对于一台服务端机器,当短时间内呈现大量的申请调用时,会导致解决不及时的申请积压,使机器过载。在这种状况下可能导致两个问题:
1. 因为申请积压,最终所有的申请都必须期待较长时间能力被解决,从而使整个服务瘫痪。
2. 服务端机器长时间的过载可能有宕机的危险。因而,在可能存在过载危险时,回绝掉一部分申请反而是更好的抉择。在之前的 Dubbo 版本中,限流是通过在 provider 端设置动态的最大并发值实现的。然而在服务数量多,拓扑简单且解决能力会动态变化的场面下,该值难以通过计算动态设置。
基于以上起因,咱们须要一种自适应的算法,其能够动静调整服务端机器的最大并发值,使其能够在保障机器不过载的前提下,尽可能多的解决接管到的申请。
因而,咱们参考局部业界计划实现根底上,在 Dubbo 的框架内实现了两种自适应限流算法,别离是基于启发式平滑的 “HeuristicSmoothingFlowControl” 和基于窗口的 “AutoConcurrencyLimier”。
代码的 github 地址 [ 2]
成果介绍
自适应限流局部的有效性试验咱们在提供端机器配置尽可能大的状况下进行,并且为了凸显成果,在试验中咱们将单次申请的复杂度进步,将超时工夫尽可能设置的大,并且开启生产端的重试性能。
应用办法
要确保服务端存在多个节点,并且生产端开启重试策略的前提下,限流性能能力更好的发挥作用。设置办法与动态的最大并发值设置相似,只需在 provider 端将 “flowcontrol” 设置为 “autoConcurrencyLimier” 或者 “heuristicSmoothingFlowControl” 即可。
代码构造
- FlowControlFilter:在 provider 端的 filter 负责依据限流算法的后果来对 provider 端进行限流性能。
- FlowControl:依据 Dubbo 的 spi 实现的限流算法的接口。限流的具体实现算法须要继承自该接口并能够通过 Dubbo 的 spi 形式应用。
- CpuUsage:周期性获取 cpu 的相干指标
- HardwareMetricsCollector:获取硬件指标的相干办法
- ServerMetricsCollector:基于滑动窗口的获取限流须要的指标的相干办法。比方 qps 等。
- AutoConcurrencyLimier:自适应限流的具体实现算法。
- HeuristicSmoothingFlowControl:自适应限流的具体实现办法。
原理介绍
HeuristicSmoothingFlowControl
相干指标
- alpha
alpha 为可承受的延时的上升幅度,默认为 0.3
- minLatency
在一个工夫窗口内的最小的 Latency 值。
- noLoadLatency
noLoadLatency 是单纯解决工作的延时,不包含排队工夫。这是服务端机器的固有属性,然而并不是变化无穷的。在 HeuristicSmoothingFlowControl 算法中,咱们依据机器 CPU 的使用率来确定机器以后的 noLoadLatency。当机器的 CPU 使用率较低时,咱们认为 minLatency 便是 noLoadLatency。当 CPU 使用率适中时,咱们平滑的用 minLatency 来更新 noLoadLatency 的值。当 CPU 使用率较高时,noLoadLatency 的值不再扭转。
- maxQPS
一个工夫窗口周期内的 QPS 的最大值。
- avgLatency
一个工夫窗口周期内的 Latency 的平均值,单位为毫秒。
- maxConcurrency
计算失去的以后服务提供端的最大并发值。
maxConcurrency=ceil(maxQPS((2 + alpha)noLoadLatency – avgLatency))
算法实现
当服务端收到一个申请时,首先判断 CPU 的使用率是否超过 50%。如果没有超过 50%,则承受这个申请进行解决。如果超过 50%,阐明以后的负载较高,便从 HeuristicSmoothingFlowControl 算法中取得以后的 maxConcurrency 值。如果以后正在解决的申请数量超过了 maxConcurrency,则回绝该申请。
AutoConcurrencyLimier
相干指标
- MaxExploreRatio
默认设置为 0.3
- MinExploreRatio
默认设置为 0.06
- SampleWindowSizeMs
采样窗口的时长。默认为 1000 毫秒。
- MinSampleCount
采样窗口的最小申请数量。默认为 40。
- MaxSampleCount
采样窗口的最大申请数量。默认为 500。
- emaFactor
平滑解决参数。默认为 0.1。
- exploreRatio
摸索率。初始设置为 MaxExploreRatio。若 avgLatency<=noLoadLatency(1.0 + MinExploreRatio) 或者 qps>=maxQPS(1.0 + MinExploreRatio) 则 exploreRatio=min(MaxExploreRatio,exploreRatio+0.02)
否则
exploreRatio=max(MinExploreRatio,exploreRatio-0.02)
- maxQPS
窗口周期内 QPS 的最大值。
- noLoadLatency
- halfSampleIntervalMs
半采样区间。默认为 25000 毫秒。
- resetLatencyUs
下一次重置所有值的工夫戳,这里的重置包含窗口内值和 noLoadLatency。单位是微秒。初始为 0.
- remeasureStartUs
下一次重置窗口的开始工夫。
- startSampleTimeUs
开始采样的工夫。单位为微秒。
- sampleCount
以后采样窗口内申请的数量。
- totalSampleUs
采样窗口内所有申请的 latency 的和。单位为微秒。
- totalReqCount
采样窗口工夫内所有申请的数量和。留神区别 sampleCount。
- samplingTimeUs
采样以后申请的工夫戳。单位为微秒。
- latency
以后申请的 latency。
- qps
在该工夫窗口内的 qps 值。
- avgLatency
窗口内的均匀 latency。
- maxConcurrency
上一个窗口计算失去以后周期的最大并发值。
- nextMaxConcurrency
以后窗口计算出的下一个周期的最大并发值。
Little’s Law
当服务处于稳固状态时:concurrency=latency*qps。这是自适应限流实践的根底。当申请没有导致机器超载时,latency 根本稳固,qps 和 concurrency 处于线性关系。当短时间内申请数量过多,导致服务超载的时候,concurrency 会和 latency 一起回升,qps 则会趋于稳定。
算法实现
AutoConcurrencyLimier 的算法应用过程和 HeuristicSmoothingFlowControl 相似。
实现与 HeuristicSmoothingFlowControl 的最大区别是 AutoConcurrencyLimier 是基于窗口的。每当窗口内积攒了一定量的采样数据时,才利用窗口内的数据来更新失去 maxConcurrency。
其次,利用 exploreRatio 来对残余的容量进行摸索。
另外,每隔一段时间都会主动放大 max_concurrency 并继续一段时间,以解决 noLoadLatency 上涨的状况。因为预计 noLoadLatency 时必须先让服务处于低负载的状态,因而对 maxConcurrency 的放大是难以避免的。
因为 max_concurrency
Dubbo 于上周上线了新版官网与文档,涵盖 Dubbo3 外围性能及个性,对于自适应负载平衡、自适应限流及更多计划的具体解说,请拜访:https://dubbo.apache.org
相干链接
[1] 代码的 github 地址
https://github.com/apache/dubbo/pull/10745
[2] 代码的 github 地址
https://github.com/apache/dubbo/pull/10642