基于Kamailio和RTPProxy实现FreeSWITCH SIP和RTP数据包负载均衡

基于Kamailio和RTPProxy实现FreeSWITCH SIP和RTP数据包负载均衡

 一、环境

  • debian bullseye
  • Kamailio 5.7.2
  • RTPProxy 3.0-rc.1.2dbc63b9
  • FreeSWITCH· 1.10.10

二、安装Kamailio和RTPProxy

直接安装Kamailio:

sudo apt-get update
sudo apt-get install kamailio kamailio-mysql-modules

源码安装RTPProxy:

$ git clone -b master https://github.com/sippy/rtpproxy.git
$ git -C rtpproxy submodule update --init --recursive
$ cd rtpproxy
$ ./configure
$ make && make install

启动RTPProxy服务

#服务启动
# -l 对外的地址
# -s 后面的参数就是kamailio配置的rtpproxy_socket
rtpproxy -l 192.168.1.2 -s udp:192.168.1.2:7722

 RTPProxy 常用参数解析:

./rtpproxy -A 110.172.192.68 -l 0.0.0.0 -s udp:localhost:7890 -n tcp:localhost:7890 -F -d DBUG -m 50002 -M 50020 -d

参数解释如下:
-A 本机外网地址
-l 本机内网侦听地址
-s 本机侦听kamailio通知端口
-n 超时通知接收端口
-F 不检查是否为超级用户模式
-d 调试消息输出级别
-m rtp最小端口
-M rtp最大端口
-d
log_level为日志级别,值如下
DBUG INFO WARN ERR CRIT
log_facility为日志输出到哪,利用的系统日志

 三、Kamailio中的RTPProxy模块

 3.1 描述

  • rtpproxy模块可以支持多个rtpproxy,用于平衡/分发、控制/选择目的。
  • 允许定义几组rtpproxy。负载平衡将在一个集上执行,管理员可以选择应该使用的集合。集合是通过它的id来选择的——id是由集合定义的。
  • rtpproxy模块根据集合中每个rtpproxy的权重自动完成集合内的平衡。
  • 集合的选择是在使用unforce_rtp_proxy(), rtpproxy_offer()或rtpproxy_answer()函数之前从脚本中完成。
  • 特别说明:如果有多个集合,则在调用rtpproxy_offer()/rtpproxy_answer()和unforce_rtpproxy()等方法时要使用相同的集合。

 3.2 模块参数

 
参数名 类型 说明和示例
rtpproxy_sock string

可以是单个socket

udp:localhost:12221

也可以是一组sockert,默认集合是0

udp:localhost:12221 udp:localhost:12222

也可以指定集合id,应 “id==”指定

1 == udp:localhost:12221 udp:localhost:12222

rtpproxy_disable_tout integer

默认60

当RTPProxy 无法访问并标记为禁用

rtpproxy_tout integer

默认1

等待rtpproxy回复的超时时间

rtpproxy_retr integer

默认5

重试次数

nortpproxy_str string

默认值为“a=nortpproxy:yes\r\n”

设置到sdp属性里,标记已经被更改

timeout_socket string

默认控

发给rtpproxy,rtpproxy用来做重传

ice_candidate_priority_avp string  
extra_id_pv string  
db_url string

数据库url(当设置了此参数,rtpproxy_sock参数将失效)

用来加载rtpproxy数据

table_name string 表名
rtp_inst_pvar string 设置伪变量,如果设置了该参数,选中的rtpproxy的URL将存储在给定的变量中

 3.3 模块方法

方法名 参数 说明
set_rtp_proxy_set setid 设置要使用的rtpproxy集合id
unforce_rtp_proxy [flag] 断开
rtpproxy_destroy   同 unforce_rtp_proxy
start_recording   发送标记到RTPProxy,用来记录RTP Stream
rtpproxy_offer [flag [,ipaddr]]

重写SDP,让rtp流通过rtpproxy转发

在Invite时触发

rtpproxy_answer 参数同rtpproxy_offer

重写SDP,让rtp流通过rtpproxy转发

在200OK响应

rtpproxy_manage 参数同rtpproxy_offer 会自动选择需要执行的方法rtpproxy_offer、rtpproxy_answer、unforce_rtpproxy

 

