When stateful, a proxy is purely a SIP transaction processing engine.
Stateful 模式下,opensips会保持所有transaction 事务状态的消息内容,所有消息会存储在内存中。因此,当然,有状态模式会消耗大量的系统资源。
RFC3261-16.2定义了关于Stateless Proxy的细节:读者可以参考:SIP协议规范RFC3261中文分享-12
1state是什么意思?
根据前面的介绍,我们知道,其实这个state是针对transaction 来定义的。那么,transaction又是什么意思呢?关于transaction 建议读者参考:
Kamailio/OpenSIPS学习笔记-SIP相关基础
SIP讲座系列-各种SIP服务器介绍
实际上,我们在前面的文章中已经使用了opensips的transaction 模块,包括
Kamailio/OpenSIPS学习笔记-如何测试SIP 408响应超时,调用了$T_fr_timeout等变量。opensips中的tm 模块是一个非常重要的模块,支持的配置功能也非常灵活,相对比较复杂。因为篇幅关系,这里不再做太多介绍。
实话说,stateless 目前生产环境中没有看到太多的应用场景。在某些环境可能被使用,例如,作为一个SBC,SIP发起方可能对SIP服务器端不断发送option消息验证其是否是存活状态,OpenSIPS可以设置为一个stateless状态,检测到是option以后,然后丢弃。很多时候,如果用户ping 运营商的SBC的话,为了防止系统过载,SBC可能没有返回任何消息。
Stateful 模式是SIP UAS或者proxy主要的应用场景。它可以支持SIP呼叫场景基本上所有必要的相关应用, 例如,处理重传,失败路由处理,定时器调整,NAT转换处理,CDR/计费,CANCELs和ACKs消息的路由管理等。我们花费一点时间重点介绍在有状态模式下的处理流程。
2OpenSIPS中stateless和stateful的两种状态
首先读者一定要明确,默认环境下,opensips是以stateless 状态启动的。在stateless模式下,opensips通过core 模块提供SIP的信令功能,具体的函数包括forward()和sl_send_reply()。stateful模式的函数功能由transaction module提供,包括t_relay()和t_reply()。绝大部分的应用环境中,opensips是在有状态的模式下工作的,但是,默认opensips启动时又是一个无状态的模式,如果opensips需要从stateless切换到stateful 状态的话,opensips需要通过调用事务模块的函数从无状态模式切换到有状态模式。接下来,笔者介绍一下从无状态模式切换到有状态模式的两种方式。
3启动stateful状态的两种方式
启动opensips的有状态模式可以通过以下两种方式。一种是手动通过明确的外部调用函数-t_newtrans()的方式来启动opensips的有状态模式; 另外一种是自动启动的方式,通过t_relay()和t_reply()函数来自动启动有状态模式。
这里,读者需要注意,前一种方式是对请求创建了一个事务状态,但是它执行的是无SIP信令处理;后一种方式是如果没有创建有状态模式事务的话,它们会自动创建一个事务,并且执行SIP 信令操作。
4retransmissions测试示例
retransmissions是SIP网络环境中经常遇到的一个问题。如果opensips要处理retransmissions的话,opensips需要在有状态模式中进行处理。retransmissions支持两种retransmissions,一种是incoming 请求的检测和重新传输; 入局的请求通过检测可以再进行处理,否则的话,重传流程可能进入到其他的cfg脚本中,或者查询数据库等流程,这样就会耗费更多的系统资源。因此,opensips进行重传检测是非常必要的,如果发现是一个重传递请求,则系统可能直接回复相关的响应即可。另外一种retransmissions基本上是针对被呼叫方来进行的,opensips获得被呼叫方的回复以后再进行其他的业务流程处理。
为了演示如何使用t_new_tran(),这里,我们提供重传的示例演示,呼叫入局时,如果检测到一个不存在的地址以后,如何进行重传处理。具体的配置步骤如下:
首先,需要在cfg文件中加载-loadmodule "cfgutils.so" 模块。
添加cfg脚本处理流程:
## requests for my domain
if (is_method("PUBLISH|SUBSCRIBE")) {
send_reply(503, "Service Unavailable");
exit;
}
if ($rU==NULL) {
# request with no Username in RURI
send_reply(484,"Address Incomplete");
exit;
}
#发送呼叫1002到地址 地址1.1.1.1
if ($rU=="1002") {
#设置request uri是 1.1.1.1
$rd="1.1.1.1";
t_relay();
exit;
}
// 创建一个新的事务,切换到有状态模式。
t_newtran();
#进行呼叫,休眠2秒钟。
if(is_method("INVITE")) {
sleep(2);
}
以上脚本中,我们伪造了一个不存在的地址是1.1.1.1。使用一个SIP 终端呼叫另外一个终端1002的话,这里的目的地地址是一个不存在的地址,用户可能感觉到呼叫流程相对比较慢,因为在呼叫中添加了一个休眠时间。
5总结
在本文章中,笔者讨论了关于opensips中关于UAS的两种状态的具体使用方式和函数调用,以及价格应该注意到示例语法。首先,笔者介绍了stateless和stateful的基本区别,以及各自的应用场景,包括函数调用方式。接下来,笔者主要重点介绍了在无状态启动的情况下,opensips如何切换为有状态模式环境,以及两种切换方式和各自的不同。最后,笔者通过retransmissions的应用场景介绍了如何使用 t_newtran()实现opensips从无状态模式切换到有状态模式的示例,通过一个示例说明opensips如何在有状态环境中对入局呼叫进行处理。
参考资料:
https://opensips.org/html/docs/modules/1.8.x/tm.html
www.freesbc.cn
www.asterisk.org.cn
