背景
pipelimit
用于限制sip请求, 定义了管道(pipe
)概念,可用来表示来自用户的SIP请求流(INVITE
or REGISTER
)。
脱胎于ratelimit
, 可以在数据库中自定义pipe
限制,专注于流量整形
,没有队列功能。依赖数据库,
官方文档:pipelimit
支持的算法有:
-
Tail Drop Algorithm(TAILDROP)
这是一个简单的算法, 和定时器一起使用,但是和长时间间隔的定时器使用有风险。
当定时器开始之前,会清除计数。定时器开始后,每次请求计数增加,达到限制之后,pl_check()
返回false
-
Random Early Detection Algorithm(RED)
随机早期检测算法,这是默认算法. 通过测量平均负载并动态调整丢弃率来规避TAILDROP
算法带来的同步问题。
使用此算法时, kamailio会每第n个包向路由引擎返回错误, 试图把上一个时间间隔的负载均匀分布到当前时间间隔。
负面影响是:有可能会丢信息即便是未达到时间间隔. 出现这种现象请减少时间间隔。
-
Network Algorithm(NETWORK)
依赖网口提供的信息,每时间间隔内检测网口上等待消耗的字节数,如果数据超过限制,pl_check()
返回false
-
Feedback Algorithm(FEEDBACK)
使用PID
控制模式,根据cpu负载系数动态调整下降率, 以便负载系数保持在指定值附近。
由于读取cpu负载开销较大, 因此每隔定时时间才读一次,反馈值也会在这段时间间隔内重新计算。
本次测试的kamailio版本:
version: kamailio 5.8.5 (x86_64/linux)
参数解析
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# hash_table大小, slots:2^hash_size, hash_size最大10,默认为6
modparam("pipelimit", "hash_size", 10)
# 数据库连接
modparam("pipelimit", "db_url", "DBURL")
# pipe数据表名,默认:pl_pipes
modparam("pipelimit", "plp_table_name", "pl_pipes")
# 定时器时间,单位秒,默认10
modparam("pipelimit", "timer_interval", 5)
# 控制使用哪种定时器, 0:使用主程快速定时器, 1:使用辅助轮式定时器, 默认:0
modparam("pipelimit", "timer_mode", 1)
# 控制是否获取cpu和流量负载,0:不获取,1: 定时获取. 默认:1
modparam("pipelimit", "load_fetch", 0)
# 当达到限制时,返回错误值,默认: 503
modparam("pipelimit", "reply_code", 505)
# 错误原因,默认: Server Unavailable
modparam("pipelimit", "reply_reason", "Limiting")
# 到达多长时间间,是否清除未使用的pipes, 默认:0(不清理)
modparam("pipelimit", "clean_unused", 10)
|
重要函数
pl_check(name [, algorithm, limit])
检查当前name
的pipe请求,可以用在ANY_ROUTE
,参数:
name
: pipe名称
algorithm
: 算法名称, 可以为: TAILDROP
,RED
,NETWORK
,FEEDBACK
limit
: 限制值,默认: 0(不限制)
返回值:
- -2: pipe没找到
- -1: pipe达到限制
- 1: pipe有限制但是没达到
- 2: pipe有NOP算法
pl_active(name)
检查name
的pipe是否创建了, 1: pipe被发现; -1: pipe没发现;可以用在ANY_ROUTE
pl_drop([ [min ], max ])
对于当前的请求,达到限制后503- Server Unavailable
已发送返回.
如果没设置参数, 这个reply不带Retry-After
头
如果设置max
, 那么带Retry-After:max
如果设置了min
和max
, 那么带Retry-After:x
, x
为min
和max
之间的随机值.
实战
pl_pipes数据表
pl_pipes的表结构为:

pipeid
: pipe的唯一id
algorithm
: 算法可以为: NOP,RED, TAILDROP, FEEDBACK, NETWORK
plimit
: 每秒限制数
配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
loadmodule "pipelimit.so"
modparam("pipelimit", "hash_size", 10)
modparam("pipelimit", "db_url", DBURL)
modparam("pipelimit", "timer_interval", 5)
modparam("pipelimit", "timer_mode", 0)
modparam("pipelimit", "reply_code", 505)
modparam("pipelimit", "reply_reason", "Limiting")
request_route {
...
if (is_method("INVITE")) {
$var(check_result) = pl_check("one");
switch($var(check_result)) {
case -2:
xlog("L_ALERT","pl_check(\"$var(p)\") drop -pipe NOT found\n");
pl_drop();
exit;
break;
case -1:
xlog("L_ALERT","pl_check(\"$var(p)\") drop\n");
pl_drop("5");
exit;
break;
case 1:
xlog("L_INFO", "pl_check(\"$var(p)\") pass\n");
break;
case 2:
xlog("L_ALERT","pl_check(\"$var(p)\") pass -NOP algorithm\n");
break;
default:
xlog("L_ERR","pl_check(\"$var(p)\") dropping with unexpected retcode=$var(check_result)\n");
pl_drop();
exit;
}
}
...
}
|
数据表pl_pipes
新增数据: insert into pl_pipes(pipeid, algorithm, plimit) values('one', 'TAILDROP', 2);
pipeid
即是pl_check
的参数name
测试
使用sipp
分别做uac
和uas
, 并发请求,得到的日志为:
1
2
3
4
5
6
7
8
|
9(5257) INFO: <script>: ------172.16.4.113:5666|172.16.4.111:5461|udp|INVITE
9(5257) INFO: <script>: pl_check("0") pass
10(5258) INFO: <script>: ------172.16.4.113:5666|172.16.4.111:5461|udp|INVITE
10(5258) INFO: <script>: pl_check("0") pass
14(5262) INFO: <script>: ------172.16.4.113:5666|172.16.4.111:5461|udp|INVITE
14(5262) ALERT: <script>: pl_check("0") drop
9(5257) INFO: <script>: ------172.16.4.113:5666|172.16.4.111:5461|udp|INVITE
9(5257) ALERT: <script>: pl_check("0") drop
|
我们配的是2个请求限制/5s,当请求大于2时, 可以看到日志有报错,并且剩下的请求返回505

因为我们配了pl_drop("5")
,所以头带了Retry-After:5
总结
此pipelimit
模块和上章介绍的pike
功能大体相似,都能限制请求并发数,
但是pipelimit
的功能相对较丰富。
pipelimit
可设置算法,限制的pipe
,可自定义返回的错误码和错误原因。
pike
只能通过ip来限制,也没有算法选择,限制之后的返回需要用户自己设置。