JSON-RPC 路由
SBC 最强大的功能是 JSON-RPC 动态路由:每个入局 SIP INVITE 都可以调用外部 HTTP API 来决定呼叫处理方式。
1. 工作原理
SIP INVITE 到达
│
▼
SBC Inspector
│
├── 提取匹配字段 (Caller, Callee, Direction...)
├── 匹配 Rule 列表
│ │
│ ▼ 命中规则
├── 渲染请求模板 (Jinja2)
├── HTTP POST 到上游 API
│ │
│ ▼ 收到响应
├── 解析响应 JSON
├── 应用号码改写 (caller_rewrite, callee_rewrite)
├── 注入/删除 Header
└── 返回路由决策 (forward / reject / busy)
2. 配置文件
config/sbc/sbc_jsonrpc.toml:
[[rules]]
name = "route-by-api"
description = "通过 API 路由所有入局呼叫"
[rules.match]
logic = "all"
conditions = [
{ field = "Direction", operator = "Equals", value = "inbound" }
]
[rules.upstream]
url = "http://10.0.0.50:3000/api/sbc/route"
method = "POST"
timeout_ms = 2000
retries = 1
[rules.upstream.headers]
Authorization = "Bearer my-api-key"
Content-Type = "application/json"
[rules.upstream.body_template]
# Jinja2 模板
body = '''
{
"caller": "{{ caller }}",
"callee": "{{ callee }}",
"call_id": "{{ call_id }}",
"from_host": "{{ from_host }}",
"direction": "{{ direction }}"
}
'''
[rules.response]
# 从响应 JSON 提取路由决策
action_field = "action" # "forward" / "reject" / "busy"
trunk_field = "trunk" # 转发到的中继名称
caller_rewrite_field = "caller" # 改写后的主叫
callee_rewrite_field = "callee" # 改写后的被叫
reject_code_field = "sip_code" # 拒绝时的 SIP 状态码
[[rules.response.header_injections]]
action = "add"
name = "X-Route-Source"
value = "sbc-jsonrpc"
3. 匹配条件
3.1 可匹配字段
| 字段 | 说明 | 示例 |
|---|---|---|
| Caller | 完整主叫 URI | sip:1000@10.0.0.1 |
| Callee | 完整被叫 URI | sip:9000@10.0.0.1 |
| CallerUser | 主叫号码 | 1000 |
| CalleeUser | 被叫号码 | 9000 |
| CallerHost | 主叫域名/IP | 10.0.0.1 |
| CalleeHost | 被叫域名/IP | 10.0.0.1 |
| Direction | 方向 | inbound / outbound |
| CallId | SIP Call-ID | abc@10.0.0.1 |
| UserAgent | SIP User-Agent | Linphone/4.0 |
| Header | 自定义 Header | 任意 Header 名 |
3.2 操作符
| 操作符 | 说明 |
|---|---|
| Equals | 精确匹配 |
| NotEquals | 不等于 |
| Contains | 包含 |
| StartsWith | 前缀匹配 |
| EndsWith | 后缀匹配 |
| Regex | 正则匹配 |
| Exists | Header 存在 |
| NotExists | Header 不存在 |
3.3 逻辑组合
[rules.match]
logic = "all" # all = 全部满足, any = 任一满足
conditions = [
{ field = "Direction", operator = "Equals", value = "inbound" },
{ field = "CalleeUser", operator = "StartsWith", value = "9" },
]
4. 上游 API 契约
4.1 请求(SBC → 上游)
SBC 发送 HTTP POST,Body 由模板渲染。
4.2 响应(上游 → SBC)
{
"action": "forward",
"trunk": "carrier-a",
"callee": "861012345678",
"caller": "057112345678",
"headers": {
"X-Custom": "value"
}
}
action 可选值:
| 值 | 说明 |
|---|---|
forward | 转发到指定中继 |
reject | 拒绝(可指定 SIP 状态码) |
busy | 返回 486 Busy |
4.3 主叫覆写与非法 URI(0.5+)
- 响应中的
caller字段会覆写出站腿的主叫身份——外部系统可按通话重新盖 CLI(租户级主叫池、合规改写)。 - 响应中的非法目标 URI 现在直接使呼叫失败并给出明确错误,而不是回退到非预期路由:畸形的决策按路由错误处理,不再被静默忽略。
5. 模板变量
Jinja2 模板可用变量:
| 变量 | 说明 |
|---|---|
{{ caller }} | 主叫号码 |
{{ callee }} | 被叫号码 |
{{ call_id }} | SIP Call-ID |
{{ from_host }} | 来源 IP |
{{ to_host }} | 目标 IP |
{{ direction }} | 方向 |
{{ user_agent }} | User-Agent |
6. 控制台操作
6.1 JSON-RPC 配置管理
SBC → 路由 → JSON-RPC 配置:Web UI 编辑和保存 sbc_jsonrpc.toml。
6.2 规则模拟
SBC → 路由 → 模拟:输入 Caller/Callee 等字段,测试匹配哪条规则,预览发送到上游 API 的请求。
6.3 路由模拟
SBC → 路由 → 路由模拟:完整模拟一个 INVITE 的路由决策过程(含 JSON-RPC 调用)。