Featured image of post kamailio dispatcher介绍

kamailio dispatcher介绍

背景介绍

kamailio 的dispatcher模块提供负载均衡功能,可以采用轮询,负载上的权重,通话负载分发和哈希方式。 该模块比较轻量,适合高并发场景。但是不能分发REGISTER请求。

它和opensipsdispatcher不一样,opensips的dispatcher可以分发REGISTER请求。 它更像是opensips的load_balancer

这个模块既可以使用数据库,也可以使用文件来配置。官方文档地址:dispatcher

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
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# 设置dispatcher配置加载的文件
modparam("dispatcher", "list_file", "/run/kamailio/dispatcher.list")
# 设置从数据库中加载dispather配置,默认NULL,不从数据库加载
modparam("dispatcher", "db_url", "mysql://user:passwd@localhost/kamailio")
# 确定分发的方式,是位掩码,默认0,使用username,hostname,port来计算URI哈希;1,使用username计算URI哈希;2,分发失败后,可以尝试下一个地址。 
modparam("dispatcher", "flags", 3)
# 目的地址集的最后一个地址作为发送地址,默认0
modparam("dispatcher", "use_default", 1)
#存储分发地址的AVP变量名,默认: _dsdst_
modparam("dispatcher", "xavp_dst", "_dsdst_")
# 控制哪些参数存XAVP_DST里,
modparam("dispatcher", "xavp_dst_mode", 1)

# ping方式,默认: OPTIONS
modparam("dispatcher", "ds_ping_method", "OPTIONS")
# ping的from地址
modparam("dispatcher", "ds_ping_from", "sip:proxy@sip.somehost.com")
# ping的时间间隔,默认0, 不能ping
modparam("dispatcher", "ds_ping_interval", 30)
# ping使用的地址,必须提前监听
modparam("dispatcher", "ds_default_socket", "udp:192.168.0.125:5060")
# ping失败多少次数后,把gateway标记为不可用,默认1
modparam("dispatcher", "ds_probing_threshold", 10)
# gateway从不活跃状态恢复到活跃态,需要ping多少次,默认1
modparam("dispatcher", "ds_inactive_threshold", 10)
# 定义合法的ping响应码,默认:"",只接受200OK
modparam("dispatcher", "ds_ping_reply_codes", "class=2;code=403;code=488;class=3")
# 0, 只有PROBING状态的gateway会ping;1, 所有gateway都会ping;
# 2,只有INACTIVE状态并且处于PROBING的gateway会ping;3,持续探测任何状态为PROBING的gateway
modparam("dispatcher", "ds_probing_mode", 1)
# 设置定时器进程的运行模式,0, 使用主定时器;1,使用次定时器。
modparam("dispatcher", "ds_timer_mode", 1)
# 当gateway配置错误时,1,报错;2,跳过错误的gateway,继续下一个
modparam("dispatcher", "ds_load_mode", 1)
# 当重新加载dispatcher配置时,延迟多少秒,默认5s
modparam("dispatcher", "reload_delta", 1)

重要函数

ds_select_dst(set, alg[, limit])

从地址集中选择一个地址,然后把地址设置到$du,

  • set 表dispatcher的setid
  • alg 负载均衡算法,可多个叠加:
    • “0”: 哈希callid
    • “1”: 哈希from URI
    • “2”: 哈希to URI
    • “3”: 哈希request-URI user
    • “4”: 轮询
    • “5”: 哈希authorization-username,如果没有此字段,使用轮询
    • “6”: 随机
    • “7”: 哈希PVs,必须设置hash_pvar
    • “8”: 使用dispatcher表里按照priority排序的gateway
    • “9”: 权重分发,使用dispatcher表里按照weight排序的gateway
    • “10”: 使用通话负载分发,必须设置ds_hash_sizedispatcher表的attrs添加duid,速度很快
    • “11”: 使用相对权重分发, dispatcher表的attrs添加rweight, 相对于权重分发,如果有gateway不活跃了,每次会重新计算权重
    • “12”: 分发所有节点
    • “13”: 延迟分发ds_ping_latency_stats, 使用的是轮询分发

ds_next_dst()

获取下一个地址,并设置$du

ds_set_dst()

把当前的地址设置为$du

ds_select_domain(set, alg[, limit])

从地址集中选择一个地址,然后重写R-URI里的hostPort,其他参数和ds_select_dst一致。

ds_next_domain()

获取下一个地址,并设置R-URI

ds_set_domain()

把当前的地址设置为R-URI

ds_select(set, alg[, limit])

从地址集中选择一个地址,存在XAVP变量里,不会重写R-URI$du

ds_select_routes(rules, mode [, limit])

按照规则从地址集中选择一个地址

  • rules 匹配规则,格式为grp1=alg1;grp2=alg2
  • mode 选择的地址推到哪里
    • ‘0’,’d’,‘D’: 修改$du
    • ‘1’,‘r’,‘R’: 修改R-URI
    • ‘2’,‘x’,‘X’: 存储在XAVP变量

ds_mark_dst([state])

标记上次使用的地址状态

  • state
    • “a”,“A”: 活跃状态
    • “i”,“I”: 非活跃状态
    • “t”,“T”: 尝试状态
    • “p”,“P”: 可探测状态

实战

