Skip to content
Projects
Groups
Snippets
Help
Loading...
Sign in / Register
Toggle navigation
B
brpc
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Packages
Packages
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
submodule
brpc
Commits
3c0a6df2
Commit
3c0a6df2
authored
Aug 21, 2018
by
Ge Jun
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Rename peak_qps in AutoConcurrencyLimiter to max_qps which is more accurate
parent
b08edd65
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
22 additions
and
21 deletions
+22
-21
auto_concurrency_limiter.md
docs/cn/auto_concurrency_limiter.md
+16
-14
auto_concurrency_limiter.cpp
src/brpc/policy/auto_concurrency_limiter.cpp
+5
-5
auto_concurrency_limiter.h
src/brpc/policy/auto_concurrency_limiter.h
+1
-2
No files found.
docs/cn/auto_concurrency_limiter.md
View file @
3c0a6df2
...
...
@@ -37,12 +37,14 @@ server.MaxConcurrencyOf("example.EchoService.Echo") = "auto";
**max_concurrency**
: 允许的最大concurrency,又被称为“最大并发度”。
**noload_latency**
:
处理任务的平均延时,不包括排队时间
。
**noload_latency**
:
单纯处理任务的延时,不包括排队时间。另一种解释是负载近零的延时
。
**min_latency**
: 实际测定的latency中的较小值的ema,当
预估的最大并发度没有显著高于真实的
并发度时,min_latency和noload_latency接近。
**min_latency**
: 实际测定的latency中的较小值的ema,当
并发度没有高于最大
并发度时,min_latency和noload_latency接近。
**peak_qps**
: 极限qps。注意是处理或回复的qps而不是接收的qps。
**max_qps**
: 实际测定的qps中的较大值。当并法度没有高于最大并法度时,max_qps和peak_qps接近。
### Little's Law
在服务处于稳定状态时: concurrency = latency
*
qps。 这是自适应限流的理论基础。
...
...
@@ -58,17 +60,17 @@ server.MaxConcurrencyOf("example.EchoService.Echo") = "auto";
自适应限流会不断的对请求进行采样,当采样窗口的样本数量足够时,会根据样本的平均延迟和服务当前的qps计算出下一个采样窗口的max_concurrency:
> max_concurrency =
peak
_qps * ((2+alpha) * min_latency - latency)
> max_concurrency =
max
_qps * ((2+alpha) * min_latency - latency)
alpha为可接受的延时上升幅度,默认0.3。
latency是当前采样窗口内所有请求的平均latency。
peak
_qps是最近一段时间测量到的qps的极大值。
max
_qps是最近一段时间测量到的qps的极大值。
min_latency是最近一段时间测量到的latency较小值的ema,是noload_latency的估算值。
当服务处于低负载时,min_latency约等于noload_latency,此时计算出来的max_concurrency会高于实际并发,但低于真实并发,给流量上涨留探索空间。而当服务过载时,服务的qps约等于
peak
_qps,同时latency开始明显超过min_latency,此时max_concurrency则会接近或小于实际并发,并通过定期衰减避免远离真实并发,保证服务不会过载。
当服务处于低负载时,min_latency约等于noload_latency,此时计算出来的max_concurrency会高于实际并发,但低于真实并发,给流量上涨留探索空间。而当服务过载时,服务的qps约等于
max
_qps,同时latency开始明显超过min_latency,此时max_concurrency则会接近或小于实际并发,并通过定期衰减避免远离真实并发,保证服务不会过载。
### 估算noload_latency
...
...
@@ -84,7 +86,7 @@ min_latency是最近一段时间测量到的latency较小值的ema,是noload_l
方案3的问题在于,假如服务的性能瓶颈在下游服务,那么请求在服务本身的排队等待时间无法反应整体的负载情况。
方案4是最通用的,也经过了大量实验的考验。缩小max_concurrency和公式中的alpha存在关联。让我们做个假想实验,若latency极为稳定并都等于min_latency,那么公式简化为max_concurrency =
peak_qps
* latency *
(1 + alpha)。根据little's law,qps最多为peak_qps
*
(1 + alpha). alpha是qps的"探索空间",若alpha为0,则qps被锁定为peak
_qps,算法可能无法探索到”极限qps“。但在qps已经达到极限qps时,alpha会使延时上升(已拥塞),此时测定的min_latency会大于noload_latency,一轮轮下去最终会导致min_latency不收敛。定期降低max_concurrency就是阻止这个过程,并给min_latency下降提供”探索空间“。
方案4是最通用的,也经过了大量实验的考验。缩小max_concurrency和公式中的alpha存在关联。让我们做个假想实验,若latency极为稳定并都等于min_latency,那么公式简化为max_concurrency =
max_qps
* latency *
(1 + alpha)。根据little's law,qps最多为max_qps
*
(1 + alpha). alpha是qps的"探索空间",若alpha为0,则qps被锁定为max
_qps,算法可能无法探索到”极限qps“。但在qps已经达到极限qps时,alpha会使延时上升(已拥塞),此时测定的min_latency会大于noload_latency,一轮轮下去最终会导致min_latency不收敛。定期降低max_concurrency就是阻止这个过程,并给min_latency下降提供”探索空间“。
#### 减少重测时的流量损失
...
...
@@ -115,25 +117,25 @@ else:
### 估算peak_qps
#### 提高qps增长的速度
当服务启动时,由于服务本身需要进行一系列的初始化,tcp本身也有慢启动等一系列原因。服务在刚启动时的qps一定会很低。这就导致了服务启动时的max_concurrency也很低。而按照上面的计算公式,当max_concurrency很低的时候,预留给qps增长的冗余concurrency也很低(即:alpha
*
peak
_qps *
min_latency)。从而会影响当流量增加时,服务max_concurrency的增加速度。
当服务启动时,由于服务本身需要进行一系列的初始化,tcp本身也有慢启动等一系列原因。服务在刚启动时的qps一定会很低。这就导致了服务启动时的max_concurrency也很低。而按照上面的计算公式,当max_concurrency很低的时候,预留给qps增长的冗余concurrency也很低(即:alpha
*
max
_qps *
min_latency)。从而会影响当流量增加时,服务max_concurrency的增加速度。
假如从启动到打满qps的时间过长,这期间会损失大量流量。在这里我们采取的措施有两个,
1.
采样方面,一旦采到的请求数量足够多,直接提交当前采样窗口,而不是等待采样窗口的到时间了才提交
2.
计算公式方面,当current_qps > 保存的
peak
_qps时,直接进行更新,不进行平滑处理。
2.
计算公式方面,当current_qps > 保存的
max
_qps时,直接进行更新,不进行平滑处理。
在进行了这两个处理之后,绝大部分情况下都能够在2秒左右将qps打满。
#### 平滑处理
为了减少个别窗口的抖动对限流算法的影响,同时尽量降低计算开销,在计算
peak
_qps时,会通过使用
[
EMA
](
https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
)
来进行平滑处理:
为了减少个别窗口的抖动对限流算法的影响,同时尽量降低计算开销,在计算
max
_qps时,会通过使用
[
EMA
](
https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average
)
来进行平滑处理:
```
if current_qps >
peak
_qps:
peak
_qps = current_qps
if current_qps >
max
_qps:
max
_qps = current_qps
else:
peak_qps = current_qps * ema_alpha / 10 + (1 - ema_alpha / 10) * peak
_qps
max_qps = current_qps * ema_alpha / 10 + (1 - ema_alpha / 10) * max
_qps
```
将
peak_qps的ema参数置为min_latency的ema参数的十分之一的原因是: peak
_qps 下降了通常并不意味着极限qps也下降了。而min_latency下降了,通常意味着noload_latency确实下降了。
将
max_qps的ema参数置为min_latency的ema参数的十分之一的原因是: max
_qps 下降了通常并不意味着极限qps也下降了。而min_latency下降了,通常意味着noload_latency确实下降了。
### 与netflix gradient算法的对比
...
...
@@ -144,5 +146,5 @@ netflix中的gradient算法公式为:max_concurrency = min_latency / latency *
这个公式可以和本文的算法进行类比:
*
gradient算法中的latency和本算法的不同,前者的latency是最小值,后者是平均值。netflix的原意是最小值能更好地代表noload_latency,但实际上只要不对max_concurrency做定期衰减,不管最小值还是平均值都有可能不断上升使得算法不收敛。最小值并不能带来额外的好处,反而会使算法更不稳定。
*
gradient算法中的max_concurrency / latency从概念上和qps有关联(根据little's law),但可能严重脱节。比如在min_latency重置前,若所有latency都小于min_latency,那么max_concurrency会不断下降甚至到0;但按照本算法,
peak
_qps和min_latency仍然是稳定的,它们计算出的max_concurrency也不会剧烈变动。究其本质,gradient算法在迭代max_concurrency时,latency并不能代表实际并发为max_concurrency时的延时,两者是脱节的,所以max_concurrency / latency的实际物理含义不明,与qps可能差异甚大,最后导致了很大的偏差。
*
gradient算法中的max_concurrency / latency从概念上和qps有关联(根据little's law),但可能严重脱节。比如在min_latency重置前,若所有latency都小于min_latency,那么max_concurrency会不断下降甚至到0;但按照本算法,
max
_qps和min_latency仍然是稳定的,它们计算出的max_concurrency也不会剧烈变动。究其本质,gradient算法在迭代max_concurrency时,latency并不能代表实际并发为max_concurrency时的延时,两者是脱节的,所以max_concurrency / latency的实际物理含义不明,与qps可能差异甚大,最后导致了很大的偏差。
*
gradient算法的queue_size推荐为sqrt(max_concurrency),这是不合理的。netflix对queue_size的理解大概是代表各种不可控环节的缓存,比如socket里的,和并发度存在一定的正向关系情有可原。但在我们的理解中,这部分queue_size作用微乎其微,没有或用常量即可。我们关注的queue_size是给concurrency上升留出的探索空间: max_concurrency的更新是有延迟的,在并发从低到高的增长过程中,queue_size的作用就是在max_concurrency更新前不限制qps上升。而当concurrency高时,服务可能已经过载了,queue_size就应该小一点,防止进一步恶化延时。这里的queue_size和并发是反向关系。
src/brpc/policy/auto_concurrency_limiter.cpp
View file @
3c0a6df2
...
...
@@ -56,7 +56,7 @@ AutoConcurrencyLimiter::AutoConcurrencyLimiter()
,
_remeasure_start_us
(
NextResetTime
(
butil
::
gettimeofday_us
()))
,
_reset_latency_us
(
0
)
,
_min_latency_us
(
-
1
)
,
_ema_
peak
_qps
(
-
1
)
,
_ema_
max
_qps
(
-
1
)
,
_last_sampling_time_us
(
0
)
,
_total_succ_req
(
0
)
{
}
...
...
@@ -176,10 +176,10 @@ void AutoConcurrencyLimiter::UpdateQps(int32_t succ_count,
int64_t
sampling_time_us
)
{
double
qps
=
1000000.0
*
succ_count
/
(
sampling_time_us
-
_sw
.
start_time_us
);
const
double
ema_factor
=
FLAGS_auto_cl_alpha_factor_for_ema
/
10
;
if
(
qps
>=
_ema_
peak
_qps
)
{
_ema_
peak
_qps
=
qps
;
if
(
qps
>=
_ema_
max
_qps
)
{
_ema_
max
_qps
=
qps
;
}
else
{
_ema_
peak_qps
=
qps
*
ema_factor
+
_ema_peak
_qps
*
(
1
-
ema_factor
);
_ema_
max_qps
=
qps
*
ema_factor
+
_ema_max
_qps
*
(
1
-
ema_factor
);
}
}
...
...
@@ -201,7 +201,7 @@ void AutoConcurrencyLimiter::UpdateMaxConcurrency(int64_t sampling_time_us) {
}
else
{
const
double
overload_threshold
=
FLAGS_auto_cl_overload_threshold
;
int32_t
noload_concurrency
=
std
::
ceil
(
_min_latency_us
*
_ema_
peak
_qps
/
1000000
);
std
::
ceil
(
_min_latency_us
*
_ema_
max
_qps
/
1000000
);
if
(
avg_latency
<
(
1.0
+
overload_threshold
)
*
_min_latency_us
)
{
next_max_concurrency
=
std
::
ceil
(
noload_concurrency
*
(
2.0
+
overload_threshold
-
double
(
avg_latency
)
/
_min_latency_us
));
...
...
src/brpc/policy/auto_concurrency_limiter.h
View file @
3c0a6df2
...
...
@@ -60,14 +60,13 @@ private:
void
ResetSampleWindow
(
int64_t
sampling_time_us
);
void
UpdateMinLatency
(
int64_t
latency_us
);
void
UpdateQps
(
int32_t
succ_count
,
int64_t
sampling_time_us
);
double
peak_qps
();
// modified per sample-window or more
int
_max_concurrency
;
int64_t
_remeasure_start_us
;
int64_t
_reset_latency_us
;
int64_t
_min_latency_us
;
double
_ema_
peak
_qps
;
double
_ema_
max
_qps
;
// modified per sample.
butil
::
atomic
<
int64_t
>
BAIDU_CACHELINE_ALIGNMENT
_last_sampling_time_us
;
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment