Wholesale 完整案例

本节通过一个完整案例,演示从零搭建 Wholesale 语音中转平台。

场景描述

公司:某语音运营商,向 3 个下游客户(租户)提供国内/国际语音落地。

上游运营商:

  • 运营商 A:国内线路,SIP 中继 10.0.1.100:5060
  • 运营商 B:国际线路,SIP 中继 10.0.2.50:5060

下游客户:

  • 客户 X:日均 5 万通,主要呼国内
  • 客户 Y:日均 2 万通,呼国内+国际
  • 客户 Z:日均 1 万通,仅呼国际

Step 1:数据库准备

# MySQL
mysql -u root -p <<EOF
CREATE DATABASE rustpbx CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
CREATE USER 'rustpbx'@'%' IDENTIFIED BY 'StrongP@ssw0rd';
GRANT ALL PRIVILEGES ON rustpbx.* TO 'rustpbx'@'%';
FLUSH PRIVILEGES;
EOF

Step 2:config.toml

http_addr = "0.0.0.0:8080"
database_url = "mysql://rustpbx:StrongP@ssw0rd@10.0.0.100:3306/rustpbx"
external_ip = "203.0.113.10"

[proxy]
addr = "0.0.0.0"
udp_port = 5060
modules = ["acl", "auth", "registrar", "call"]
media_proxy = "auto"
addons = ["wholesale"]

[proxy.wholesale]
route_cache_capacity = 10000
route_cache_ttl_secs = 30

[proxy.wholesale.circuit_breaker]
failure_threshold = 5
open_duration_secs = 30
half_open_probes = 1
failure_codes = [503, 408, 504]

[console]
session_secret = "my-console-secret-2026"

Step 3:配置 SIP 中继

mkdir -p config/trunks

config/trunks/carrier_a.toml(国内运营商):

[[trunk]]
name = "carrier-a"
dest = "sip:10.0.1.100:5060"
backup_dest = "sip:10.0.1.101:5060"
direction = "outbound"
codec = ["pcmu", "pcma", "g729"]
max_calls = 300
max_cps = 50
weight = 100

[trunk.auth]
username = "myacct_a"
password = "pass_a"

config/trunks/carrier_b.toml(国际运营商):

[[trunk]]
name = "carrier-b"
dest = "sip:10.0.2.50:5060"
direction = "outbound"
codec = ["pcmu", "pcma", "g729", "g722"]
max_calls = 200
max_cps = 30
weight = 100

[trunk.auth]
username = "myacct_b"
password = "pass_b"

Step 4:启动服务

docker run -d --name rustpbx \
  -v $(pwd)/config.toml:/app/config.toml \
  -v $(pwd)/config:/app/config \
  -p 8080:8080 -p 5060:5060/udp \
  docker.cnb.cool/miuda.ai/rustpbx:latest

Step 5:Web 控制台配置

访问 http://203.0.113.10:8080,创建超级用户后进入控制台。

5.1 创建费率表

Wholesale → 费率表 → 新建:

名称类型
buy-carrier-a买
buy-carrier-b买
sell-domestic卖
sell-international卖

添加费率条目:

buy-carrier-a(买-国内):

前缀费率最低时长步长
86100.02566
86210.02566
860.03566

buy-carrier-b(买-国际):

前缀费率最低时长步长
10.0666
440.0566
810.0766

sell-domestic(卖-国内):

前缀费率最低时长步长
86100.0466
86210.0466
860.0566

sell-international(卖-国际):

前缀费率最低时长步长
10.1066
440.0866
810.1266

5.2 配置中继 Wholesale 参数

Wholesale → 中继管理:

  • carrier-a → 买费率表: buy-carrier-a,启用熔断器
  • carrier-b → 买费率表: buy-carrier-b,启用熔断器

5.3 创建路由策略

Wholesale → 路由策略 → 新建:

策略 “profile-domestic”(国内路由):

优先级中继前缀权重
10carrier-a86100

策略 “profile-full”(国内+国际):

优先级中继前缀权重
10carrier-a86100
10carrier-b1100
10carrier-b44100
10carrier-b81100

策略 “profile-international”(仅国际):

优先级中继前缀权重
10carrier-b1100
10carrier-b44100
10carrier-b81100

5.4 创建租户

字段客户 X客户 Y客户 Z
名称客户X-国内客户Y-全路由客户Z-国际
余额500003000020000
信用额度500030002000
卖费率表sell-domesticsell-domestic + sell-internationalsell-international
路由策略profile-domesticprofile-fullprofile-international
最大并发20010050
最大 CPS301510
LCR启用启用启用

5.5 绑定租户中继

为每个租户添加入向中继绑定,配置源 IP:

  • 客户 X:来源 IP 198.51.100.10
  • 客户 Y:来源 IP 198.51.100.20
  • 客户 Z:来源 IP 198.51.100.30

5.6 热加载

点击费率表和路由策略的 “重新加载” 按钮。

Step 6:验证

6.1 测试呼叫

从客户 X 的软交换发起呼叫到 861012345678:

  • 应匹配路由前缀 8610(优先级 10)
  • 选择 carrier-a
  • 匹配卖费率 8610 = 0.04 元/分钟
  • 匹配买费率 8610 = 0.025 元/分钟

6.2 检查 CDR

Wholesale → 话单管理:确认 CDR 已生成,字段正确。

6.3 检查余额

租户详情:确认余额已扣减。

6.4 价格计算器验证

租户详情 → 价格计算器:

  • 输入 861012345678 → 显示前缀 8610,卖 0.04,买 0.025,利润 0.015
  • 输入 12125551234 → 客户 X 应显示“无路由“(仅有国内策略)

Step 7:日常运维

操作频率位置
查看监控大盘每天Wholesale 控制台
检查熔断器状态每天监控限流
核对余额每天租户详情
导出 CDR每周话单管理 → 导出
生成账单每月账单管理
费率调整按需费率表 → 重新加载
数据库备份每天MySQLdump / pg_dump