openSIPS 4.0 核心配置参数详解
面向已按官方仓库安装好 openSIPS 4.0 的运维工程师,逐段解析默认 residential 配置模板的三大块——全局参数、模块加载与模块参数、路由逻辑,并把关键参数的语义与运行时行为一一对应验证:日志级别与进程实测、udp_workers 与进程树、内存参数与 MI 统计、注册与 usrloc 绑定实测、Max-Forwards/404/484/408 等路由行为实测,另附 opensips-cli 运行时观察与配置校验排障实录,全部命令与回显均为实测。
openSIPS 4.0 核心配置参数详解
目标:面向已按官方仓库安装好 openSIPS 4.0 的运维工程师,逐段解析默认 residential 配置模板的三大块——全局参数、模块加载与模块参数、路由逻辑,并把关键参数的语义与运行时行为一一对应验证:日志级别与进程实测、udp_workers 与进程树、内存参数与 MI 统计、注册与 usrloc 绑定实测、Max-Forwards/404/484/408 等路由行为实测,另附 opensips-cli 运行时观察与配置校验排障实录,全部命令与回显均为实测。
一、环境信息
本章列出本次解析与验证所用的环境,后续章节的全部命令与回显均基于该环境。
| 项目 | 取值 |
|---|---|
| 服务器 IP | 10.89.62.3(内网地址) |
| 操作系统 | Ubuntu 22.04.5 LTS |
| openSIPS 版本 | 4.0.2-1(官方 APT 仓库安装,服务由 systemd 托管) |
| 配置文件 | /etc/opensips/opensips.cfg(262 行,Debian 包默认 residential 模板) |
| MI 客户端 | opensips-cli 0.4.1(见第七章安装) |
| SIP 测试客户端 | 10.89.62.4(sipsak 0.9.8.1) |
| 服务监听 | udp:10.89.62.3:5060 |
安装与服务管理、监听地址的修改过程见《Ubuntu 22.04 安装 openSIPS 4.0 指南》;同一份模板的容器化部署形态(端口发布、TCP 传输启用)见《openSIPS 4.0 Docker Compose 部署指南》。本篇专注把配置文件里的每一类参数讲透,并用运行时证据印证。
二、配置体系总览
2.1 三段结构
openSIPS 配置文件是"脚本"而非"键值对清单":前两段声明环境与能力,第三段是一段真正会逐请求执行的路由脚本语言。grep 三个段标题即可定位结构:
grep -n '^#######' /etc/opensips/opensips.cfg
14:####### Global Parameters #########
40:####### Modules Section ########
97:####### Routing Logic ########
| 段 | 行区间 | 内容 |
|---|---|---|
| 全局参数 | 14–38 | 进程级行为:日志、worker 数、监听 socket |
| 模块段 | 40–95 | 模块加载(loadmodule)、模块参数(modparam)、传输层 |
| 路由逻辑 | 97–261 | 主 route 与子路由脚本,每个 SIP 请求进来都会执行一遍 |
2.2 配置校验:改配置的固定动作
openSIPS 不提供配置热加载——修改文件后必须重启服务才生效。因此改配置的固定三步是:编辑 → opensips -c 校验 → systemctl restart opensips。校验命令只读磁盘文件、与运行中进程完全无关:
opensips -c
Listening on
udp: 10.89.62.3 [10.89.62.3]:5060
Aliases:
Oct 26 11:09:10 [4443] NOTICE:core:main: config file ok, exiting...
末行 config file ok, exiting... 即校验通过;顺带打印的监听清单就是当前配置的 socket 解析结果。语法错误的定位实录见第八章 8.1 节。
⚠️ 实测确认:在校验失败期间(文件已改坏、尚未恢复),运行中的服务完全不受影响(
systemctl is-active opensips仍为active)——磁盘文件与内存中的路由脚本互不影响,直到你重启。这既是保护也是陷阱:改坏了文件不校验就直接重启,服务会起不来。
三、全局参数详解(14–38 行)
14 ####### Global Parameters #########
16 /* uncomment the following lines to enable debugging */
17 #debug_mode=yes
19 log_level=3
20 xlog_level=3
21 stderror_enabled=no
22 syslog_enabled=yes
23 syslog_facility=LOG_LOCAL0
25 udp_workers=4
29 #disable_dns_blacklist=no
33 #dns_try_ipv6=yes
36 socket=udp:10.89.62.3:5060 # CUSTOMIZE ME
3.1 debug_mode(默认 no,注释状态)
调试总开关:置 yes 后强制把日志级别拉到最高、日志改投 stderr 并附带源码行号,还会关闭 daemon 化。只用于排障现场,生产严禁开启(性能损失大)。
3.2 log_level / xlog_level(实测验证)
log_level=3:openSIPS 自身日志的级别(1=alert 到 4=debug,3 为 INFO 级);xlog_level=3:脚本中xlog()语句的输出上限,脚本打了xlog("L_DBG",...)但 xlog_level 只有 3 时不会刷出来。
运行时逐进程核验(MI 命令,第七章有完整工具链):
opensips-cli -x mi core:log_level
{
"Processes": [
{
"PID": 4033,
"Log level": 3,
"Type": "attendant"
},
{
"PID": 4034,
"Log level": 3,
"Type": "MI FIFO"
},
{
"PID": 4035,
"Log level": 3,
"Type": "time_keeper"
},
{
"PID": 4036,
"Log level": 3,
"Type": "timer"
},
{
"PID": 4037,
"Log level": 3,
"Type": "SIP receiver udp:10.89.62.3:5060"
},
...(4 个 SIP receiver 与 Timer handler,Log level 同为 3)
]
}
(输出节选,实测)所有进程 Log level 均为 3,与配置一致。该 MI 命令带参数还能在线调级(如 core:log_level 4),排障时临时开 debug 不用重启。
3.3 stderror_enabled / syslog_enabled / syslog_facility(日志去向)
Debian 包模板的取值组合是 stderror_enabled=no + syslog_enabled=yes + syslog_facility=LOG_LOCAL0——日志走 syslog 协议投递,facility 标记为 local0。运行时日志链路实测:
ls -l /dev/log && journalctl -u opensips -n 3 --no-pager
lrwxrwxrwx 1 root root 28 Oct 26 09:52 /dev/log -> /run/systemd/journal/dev-log
Oct 26 11:06:23 b1-server /usr/sbin/opensips[4034]: ERROR:core:get_mi_int_param: Parameter: brief is not an valid integer
Oct 26 11:06:36 b1-server /usr/sbin/opensips[4034]: ERROR:core:handle_mi_request: Invalid parameters
Oct 26 11:07:23 b1-server /usr/sbin/opensips[4041]: NOTICE:ACC: call missed: timestamp=1793012843;created=0;setuptime=1793012843;method=OPTIONS;from_tag=68b2441a;to_tag=;call_id=1756513306@10.89.62.4;code=408;reason=Request Timeout
三点解读:
- openSIPS 把 syslog 消息写到
/dev/log,该 socket 由 systemd-journald 提供,所以日志最终进入 journald,journalctl -u opensips可查(第三条NOTICE:ACC: call missed是 acc 模块对一次失败呼叫的记账实录,第五章 5.3 节正好触发过它); - 与 systemd 直采 stdout/stderr 的服务不同,openSIPS 在此模板下不走 journald 的 stdout 通道,
systemctl status里看不到日志行,必须用 journalctl 过滤; - 若接 ELK 等集中日志,rsyslog 侧按 facility=local0 配一条转发规则即可(
local0.* /var/log/opensips.log)。
💡 模板差异提示:openSIPS 官方仓库(源码/官方镜像)同版模板此处是
stderror_enabled=yes+syslog_enabled=no(日志直投 stderr),与 Debian 包取值相反。两处取值没有对错,但跨环境迁移配置文件时要注意日志会"消失"或"重复"。
3.4 udp_workers(实测验证)
udp_workers=4:每个 UDP 监听 socket 的接收 worker 数。这是 4.0 的新式写法(3.x 用 children 参数)。实测进程树:
opensips-cli -x mi core:ps
{
"Processes": [
{ "ID": 0, "PID": 4033, "Type": "attendant" },
{ "ID": 1, "PID": 4034, "Type": "MI FIFO" },
{ "ID": 2, "PID": 4035, "Type": "time_keeper" },
{ "ID": 3, "PID": 4036, "Type": "timer" },
{ "ID": 4, "PID": 4037, "Type": "SIP receiver udp:10.89.62.3:5060" },
{ "ID": 5, "PID": 4038, "Type": "SIP receiver udp:10.89.62.3:5060" },
{ "ID": 6, "PID": 4039, "Type": "SIP receiver udp:10.89.62.3:5060" },
{ "ID": 7, "PID": 4040, "Type": "SIP receiver udp:10.89.62.3:5060" },
{ "ID": 8, "PID": 4041, "Type": "Timer handler" }
]
}
对照关系:udp_workers=4 → 恰好 4 个 SIP receiver 进程(ID 4–7),加上 attendant(主控/MI 汇总)、MI FIFO、time_keeper、timer、Timer handler 共 9 个进程,与 ss -lunp 里共享同一 socket fd 的 9 个 opensips 进程完全吻合(见《Ubuntu 22.04 安装 openSIPS 4.0 指南》6.5 节)。多核机器上把 udp_workers 调到核数级别是吞吐调优的第一杠杆。
3.5 socket 与 alias(本机判定)
socket=udp:10.89.62.3:5060 # CUSTOMIZE ME
socket 声明监听的传输协议、IP 与端口。它在脚本里还有第二个身份:决定 is_myself() 的判定基准。路由脚本大量依赖"这个请求是不是发给我的"判断(第五章 5.2 步),判断范围 = 所有 socket 的 IP:port + 所有 alias 声明的地址。裸机安装时客户端打的就是服务 IP,默认即匹配;但一旦服务躲在端口发布或域名的后面,就要把对外地址补进 alias:
alias=10.89.62.1
alias="sip.example.com"
相关实测(403 → 加 alias → 恢复正常)的完整过程见《openSIPS 4.0 Docker Compose 部署指南》5.4 节。
3.6 注释掉的两项 DNS 行为
disable_dns_blacklist(默认 no):对解析失败/连接失败的出站目标做临时自动拉黑,出站密集的场景建议打开,避免反复撞死目标拖慢事务;dns_try_ipv6(默认 no):IPv4 解析失败后是否继续试 IPv6(AAAA)。
四、模块加载与模块参数详解(40–95 行)
4.1 加载机制:mpath / loadmodule / modparam
43 mpath="/usr/lib/x86_64-linux-gnu/opensips/modules/"
45 #### SIGNALING module
46 loadmodule "signaling.so"
52 loadmodule "tm.so"
95 loadmodule "proto_udp.so"
mpath:模块搜索目录,Debian 包固定在/usr/lib/x86_64-linux-gnu/opensips/modules/(该目录下共有 102 个模块);loadmodule "<模块名>.so":加载功能模块。核心路由功能全部由模块提供:stateless 转发(sl)、事务层(tm)、record-route(rr)、注册(registrar + usrloc)、记账(acc)……没有 tm.so 就没有有状态的事务能力;modparam("模块名", "参数名", 值):给模块传参,必须在对应 loadmodule 之后;proto_udp.so单独成行:4.x 起传输协议本身也是模块(proto_udp / proto_tcp / proto_tls…),要监听 TCP 就得再 load proto_tcp.so 并加一行 tcp socket(实操见《openSIPS 4.0 Docker Compose 部署指南》第六章)。
4.2 tm:事务层参数(52–56 行)
53 modparam("tm", "fr_timeout", 5)
54 modparam("tm", "fr_inv_timeout", 30)
55 modparam("tm", "restart_fr_on_each_reply", 0)
56 modparam("tm", "onreply_avp_mode", 1)
| 参数 | 语义 | 实测佐证 |
|---|---|---|
fr_timeout | 非_INVITE 事务的最终响应等待期(秒),超时则本地生成超时响应 | 5.3 节实测:OPTIONS 经 lookup 命中后转发到不可达对端,tm 在该等待期内未收到响应,回给客户端 SIP/2.0 408 Request Timeout |
fr_inv_timeout | INVITE 事务的振铃等待期(秒),决定"最长响多久算超时" | 语义同上,作用于 INVITE |
restart_fr_on_each_reply | 每收到 provisional 响应是否重置定时器;0 表示不重置(180 振铃多次也不延长) | 配置语义 |
onreply_avp_mode | 应答处理时 AVP 的可见范围模式 | 配置语义 |
事务计数器可实时观察:opensips-cli -x mi statistics:get tm: 返回 tm:UAS_transactions、tm:inuse_transactions 等全套计数(实测输出见第七章 7.3 节)。
4.3 rr:record-route(58–61 行)
61 modparam("rr", "append_fromtag", 0)
append_fromtag=0:Record-Route 头不追加 from tag。后续 sequential 请求的方向判定不依赖它(detect_direction 也为 0,见 4.7),拓扑简单的代理场景无碍。
4.4 mi_fifo:管理接口通道(69–72 行)
71 modparam("mi_fifo", "fifo_name", "/run/opensips/opensips_fifo")
72 modparam("mi_fifo", "fifo_mode", 0666)
启用 FIFO 协议的 MI 通道。运行时确认:
ls -la /run/opensips/
total 4
drwxrwxr-x 2 opensips opensips 80 Oct 26 10:30 .
drwxr-xr-x 11 root root 300 Oct 26 10:30 ..
-rw-r--r-- 1 opensips opensips 5 Oct 26 10:30 opensips.pid
prw-rw-rw- 1 opensips opensips 0 Oct 26 10:30 opensips_fifo
opensips_fifo 的 p 开头即 FIFO 管道文件,权限 prw-rw-rw- 与 fifo_mode=0666 一致。所有 MI 命令(第七章)都从这条通道进出。
4.5 usrloc:用户位置表(74–77 行)
76 modparam("usrloc", "nat_bflag", "NAT")
77 modparam("usrloc", "working_mode_preset", "single-instance-no-db")
nat_bflag:把名为NAT的 branch flag 关联为"该联系人位于 NAT 后",供后续 nat 处理逻辑打标使用;working_mode_preset:usrloc 运行模式预设,single-instance-no-db= 单实例 + 不落库(纯内存)。含义有二:绑定表只在本进程组内存里;服务重启后注册全部清零(终端会自动重注册,短期无感,但这是后续上 HA/持久化时要最先改的参数——切到 DB 后端需配套 load db_mysql 等模块)。
实测注册行为见第七章 7.4 节。
4.6 registrar:注册服务器(79–83 行)
81 modparam("registrar", "tcp_persistent_flag", "TCP_PERSISTENT")
83 #modparam("registrar", "max_contacts", 10)
tcp_persistent_flag:TCP 注册用户保持连接不拆(配合 TCP 传输时避免每次请求重新建连);max_contacts(注释状态):每个 AOR 允许的联系人上限。注释即不限——实测里同一用户连续注册两次得到 2 个 Contact(第七章 7.4 节的绑定表可证),改生产时按业务决定是否放开此行。
4.7 acc:记账(85–93 行)
88 modparam("acc", "early_media", 0)
89 modparam("acc", "report_cancels", 0)
93 modparam("acc", "detect_direction", 0)
acc 本身只定义了"记什么事件",真正触发记账的是路由脚本里的 do_accounting() 函数(第五章 5.2 步):early_media 是否记录 183 早媒体、report_cancels 是否记录 CANCEL、detect_direction 是否通过 from tag 判定 sequential 请求方向(为 1 时须配合 4.3 的 append_fromtag=1)。实测产物:一次 lookup 命中但对端不可达的呼叫在 journalctl 里留下了完整的 missed 记录(3.3 节第三条日志,含 call_id/from_tag/code=408)。
五、路由逻辑详解(97–261 行)
5.1 主 route 的九步流水线
每个 SIP 请求从上到下执行 route{}(101–217 行),residential 模板的骨架是一条九步流水线:
101 route{
103 if (!mf_process_maxfwd_header(10)) { send_reply(483,"Too Many Hops"); exit; }
108 if (has_totag()) { ... 环内请求处理 ... exit; }
137 if (is_method("CANCEL")) { if (t_check_trans()) t_relay(); exit; }
144 t_check_trans(); # 吸收重传
146 if ( !(is_method("REGISTER")) ) { # 非注册请求:防开放中继检查
148 if (is_myself("$fd")) { }
153 else if (!is_myself("$rd")) { send_reply(403,"Relay Forbidden"); exit; }
162 if (loose_route()) { ... 预置 Route 拒绝 ... exit; }
171 if (!is_method("REGISTER|MESSAGE")) record_route();
175 if (is_method("INVITE")) { do_accounting("log"); }
181 if (!is_myself("$rd")) { append_hf("P-hint: outbound\r\n"); route(relay); }
189 if (is_method("PUBLISH|SUBSCRIBE")) { send_reply(503, "Service Unavailable"); exit; }
194 if (is_method("REGISTER")) { save("location"); exit; }
202 if ($rU==NULL) { send_reply(484,"Address Incomplete"); exit; }
209 if (!lookup("location","method-filtering")) { t_reply(404, "Not Found"); exit; }
215 do_accounting("log","missed");
216 route(relay);
217 }
分步解读(行号为实际配置行号):
| 步 | 行 | 逻辑 | 实测行为 |
|---|---|---|---|
| 1 | 103–106 | Max-Forwards 计数不足 10 → 拒绝,防环 | OPTIONS 带 Max-Forwards:0 → 483 Too Many Hops(5.1 节实测) |
| 2 | 108–134 | 带 To tag 的环内请求:ACK 直通、loose_route 定路径、BYE 记失败账 | 属对话内逻辑,需完整呼叫链 |
| 3 | 137–141 | CANCEL 只对存在的事务转发 | — |
| 4 | 144 | t_check_trans() 吸收重传 | — |
| 5 | 146–159 | 防开放中继:非 REGISTER 请求,from 域($fd)与 R-URI 域($rd)都不属于本机 → 403 | 端口发布场景实测出 403(见 B2 篇 5.4 节) |
| 6 | 162–168 | 预置 Route 头检查(防 Path 伪造),xlog 报警 | — |
| 7 | 171–178 | 除 REGISTER/MESSAGE 外全部 record_route(保证后续请求过本机);INVITE 开账 | — |
| 8 | 181–200 | R-URI 非本机域 → 出站中继;PUBLISH/SUBSCRIBE 未启用 → 503;REGISTER → save("location") 后结束 | 注册实测(7.4 节) |
| 9 | 202–216 | R-URI 无用户名 → 484;查 usrloc 表,未命中 → 404;命中 → 记 missed 账 → 交 relay 子路由 | 484/404/408 全链实测(5.1–5.3 节) |
5.2 子路由(220–260 行)
220 route[relay] {
222 if (is_method("INVITE")) {
223 t_on_branch("per_branch_ops");
224 t_on_reply("handle_nat");
225 t_on_failure("missed_call");
226 }
228 if (!t_relay()) { send_reply(500,"Internal Error"); }
231 exit;
232 }
route[relay] 是唯一出口:INVITE 请求在这里挂三个事件钩子——每分支路由(per_branch_ops)、应答路由(handle_nat)、失败路由(missed_call),随后 t_relay() 发起事务级转发,失败则 500。三个钩子路由的当前实现都是占位(xlog 或空体),这是模板留给二次开发的挂载点:NAT 修补、3xx 重定向拦截(254–257 行的注释块)等都在这里加。
5.3 伪变量速查
路由脚本里反复出现的 $xx 是 openSIPS 伪变量,本模板用到的:
| 伪变量 | 含义 | 脚本中的用途 |
|---|---|---|
$fd | From 头的域 | 148 行:呼叫方是否本机域 |
$rd | R-URI 的域 | 153/181 行:被叫域是否本机 |
$rU | R-URI 的用户名 | 202 行:无用户名 → 484 |
$fu / $tu | 完整 From / To URI | 164/197 行:日志 |
$ru | 完整 R-URI | 164/238 行:日志与分支 |
$ci | Call-ID | 164 行:日志关联 |
$du | 目标 URI(destination set) | 130 行注释:loose_route 设定的出站目标 |
六、内存参数与 systemd 参数链
openSIPS 的内存分两级:共享内存(shm,全进程组共用,装 usrloc/dialog/事务等全局数据)与私有内存(pkg,每进程独立)。二者不在配置文件里调,而在 systemd 启动参数上:
grep -v '^#' /etc/default/opensips | grep -v '^$'
RUN_OPENSIPS=no
USER=opensips
GROUP=opensips
S_MEMORY=64
P_MEMORY=4
DUMP_CORE=no
OPTIONS=""
systemctl cat opensips
# /lib/systemd/system/opensips.service
[Service]
Type=forking
User=opensips
Group=opensips
RuntimeDirectory=opensips
RuntimeDirectoryMode=775
Environment=P_MEMORY=32 S_MEMORY=32
EnvironmentFile=-/etc/default/opensips
PermissionsStartOnly=yes
PIDFile=%t/opensips/opensips.pid
ExecStart=/usr/sbin/opensips -P %t/opensips/opensips.pid -f /etc/opensips/opensips.cfg -m $S_MEMORY -M $P_MEMORY $OPTIONS
ExecStop=/usr/bin/pkill --pidfile %t/opensips/opensips.pid
Restart=always
TimeoutStopSec=30s
LimitNOFILE=262144
参数链:Environment= 里的默认值 32/32 被 EnvironmentFile=(即 /etc/default/opensips 的 64/4)覆盖,最终以 -m 64 -M 4 传给主进程(本机 S_MEMORY=64/P_MEMORY=4 为安装篇即已调定的值)。运行时用 MI 统计闭环验证:
opensips-cli -x mi statistics:get shmem:
{
"shmem:total_size": 67108864,
"shmem:max_used_size": 3627680,
"shmem:free_size": 63481184,
"shmem:used_size": 3539600,
"shmem:real_used_size": 3627680,
"shmem:fragments": 970
}
shmem:total_size = 67108864 恰为 64 MB——与 /etc/default/opensips 的 S_MEMORY=64 严丝合缝;空载时实占约 3.5 MB。调内存的正确姿势:改 /etc/default/opensips → systemctl daemon-reload(unit 未变时非必需)→ systemctl restart opensips → 用这条 MI 命令核对新值。
单元文件本身另有两点值得知道:User=opensips 说明服务不以 root 运行(/run/opensips 运行时目录由 systemd 建好并授权);LimitNOFILE=262144 拉高了文件描述符上限(TCP/TLS 大并发时的隐形天花板)。
七、运行时观察:MI 接口实战
7.1 安装 opensips-cli
Debian 主包不含任何 MI 客户端(实测只有 osipsconfig 配置生成器)。管理客户端 opensips-cli 就在同一个官方仓库里,直接安装:
apt-cache policy opensips-cli | head -5 && apt-get install -y opensips-cli 2>&1 | tail -2
opensips-cli:
Installed: (none)
Candidate: 0.4.1-1
Version table:
0.4.1-1 500
500 https://apt.opensips.org jammy/4.0-releases amd64 Packages
500 https://apt.opensips.org jammy/4.0-releases all Packages
0.4.0-1 500
Setting up opensips-cli (0.4.1-1) ...
OpenSIPS CLI 0.4.1
7.2 命名空间语法
opensips-cli 的 MI 命令是 mi <模块>:<命令> 的命名空间形式,直接写裸 MI 命令名会报错(实录见第八章 8.2 节)。列出全部可用命令:
opensips-cli -x mi
ERROR: module 'mi' expects the following commands: statistics:get, statistics:list, statistics:reset, statistics:reset_all, core:uptime, core:version, core:pwd, core:arg, core:which, core:ps, core:kill, core:log_level, core:xlog_level, core:profiling_proc, core:log_level_filter, core:log_mute_state, core:reload_routes, core:help, tcp:list, tcp:close, mem:shm_check, mem:pkg_dump, mem:shm_dump, mem:rpm_dump, cache:store, cache:fetch, cache:remove, status_report:get, status_report:status, status_report:reports, status_report:identifiers, evi:subscribe, evi:list, evi:subscribers, evi:raise, blacklists:list, blacklists:check_all, blacklists:check, blacklists:add_rule, blacklists:del_rule, tm:uac_dlg, tm:uac_cancel, tm:hash, tm:reply, usrloc:rm, usrloc:rm_contact, usrloc:dump, usrloc:show_contact, usrloc:add, usrloc:flush, usrloc:sync, usrloc:cluster_sync
这行报错实际是一张"命令清单":core:(核心)、statistics:(统计)、tm:(事务)、usrloc:(位置表)、mem:(内存转储)等分组一目了然。
7.3 核心命令三连
opensips-cli -x mi core:uptime && opensips-cli -x mi statistics:get tm:
{
"Now": "Mon Oct 26 11:04:48 2026",
"Up since": "Mon Oct 26 10:30:17 2026",
"Up time": "2071 [sec]"
}
{
"tm:received_replies": 0,
"tm:relayed_replies": 0,
"tm:local_replies": 0,
"tm:UAS_transactions": 1,
"tm:UAC_transactions": 0,
"tm:2xx_transactions": 0,
"tm:3xx_transactions": 0,
"tm:4xx_transactions": 0,
"tm:5xx_transactions": 0,
"tm:6xx_transactions": 0,
"tm:inuse_transactions": 1,
...
}
(输出节选,实测首部)tm:UAS_transactions=1、tm:inuse_transactions=1 正是当时那笔尚未超时的 OPTIONS 事务——统计与真实业务一一对应。
7.4 注册与位置表实测
客户端(10.89.62.4)注册用户 1001:
sipsak -U -x 300 -s sip:1001@10.89.62.3
命令静默退出(退出码 0)。服务器侧查位置表:
opensips-cli -x mi usrloc:dump 0
{
"Domains": [
{
"name": "location",
"hash_size": 512,
"AORs": [
{
"AOR": "1001@10.89.62.3",
"Contacts": [
{
"Contact": "sip:1001@10.89.62.4:48380",
"ContactID": "2661205167310912667",
"Expires": 240,
"Q": "",
"Callid": "549550974@10.89.62.4",
"Cseq": 1,
"User-agent": "sipsak 0.9.8.1",
"State": "CS_NEW",
"Flags": 0,
"Cflags": "",
"Socket": "udp:10.89.62.3:5060",
"Methods": 4294967295
},
{
"Contact": "sip:1001@10.89.62.4:52884",
"ContactID": "2661205167310912668",
"Expires": 257,
...
}
]
}
]
}
]
}
(输出节选,实测)这份绑定表把 4.5/4.6 节的参数全部可视化:save("location") 生效、AOR 带 full domain(1001@10.89.62.3)、同一用户两次注册得到 2 个 Contact(max_contacts 未限制)、Expires 从 300 逐秒递减、Socket 字段记录了注册进入的 socket。
7.5 打已注册用户:lookup 命中与 408
向已注册用户 1001 发 OPTIONS,走完整链路:防开放中继放行($rd 是本机)→ record_route → lookup 命中 → route(relay) → t_relay() 转发到 Contact 地址(sipsak 进程已退出,不可达)→ tm 在 fr_timeout=5 内未等到响应 → 本地生成超时响应:
sipsak -v -s sip:1001@10.89.62.3
** timeout after 500 ms**
** timeout after 1000 ms**
** timeout after 2000 ms**
SIP/2.0 408 Request Timeout
Via: SIP/2.0/UDP 10.89.62.4:39532;received=10.89.62.4;branch=z9hG4bK.19598754;rport=40893;alias
To: sip:1001@10.89.62.3;tag=4985-885041cfe299fa04c7b65e5dd74fdf6f
From: sip:sipsak@10.89.62.4:39532;tag=68b2441a
Call-ID: 1756513306@10.89.62.4
CSeq: 1 OPTIONS
Server: OpenSIPS (4.0.2 (x86_64/linux))
Content-Length: 0
To: 头带上了服务端生成的 tag(tag=4985-...)——这是 tm 本地合成的响应,不是对端回的。同时 4.7 节的 missed 记账落进了 journalctl(3.3 节日志实录)。同理,lookup 未命中(用户从未注册)时走 209–211 行的 t_reply(404):
sipsak -v -s sip:9999@10.89.62.3
SIP/2.0 404 Not Found
Server: OpenSIPS (4.0.2 (x86_64/linux))
八、排障实录
8.1 配置语法错误:opensips -c 的定位能力
向配置文件追加一行非法语句后校验:
echo "this_is_not_a_valid_statement" >> /etc/opensips/opensips.cfg && opensips -c
Oct 26 11:08:04 [4416] CRITICAL:Traceback (last included file at the bottom):
Oct 26 11:08:04 [4416] CRITICAL: 0. /etc/opensips/opensips.cfg
Oct 26 11:08:04 [4416] CRITICAL:core:yyerror: parse error in /etc/opensips/opensips.cfg:263:1-30: syntax error
Oct 26 11:08:04 [4416] CRITICAL:this_is_not_a_valid_statement
Oct 26 11:08:04 [4416] CRITICAL:^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Oct 26 11:08:04 [4416] CRITICAL:core:yyerror: parse error in /etc/opensips/opensips.cfg:263:1-30:
解析器给出文件名:行:列(263:1-30)并原样回显出错行、用插入符标出错误范围——报错比多数语言友好。删除该行后 opensips -c 返回 config file ok, exiting...,服务全程未受影响(2.2 节)。红线纪律:任何配置修改必须过 opensips -c 再重启。
8.2 opensips-cli 的命名空间语法坑
按直觉传裸 MI 命令名(如 mi uptime)一律报错:
ERROR: no command 'uptime' in module 'mi'
原因:opensips-cli 0.4.1 起 MI 命令按 <模块>:<命令> 命名空间注册,正确写法是 opensips-cli -x mi core:uptime(7.2 节的报错清单即全部命令)。写错不存在的组合(如 mi uptime)时,先跑一遍不带参数的 opensips-cli -x mi 拿到合法命令清单再对号入座。
8.3 usrloc MI 命令的参数坑
usrloc:dump / usrloc:show_contact 的参数最容易踩坑,实录三连:
-- 传表名当首参 --
ERROR: command 'usrloc:dump' returned: Error executing command: -32602: Invalid params (Bad type for parameter 'brief')
-- 再补一个参数 --
ERROR: command 'usrloc:dump' returned: Error executing command: -32602: Invalid params (Too few or too many parameters)
-- show_contact 传裸用户名 --
ERROR: command 'usrloc:show_contact' returned: Error executing command: 400: Domain missing in AOR
正确用法:usrloc:dump <brief>(首参是 0/1 的"精简模式"开关,不是表名);usrloc:show_contact location 1001@10.89.62.3(AOR 必须带域,与 7.4 节绑定表中的 AOR 形式一致)。另注:参数类型错误还会同步落一条 openSIPS 侧日志(3.3 节 journalctl 前两行 get_mi_int_param/handle_mi_request ERROR 即是),从服务端日志反查 MI 调用错误也是一条路。
九、参考链接
- 本专栏前置篇目:
- 《openSIPS 4.0 介绍与选型指南》——产品认知与同类选型对比
- 《Ubuntu 22.04 安装 openSIPS 4.0 指南》——APT/离线安装、systemd 服务管理与监听地址修改
- 《openSIPS 4.0 Docker Compose 部署指南》——容器化形态下的 socket/alias/TCP 传输实操
- 官方文档:
- Core Cookbook(全局参数与伪变量):opensips.org/Documentation/Manual
- 各模块参数文档:opensips.org/Documentation/Modules-4-0
- opensips-cli 仓库:github.com/OpenSIPS/opensips-cli