本次测试的场景是:

  1. 软电话1008注册到freeswitch A(172.18.11.187)上, A配置网关转发请求到kamailio (172.16.4.111)
  2. 1008拨打1003, kamailio(172.16.4.111)收到请求, dispatcher分发给freeswitch B(172.16.4.113)或 freeswitch C(172.16.4.114)
  3. 两个软电话分别向freeswitch B和freeswitch C注册1003
  4. B或者C收到请求,拨打软电话,电话接通

从文件读取

  1. dispatcher.list
1
2
3
# setid(int) destination(sip uri) flags(int,opt) priority(int,opt) attributes(str,opt)
1 sip:172.16.4.114:5060 0 5 class=4;socket=udp:172.16.4.111:5460;pipe=p10
1 sip:172.16.4.113:5060 0 5 class=4;socket=udp:172.16.4.111:5460;pipe=p10

这里要介绍一下attributes参数:

  • class=4 接受错误码为400~499的响应
  • strip=3 从被叫号码(R-URI)中去除前3位
  • prefix=86 在被叫号码(R-URI)前加上86
  • duid=abc 唯一标识目标节点
  • maxload=30 最大负载值,超过则拒绝
  • weight=5 1~100,权重,总和为100,权重越大,请求越多
  • rweight=5 1~100,相对权重,总和为100
  • socket=udp:127.0.0.1:5060 定义分发请求的socket,覆盖ds_default_socket
  • socketname=xxx 定义分发请求的socket name
  • ping_from 定义ping的From URI,覆盖ds_ping_from
  • obproxy
  • latency
  1. 配置示例:
 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
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
listen=udp:172.16.4.111:5460
listen=udp:172.16.4.111:5461
listen=udp:172.16.4.111:5464

loadmodule "dispatcher.so"
modparam("dispatcher", "list_file", "/usr/local/etc/kamailio/dispatcher.list")
modparam("dispatcher", "xavp_dst", "_dsdst_")
modparam("dispatcher", "xavp_ctx", "_dsctx_")
modparam("dispatcher", "flags", 2)
modparam("dispatcher", "ds_ping_method", "OPTIONS")
modparam("dispatcher", "ds_ping_from", "sip:proxy@172.16.4.111")
modparam("dispatcher", "ds_ping_interval", 30)
modparam("dispatcher", "ds_default_socket", "udp:172.16.4.111:5461")
modparam("dispatcher", "ds_timer_mode", 1)
modparam("dispatcher", "ds_probing_threshold", 2)
modparam("dispatcher", "ds_inactive_threshold", 3)
modparam("dispatcher", "ds_probing_mode", 1)
modparam("dispatcher", "ds_hash_size", 9)
...
request_route {
    ...
    route(REGISTRAR);

    if ($rU==$null) {
            # request with no Username in RURI
            sl_send_reply("484", "Address Incomplete");
            exit;
    }
    route(DISPATCH);

    # dispatch destinations to PSTN
    #route(PSTN);

    # user location service
    #route(LOCATION);

    return;

}

route[DISPATCH] {
        # round robin dispatching on gateways group '1'
        if(!ds_select_dst("1", "4")) {
                send_reply("404", "No destination");
                exit;
        }
        xlog("L_INFO", "----- going to $ru via $du (attrs: $xavp(_dsdst_=>attrs))\n");
        t_on_failure("RTF_DISPATCH");
        route(RELAY);
        exit;
}


failure_route[RTF_DISPATCH] {
        if (t_is_canceled()) {
                exit;
        }
        # next DST - only for 500 or local timeout
        if (t_check_status("500")
                        or (t_branch_timeout() and !t_branch_replied())) {
                if(ds_next_dst()) {
                        xlog("L_INFO","--- SCRIPT: retrying to <$ru> via <$du> (attrs: $xavp(_dsdst_=>attrs))\n");
                        t_on_failure("RTF_DISPATCH");
                        route(RELAY);
                        exit;
                }
        }
}

event_route[dispatcher:dst-down] {
    xlog("L_ERR", "-----------------Destination down: $rm $ru ($du)\n");
}

event_route[dispatcher:dst-up] {
    xlog("L_ERR", "-----------------Destination up: $rm $ru\n");
}

测试效果

ping

kamailio 分别往两个节点发送OPTIONS请求

  1. 172.16.4.113机器: 113
  2. 172.16.4.114机器: 114

正常拨打电话

113 114 可以看到ds_select_dst配置的是轮询,所以不管第一通172.16.4.113是否接通,第二通都会转到172.16.4.114节点

注意: 一定不要配置modparam("dispatcher", "use_default", 1), 否则不管配置的策略是什么,都只会往一个节点发送请求。

当其中一个节点停掉

停掉172.16.4.113节点的freeswitch,在dispatcher未标记此节点为非活跃状态之前,请求还是会发往此节点的。 113

dispatcher标记172.16.4.113状态为非活跃状态之后,所有的请求只会转到172.16.4.114节点。 113

存在的问题

dispatcher模块目前没有找到配置可以让服务一启动就开始ping节点。 这就会导致服务启动后,在ping的定时器开始之前,如果有请求过来,不会分发请求。

当然可以缩短ds_ping_interval间隔,但是这样的话,ping的频率会变高。

本博客已稳定运行
发表了26篇文章 · 总计45.09k字
本站总访问量 次 · 您是本站第 位访问者
粤ICP备2025368587号-1| 使用 Hugo 构建
主题 StackJimmy 设计