3.4 流程逻辑

  1. kamailio收到invite请求
    • 调用rtpproxy服务,rtpproxy创建会话,返回接收地址和端口
    • 将rtpproxy返回的地址和端口修改到sdp
    • 将修改后的invite请求发送到设备
  2. kamailio收到200 OK响应
    • 调用rtpproxy服务,将200 OK携带的发送地址和端口通知过去,返回rtpproxy转发出来的地址和端口
    • 将返回的地址和端口修改到200 OK的sdp
    • 将修改后的200 OK返回到invite方

 3.5 关键方法说明

主要用到的几个方法是rtpproxy_manage、rtpproxy_answer、rtpproxy_offer,它们使用方式基本一致,且rtpproxy_manage会根据sip消息自动决定使用rtpproxy_answer还是rtpproxy_offer。所以主要以rtpproxy_manage为例说明。

rtpproxy_manage([flag [, ipaddr]])
带有2个可选参数
a. flag:只指定flag
b. flag,ipaddr:指定flag和ip地址(指定ip地址后,修改sdp时会强制用指定的ip)

flag参数解释

  • 1:在向rtpproxy发送命令时,将第一个Via分支附加到Call-ID
  • 2:在向rtpproxy发送命令时,将第二个Via分支附加到Call-ID
  • 3:如果是request消息,在向rtpproxy发送命令时,将第一个Via分支附加到Call-ID;如果是reply消息,在向rtpproxy发送命令时,将第二个Via分支附加到Call-ID
  • a:表示对端不自持对称RTP,会自动设置r参数
  • b:在向rtpproxy发送命令时,将extra_id_pv变量的值附加到Call-ID,必须设置extra_id_pv变量,注意:b不能与1、2、3同时使用
  • l:强制“查找”,只有当相应的会话在rtpproxy中已经存在时才重写SDP
  • i/e:标志指定SIP消息的方向。i”表示内部网络(LAN),“e”表示外部网络(WAN)。使用时必须指定两个标志来定义传入网络和传出网络,且请求和响应必须一致。比如请求使用“ie”,那么响应必须也是“ie”,可以有以下组合:ie、ei、ii、ee
  • x:该标志是使用RTPProxy的“ie”或“ei”标志的快捷方式,以便在“内部网络”的IPv4和“外部 网络”的IPv6之间自动桥接。区别是由SDP中给定的IP完成的,例如,IPv4地址将始终调用“ie”到RTPProxy (IPv4(i)到IPv6(e)),而IPv6Address将始终调用“ei”到RTPProxy (IPv6(e)到IPv4(i))
  • f:告诉rtpproxy忽略正在传输中的另一个rtpproxy插入的标记,以指示会话已经通过另一个代理
  • r:标记sdp内的地址有效,如果没有这个参数,则默认使用sip消息的源地址作为媒体地址
  • c:修改session的c字段的ip
  • w:接收消息的UA,必须强制支持对称RTP
  • z:修改payload大小

3.6 示例

 

#加载rtpproxy模块
loadmodule "rtpproxy.so"

#设置rtpproxy_sock,也可以通过加载rtpproxy数据库来设置
modparam("rtpproxy", "rtpproxy_sock", "udp:192.168.1.2:7722")

request_route {

    if(is_method("REGISTER"))
    {
        ...
    }
    else
    {
        ...
        
        #收到非register请求时,尝试判断触发RTPPROXY
        if (is_method("INVITE|UPDATE")) {
            route(RTPROXY);
        }
    }

    route(RELAY);

    return;
}

route[RELAY] {

    if (is_method("INVITE|UPDATE")) {
        #响应
        if(!t_is_set("onreply_route")) {
            t_on_reply("MANAGE_REPLY");
        }
    }
    if (is_method("INVITE")) {
        #失败
        if(!t_is_set("failure_route")){
            t_on_failure("MANAGE_FAILURE");
        } 
    }

    if (!t_relay()) {
        sl_reply_error();
    }
    exit;
}

route[RTPROXY] {
    
    #触发rtpproxy,模块内部会根据sip消息内容自动选择rtpproxy_offer、rtpproxy_answer或者执行
    # 参数 “r”,会使用sdp里的地址
    rtpproxy_manage("r");
    return;
}

# Manage incoming replies in transaction context
onreply_route[MANAGE_REPLY] {

    #成功触发
    if(status=~"[12][0-9][0-9]") {
        route(RTPROXY);
    }
    return;
}

# Manage failure routing cases
failure_route[MANAGE_FAILURE] {
    #失败触发
    route(RTPROXY);
    return;
}

 

posted @ 2023-11-03 14:19  钟齐峰  阅读(1519)  评论(1编辑  收藏  举报