
1. Kamailio伪变量初探SIP路由脚本的瑞士军刀第一次接触Kamailio的伪变量Pseudo-Variables时我正被一个SIP路由逻辑卡住三天——需要在请求头中动态插入带时间戳的Call-ID。当看到$Ts这个神奇符号瞬间解决问题时那种震撼感至今难忘。伪变量就像藏在Kamailio路由脚本里的魔法道具用对了能四两拨千斤。伪变量本质上是一种特殊的变量引用形式以$符号开头在Kamailio配置脚本中提供对系统信息、SIP消息元素、运行时数据等的动态访问能力。与普通变量不同它们无需声明即用像$ru请求URI、$fuFrom URI等开箱即用实时动态取值每次访问时获取最新状态如$Ts返回当前时间戳作用域穿透在路由块、分支处理等场景下保持一致性举个真实场景的例子我们需要在REGISTER请求中校验Contact头域的过期时间。传统方式要手动解析SIP消息而用伪变量只需一行if ($ct.fields.expires 3600) { # 直接访问Contact头的expires字段 xlog(过长的注册过期时间: $ct\n); }2. 核心伪变量分类与实战用法2.1 SIP消息解剖类伪变量这类伪变量直接映射SIP消息的各个组成部分是路由脚本中最常用的工具集$ru(Request URI) 处理INVITE请求时我曾遇到需要批量替换域名的情况。通过$ru.host newdomain.com;直接修改比正则匹配高效得多。注意修改后会立即影响后续路由逻辑。$hdr(name) 获取任意头域值时$hdr(Content-Type)比手动解析更可靠。特别提醒头域名称大小写敏感$hdr(CALL-ID)和$hdr(Call-ID)结果可能不同。$bav(Body Address Variables) 处理带SDP的INVITE时$bav(media.port)能快速提取媒体端口。曾有个项目需要限制音频端口范围用这个变量省去了整个SDP解析库。表SIP消息类核心伪变量速查变量等效字段使用示例注意事项$fuFrom头URI$fu.user获取主叫号码修改会影响后续计费$tuTo头URI$tu.port5060强制端口在ACK中可能为空$du目标地址$du sip:1.2.3.4;需先调用lookup()$avp(name)自定义AVP$avp(cos) gold;跨事务持久化2.2 系统运行时伪变量这类变量反映Kamailio实例的实时状态信息$Ts(Unix时间戳) 生成唯一ID时配合$Tf(微秒)可以避免冲突$var(callid) call- $Ts - $Tf;。实测在每秒万级请求下仍能保证唯一性。$Rp(本地接收端口) 多网卡环境下我曾用if ($Rp 5061) { force_tls(); }实现智能TLS切换。注意与$Lp(监听端口)的区别。$stat(name) 监控场景下特别有用如xlog(每秒请求量: $stat(rcv_requests)\n);。建议配合$time(%H:%M:%S)打时间戳。2.3 分支处理类伪变量处理并行分支如forking call时的关键变量$br(分支索引) 在失败路由中if ($br 0) { send_to_next_gw(); }可以实现主备网关切换。记住分支号从0开始计数。$T_branch_idx 比$br更精确能获取当前处理的分支索引。在t_on_branch事件路由中调试时特别有用。重要提示修改分支相关变量如$du后必须调用append_branch()才能生效这是新手常踩的坑。3. 伪变量转换与PV模块高阶技巧3.1 字符串操作转换pv模块提供的转换函数能让伪变量如虎添翼# 提取URI用户部分并转为大写 $var(user) $(fu.user{uri.escape,upper}); # 时间格式化示例 xlog(当前时间: $(Ts{time.date:%Y-%m-%d %H:%M:%S})\n);实际项目中我曾用$(hdr(Contact){param.value,expires})快速提取过期参数比手动字符串处理稳定十倍。转换函数支持链式调用但要注意性能损耗。3.2 正则表达式转换{s.regex}转换的强大之处在于可以就地修改值# 将86号码转换为国际格式 $fu.user $(fu.user{s.regex,/^\86([0-9])$/\1/});调试技巧先在xlog中测试正则效果如xlog(转换结果: $(fu.user{s.regex,...})\n);。曾有个复杂正则导致CPU飙升后来发现是.*未锚定导致回溯爆炸。3.3 数据类型转换处理数字比较时类型转换能避免很多坑# 字符串转整数比较 if (int($ct.fields.expires) 3600) { sl_send_reply(423, Interval Too Brief); }特别提醒{int}转换遇到非数字会返回0可能导致逻辑错误。安全做法是先{s.len}检查长度。4. 调试与性能优化实战4.1 伪变量调试技巧在复杂路由中定位问题时我总结了一套调试方法xlog分级输出xlog(L_DEBUG, 请求URI: $ru, 分支数: $bn\n);条件断点法if ($ru sip:debugtest.com) { xlog(L_ERR, 调试信息: $ru, $fu, $du\n); drop(); }变量追踪技巧 用$var(trace)记录关键节点值最后统一输出$var(trace) $var(trace) 阶段1: $ru |;4.2 性能陷阱与规避方案伪变量虽方便但误用会导致性能问题重复计算问题# 错误做法每次循环都执行转换 for ($var(i)0; $var(i)10; $var(i)$var(i)1) { $(ru.user{lower}); } # 正确做法预先转换存储 $var(lower_user) $(ru.user{lower});正则表达式优化避免在循环中使用复杂正则优先使用{s.substr}等简单操作对固定模式预编译regex模块内存缓存技巧 对频繁访问的伪变量如$Ts可以缓存到普通变量$var(now) $Ts;5. 版本兼容性与升级指南从Kamailio v5.2开始伪变量系统有几个重要变化新增变量$Ctx上下文变量替代部分全局变量用法$sht共享内存表访问更安全废弃特性$avp的字符串索引方式改用$(avp(name))部分转换函数的旧语法如uri.escape改为escape.uri性能改进PV模块的转换函数采用延迟计算正则引擎升级到PCRE2升级时建议先用-c参数检查配置逐步替换废弃语法重点测试性能敏感路径6. 真实案例用伪变量构建智能路由去年我们实现了一个基于地理位置的路由系统核心逻辑如下# 从P-Asserted-Identity提取号码 $var(caller) $(hdr(P-Asserted-Identity){uri.user}); # 查询号码归属地 if (!avp_db_query(SELECT region FROM numbers WHERE num$var(caller), $avp(region))) { xlog(L_ERR, 归属地查询失败: $var(caller)\n); } # 根据地域选择网关 switch ($avp(region)) { case north: $du sip:gw1.example.com; break; case south: $du sip:gw2.example.com; break; default: loadbalance(gw_pool); }这个方案处理了每天数百万呼叫关键就在于伪变量快速提取关键信息AVP变量保持跨事务状态转换函数确保数据安全7. 开发者必备的伪变量清单这是我整理的常用伪变量速查表打印出来贴在显示器旁能省不少时间表Kamailio伪变量终极参考分类变量描述示例SIP消息$rm请求方法if ($rm INVITE)$rp响应码if ($rp 487)URI处理$(uri.user)提取URI用户部分$(fu.user{uri.user})$(uri.host)提取URI主机部分if ($(ru.host) vip.com)头域处理$hdr(Content-Length)获取头域值if ($hdr(CL) 1000)$hdr_e(name)检查头域存在性if ($hdr_e(Record-Route))系统信息$Tf当前微秒数$var(uid)$Ts$Tf$time(%S)格式化时间xlog(时间: $time(%H:%M)\n)分支处理$bT分支标签t_on_branch(BR_$bT)$bS分支状态if ($bS 2)最后分享一个冷知识在Kamailio控制台输入cfg.dump 3可以查看所有伪变量的当前值调试复杂路由时比xlog更高效。不过生产环境慎用可能影响性能。