Kamailio 有状态代理与无状态代理

人们通常会讨论 SIP 代理是 “有状态 “还是 “无状态 “运行,但这些概念之间的区别可能很难把握。RFC3261 中当然有详细说明,但那是一本相当枯燥和详细的读物。人们还经常注意到,这里指的是事务状态,而不是对话状态,但这同样不是一个容易理解的定义。这一点的目的是提供一些澄清和区别实例,说明无状态事务处理和有状态事务处理之间的区别。

事务与对话框

最简单的理解方式是,事务是一个 SIP 请求及其响应,而对话则由一系列请求和响应组成。一个过于简化的例子是,建立呼叫的一组消息是一个事务,结束呼叫的一组消息是一个单独的事务。

Kamailio中的无状态事务处理

在无状态事务处理中,Kamailio只对单个SIP消息采取行动。它不会试图在SIP请求和匹配的响应之间建立关联。在这种模式下,Kamailio的效率极高,但正如我们最终将看到的那样,它也有相当大的局限性。举例说明,我们的环境由 UAC(发送 INVITE)、Kamailio 代理(无状态转发 INVITE)和 UAS(不工作,不会响应,只是一个在 TTY 模式下运行 shell 的容器)组成。

这里的 kamailio 配置如下:

#!KAMAILIO

loadmodule "pv"
loadmodule "xlog"
loadmodule "sl"

modparam("xlog", "prefix", "[$ci $rm-$cs] ")
modparam("xlog", "prefix_mode", 1)
modparam("sl", "bind_tm", 0)

disable_sctp = yes
force_rport  = yes

request_route {
    xinfo("$rm Call-ID: $ci RURI: $ru\n");

    $rd = "uas";

    forward();
}

很简单吧?更新 RURI 域并转发。那么,流量是什么样的呢?调用梯形图是这样的:

  UAC             PROXY             UAS
───┬───          ───┬───          ───┬───
   │     INVITE     │                │
   │ ─────────────> │                │
   │                │     INVITE     │
   │                │ ─────────────> │
   │     INVITE     │                │
   │ ─────────────> │                │
   │                │     INVITE     │
   │                │ ─────────────> │
   │     INVITE     │                │
   │ ─────────────> │                │
   │                │     INVITE     │
   │                │ ─────────────> │
   │     INVITE     │                │
   │ ─────────────> │                │
   │                │     INVITE     │
   │                │ ─────────────> │
   │     INVITE     │                │
   │ ─────────────> │                │
   │                │     INVITE     │
   │                │ ─────────────> │  

UAC 向代理发送 INVITE,代理将其转发。由于 UAS 处于非活动状态,所以没有响应,代理对此并不关心。代理转发 INVTE 是它的职责所在,INVITE 的重新传输由 UAC 负责。

Kamailio 中的有状态消息

那么,如果我们在 Kamailio 中进行有状态操作,会发生什么情况呢?配置文件如下。除了加载 TM 模块和使用 t_relay()而非 forward()外,它与无状态配置几乎完全相同。请注意,现实世界中的配置需要做的远不止这些。但这给我们提供了一个清晰的例子,说明在 UAS 没有响应的简单场景中,行为的不同之处。

完整配置可在此处找到,但这是 Kamailio 配置文件:

#!KAMAILIO

loadmodule "pv"
loadmodule "xlog"
loadmodule "tm"
loadmodule "sl"

modparam("xlog", "prefix", "[$ci $rm-$cs] ")
modparam("xlog", "prefix_mode", 1)
modparam("sl", "bind_tm", 0)

disable_sctp = yes
force_rport  = yes

request_route {
    xinfo("$rm Call-ID: $ci RURI: $ru\n");

    $rd = "uas";

    t_relay();
}

由此产生的调用阶梯是如何工作的?

  UAC             PROXY             UAS
───┬───          ───┬───          ───┬───
   │     INVITE     │                │
   │ ─────────────> │                │
   │   100 Trying   │                │
   │ <───────────── │                │
   │                │     INVITE     │
   │                │ ─────────────> │
   │                │     INVITE     │
   │                │ ─────────────> │
   │                │     INVITE     │
   │                │ ─────────────> │
   │                │     INVITE     │
   │                │ ─────────────> │
   │                │     INVITE     │
   │                │ ─────────────> │ 
   │                │     INVITE     │
   │                │ ─────────────> │
   │                │     INVITE     │
   │                │ ─────────────> │
   │                │     INVITE     │
   │                │ ─────────────> │
   │                │     INVITE     │
   │                │ ─────────────> │
   │                │     INVITE     │
   │                │ ─────────────> │
   │   408 Timeout  │                │
   │ <───────────── │                │
   │       ACK      │                │
   │ ─────────────> │                │ 

这明显不同。在这里,代理不仅仅充当消息的“哑管道”,它更是一个积极的参与者。100 Trying是由TM模块免费提供的(不过可以通过模块参数禁用它)。这告诉UAC请求已经收到,因此UAC停止重传。当没有收到响应时,代理负责重传 INVITE。代理确定超时并向 UAC 发送408 Request Timeout

从表面上看,这似乎没什么大不了的,但它显示了关键的区别。因为代理知道响应,所以它可以处理响应并决定正确的操作。例如,串行分叉(看起来像这样)就可以实现。

  UAC             PROXY             UAS1             UAS2
───┬───          ───┬───          ───┬───          ───┬───
   │     INVITE     │                │                │
   │ ─────────────> │                │                │
   │   100 Trying   │                │                │
   │ <───────────── │                │                │
   │                │     INVITE     │                │
   │                │ ─────────────> │                │
   │                │    503 Error   │                │
   │                │ <───────────── │                │
   │                │              INVITE             │
   │                │ ──────────────────────────────> │
   │                │            100 Trying           │
   │                │ <────────────────────────────── │
   │                │           180 Alerting          │
   │                │ <────────────────────────────── │
   │  180 Alerting  │                │                │
   │ <───────────── │                │                │
   │                │              200 OK             │
   │                │ <────────────────────────────── │
   │     200 OK     │                │                │
   │ <───────────── │                │                │

只有当代理知道事务状态时,才能实现上述逻辑。

总结

虽然在Kamailio中采用无状态配置以实现最高性能似乎很诱人,但Kamailio中大多数有意义的路由决策都会涉及事务路由。

内容编译自:https://kaufmania.wordpress.com/2023/10/07/kamailio-stateful-vs-stateless-mode/

本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/35078.html

(0)

相关推荐

发表回复

登录后才能评论