SIP 事务(ServerTransaction)是 JAIN-SIP 的内存对象,绑定在单进程内,无法跨节点共享。
同一设备的所有 SIP 消息必须打到同一节点,这是 SIP 协议的有状态特性决定的,框架层面无法绕过。
设备 A ──┐
设备 B ──┤ ┌─ Node-1 (10.0.0.1:5060)
设备 C ──┼──→ VIP 1.2.3.4:5060 ──→ │
设备 D ──┤ (keepalived + ipvs) └─ Node-2 (10.0.0.2:5060)
设备 E ──┘ 源IP哈希
│
Redis
(共享业务状态)
关键点:
| 组件 | 说明 |
|---|---|
ServerTransaction |
JAIN-SIP 事务对象 |
SipTransactionRegistry |
事务上下文,含 ServerTransaction |
SipTransactionContext |
ThreadLocal 事务传递 |
处理方式: 靠源 IP 哈希保证同一设备打同一节点,无需改造。
| 组件 | 说明 |
|---|---|
SipSubscribe.okSubscribes/errorSubscribes |
按 callId 存的响应回调 |
处理方式: 将 ConcurrentHashMap 替换为 Redis 实现,节点故障后新节点能接管业务状态。
旧版本中的
SubscribeHolder.catalogMap/mobilePositionMap已于 v1.3.0 移除,订阅状态下放业务方根据需要自行管理(Redis 或本地)。
| 组件 | 说明 |
|---|---|
DeviceSessionCache |
设备会话(ip/port/transport),业务方实现 |
DeviceSupplier |
设备信息提供,业务方实现 |
ServerDeviceSupplier.authenticate(userId, SIPRequest) |
注册鉴权,业务方实现(v1.3.0 替代旧 ServerRegisterProcessorHandler) |
设备发包经过 NAT,服务器收到的源地址是 NAT 出口地址。
rport/received,回包发到 NAT 出口,透明。ServerRegisterRequestProcessor 从 Via 头的 received/rport 取 NAT 出口地址存入 DeviceSessionCache,主动发消息时用此地址,可达。节点监听内网地址,但 Via/Contact 头需填对外可达地址,否则设备无法回包。
配置方式:
sip:
server:
ip: 0.0.0.0 # 监听地址(内网/所有网卡)
port: 5060 # 监听端口
external-ip: 1.2.3.4 # 对外可达地址(VIP 或公网 IP),填入 Via/Contact
external-port: 5060 # 对外端口(端口映射时填映射后的端口)
external-ip 不配置时 fallback 到 ip,不影响现有部署。
多节点时 external-ip 填 VIP 地址,设备后续消息发到 VIP,ipvs 源 IP 哈希保证还是打到同一节点。
# 安装
apt install keepalived ipvsadm
# 添加虚拟服务(源IP哈希)
ipvsadm -A -u 1.2.3.4:5060 -s sh
ipvsadm -a -u 1.2.3.4:5060 -r 10.0.0.1:5060 -m
ipvsadm -a -u 1.2.3.4:5060 -r 10.0.0.2:5060 -m
# UDP 同样配置
ipvsadm -A -u 1.2.3.4:5060 -s sh
ipvsadm -a -u 1.2.3.4:5060 -r 10.0.0.1:5060 -m
ipvsadm -a -u 1.2.3.4:5060 -r 10.0.0.2:5060 -m
-s sh = source hash,-m = masquerade(NAT 模式)。
设备 NAT 出口 IP 变化(重拨、网络切换)时:
DeviceSessionCache水平扩容的粒度是设备 NAT 出口,不是物理设备数。
⚠️ 共享 NAT 出口的聚集效应:
-s sh按客户端源 IP 哈希分片,企业园区/家庭网络下多个 GB28181 设备共享同一 NAT 公网 IP 时会全部落到同一节点。规划容量时应按 NAT 出口数 而非设备数估算节点负载,必要时可在前置层做 deviceId 二次分片(但需确保同 deviceId 始终打同节点)。