配置详解
本文定位
本文讲清国标监控两个最容易配错的概念域——SIP 接入配置与 ZLM 媒体配置,并完整说明 媒体节点模块(租户级多节点 + 自动负载)的模型与运作。配置项的 yaml 全量清单见 对接指南,具体操作步骤见 操作手册。
配置前缀
模块配置根前缀是 vms(非 blade.vms),总开关 vms.enabled=false 时整个模块不绑端口、不起定时器、不加载 ZLM。
一、配置全景
vms:
enabled # 模块总开关
gb28181:
sip: … # SIP 信令接入(全局单端口,多租户按 server-id)
zlm: … # ZLM「默认节点种子」+ 全局流控参数
snapshot: … # 抓图
record: … # 录像 / 下载
stream.retry: … # CLOSE_PENDING 重试
query.cache-ttl-ms # 异步查询结果缓存其中 sip 与 zlm 是部署落地的两个核心,也是本文重点。两者职责正交:
| 维度 | SIP 配置 | ZLM 配置 |
|---|---|---|
| 面向 | 设备 / 上级平台的信令通道 | 流媒体服务的媒体通道 |
| 形态 | 全局单实例(单端口) | 租户级多节点(见媒体节点模块) |
| 协议 | GB/T 28181 SIP | HTTP REST + Webhook |
| 配错典型症状 | 设备注册不上 / 401 循环 | 信令正常但点播黑屏 / 浏览器拉不到流 |
二、SIP 配置详解
2.1 一个端口,多个租户
平台 SIP 是全局单实例:进程只在 vms.gb28181.sip.port(默认 5060)上绑定一个 UDP+TCP 端口,所有租户的设备都连这同一个端口。租户不是靠端口区分的,而是靠 server-id:
- 设备 REGISTER 报文里携带平台 server-id;平台按 server-id 反查
iot_vms_platform行,拿到该行的tenant_id注入上下文。 - 所以不同租户用不同的 server-id(20 位国标编号),共用同一个 5060 端口与同一份密码语义。
vms.gb28181.sip.server-id是平台自身的默认 server-id;多租户场景下各租户在【国标平台】页面各配一条平台记录(各自的 server-id / domain)。 default-tenant-id(默认000000):当报文无法定位到具体租户平台时的兜底租户。
不需要每租户开一个端口
有的国标实现给每个租户开独立 SIP 端口(5060/5061/…)。本平台不这样做——单端口 + server-id 路由,既省端口又天然多租户,运维只需放通一个端口。
2.2 ip 与 sip-host:绑定地址 ≠ 对外地址
这是 SIP 接入最隐蔽的坑,docker / NAT 部署必须区分:
| 字段 | 含义 | 取值建议 |
|---|---|---|
ip | SIP 协议栈监听绑定的本机网卡地址 | 一般 0.0.0.0(绑所有网卡) |
sip-host | 写入出站 Via / Contact 头的对外信令 IP,设备据此把响应/媒体回送过来 | 设备可路由到的平台地址(容器内网 IP / 宿主机 IP / 公网 IP) |
sip-host 配错 = 设备注册看似成功却收不到后续信令
若 sip-host 填了容器内回环或错误地址,设备的 200 OK / RTP 会发到错误目的地,表现为"REGISTER 通过但点播无响应"。docker 部署务必设为设备能访问到的平台地址。
2.3 鉴权:平台默认密码 + 设备独立密码 + 注册模式
密码两级:
vms.gb28181.sip.password—— 平台级默认 Digest 密码。- 设备记录上的
password字段 —— 设备独立密码,配置后优先于平台默认密码用于该设备鉴权。适用于"一机一密"安全要求。
注册模式(平台级 register-mode):
| 模式 | 行为 | 适用 |
|---|---|---|
| AUTO(自动接入) | 任意设备只要携带正确密码鉴权通过,即自动落库接入 | 设备可信、批量接入 |
| MANUAL(手动注册) | 仅放行已在白名单登记的设备;未登记设备 REGISTER 一律拒绝(403) | 强管控、防陌生设备接入 |
准入与密码解析统一收敛在协议层的 resolveRegisterAuth 闸门,业务表不感知 SIP 细节。手动注册的操作步骤见 操作手册。
2.4 其余 SIP 参数
| 字段 | 默认 | 说明 |
|---|---|---|
transport | BOTH | UDP / TCP / BOTH 双栈 |
charset | GB2312 | 国标默认;部分上级要 UTF-8 |
register-expire | 3600 | 注册有效期(秒) |
keepalive-interval | 60 | 设备心跳周期(秒) |
mobile-position-event | presence | GPS 订阅 Event 头;海康老固件可改 Catalog;id=1 或 MobilePosition |
thread-pool-size | 50 | SIP 处理线程池 |
default-tenant-id | 000000 | 无法定位租户时的兜底 |
三、ZLM 配置概念
3.1 平台 ↔ ZLM 的两条通道
平台与 ZLM 是两个独立进程,通过两条方向相反的通道协作:
- ① REST(平台 → ZLM):开/关收流端口、抓图、录制。平台用
host:http-port,带secret鉴权。 - ② Webhook(ZLM → 平台):流就绪/断流/推流鉴权/节点心跳。ZLM 用
?secret=hook-secret校验。
3.2 地址四件套:谁访问谁,决定填什么
ZLM 配置最易混淆的是四个地址各服务于不同的访问方向,务必逐个理解:
| 字段 | 谁来访问这个地址 | 含义 | 留空回退 |
|---|---|---|---|
host | 平台 调 ZLM REST API | ZLM 的 HTTP API 地址 | —(必填) |
sdp-ip | 设备 推 RTP 流 | 下发给设备 SDP 里的媒体接收 IP | → host |
public-host | 浏览器 拉流 | 回填到 flv/hls/ws/rtmp URL 的地址 | → host |
internal-http-port | ZLM 自己(回环抓图) | ZLM 经 127.0.0.1 自访问的 HTTP 端口 | → http-port |
三个最常见的"信令正常但黑屏"
sdp-ip是回环/错误地址 → 设备无法把 RTP 推到平台,点播/回放无画面(生产启动期强制校验非回环)。public-host错 → 浏览器侧 ws-flv 地址不可达,前端转圈拉不到流(生产启动期强制非空)。- docker NAT 下用了对外映射端口当
internal-http-port→ ZLM 抓图回环拉不到自身流,getSnap 失败。容器内须填容器内[http] port。
3.3 安全三件套
| 字段 | 作用 | 强制性 |
|---|---|---|
secret | 平台调 ZLM REST API 的密钥,须等于 ZLM config.ini [api] secret | 必配 |
hook-secret | ZLM 回调平台的校验密钥,配在各 hook URL 的 ?secret= | 启动期 fail-fast 强制非空 |
allowed-hook-ips | 可选 webhook 源 IP 白名单(CSV) | 生产建议配,防伪造回调 |
3.4 收流模式与流控
| 字段 | 默认 | 说明 |
|---|---|---|
tcp-mode | true | TCP 被动收流;NAT 场景建议 true(媒体节点可按节点覆盖) |
waiting-ttl-seconds | 90 | WAITING 短超时:INVITE 已发但未收到 RTP 即回收 |
session-ttl-seconds | 7200 | 流活跃 TTL;GC 与 CLOSE_PENDING→EXPIRED 都依据 |
session-retain-days | 3 | 终态会话历史保留天数 |
rtmp-port | 1935 | 回填 rtmp 拉流地址用 |
四、媒体节点模块
4.1 从「单节点写死」到「租户级多节点」
早期 ZLM 是全局单节点:vms.gb28181.zlm 里写死一个 host/secret,所有流都落它。自 v2.7 起演进为租户级多节点 + 全自动负载:
| 项 | 旧(单节点) | 新(媒体节点模块) |
|---|---|---|
| 节点来源 | yaml 写死 | iot_vms_media_node 表,页面增删改 |
| 多租户 | 全租户共用一个 ZLM | 每租户配自己的节点,互不影响 |
| 多节点 | 不支持 | 配几个即几元集群,无集群开关 |
| 选节点 | 恒定唯一 | 每路新流自动选并发最低的启用节点 |
| yaml 角色 | 唯一事实源 | 仅作「默认节点种子」植入默认租户 |
设计取舍:不过度
- 选节点门槛只看
enabled,不以心跳在线作硬门槛——信任管理员意图,免离线巡检任务、无心跳未配的回归。 - 负载度量用 DB 实时并发会话数
COUNT,不引 Redis 计数器。 - 在线状态由心跳新鲜度派生,无冗余
node_status列。 - 删除带活动会话的节点视为误操作,按正常异常传播,不做迁移兜底。
4.2 数据模型(iot_vms_media_node)
| 列 | 说明 |
|---|---|
node_id | 节点编码,全局唯一,须等于 ZLM general.mediaServerId |
name | 节点名称 |
host / http_port | 平台访问 ZLM REST API 的地址端口 |
internal_http_port | ZLM 回环自访问端口,0 回退 http_port |
sdp_ip | 对设备暴露的 RTP 媒体 IP,空回退 host |
public_host | 浏览器拉流 host,空回退 host |
rtmp_port / secret / tcp_mode | RTMP 端口 / API 密钥 / TCP 被动收流 |
enabled | 启用开关(选节点的唯一准入条件) |
last_keepalive_time | 最近心跳,派生在线状态(默认 60s 窗口) |
字段语义与「地址四件套」(§3.2)一致——媒体节点表本质就是把原 yaml 单节点的字段搬进按租户多行的表。
4.3 选节点与自动负载
关键不变量:会话一旦创建即与某个 node_id 绑定;后续 ZLM 回调(流就绪回填 ws-flv 地址)、关流、录制都按 session.nodeId 回查同一节点,杜绝"在 A 节点开、去 B 节点关"的错配。节点配置以不可变视图 ZlmNode 贯穿,地址回退(sdp-ip→host 等)内聚其中。
pickNode():当前租户enabled=1的节点中取并发最低者;无可用节点抛清晰异常("当前租户无可用媒体节点…")。- 某节点停用后不再被选;在播旧流断开、重新点播即自动转移到其他节点(被动容灾)。
4.4 心跳与在线状态
- ZLM 周期 POST
…/vms/webhook/on_server_keepalive,上报mediaServerId;平台回写对应节点last_keepalive_time。 - 在线 = 最近心跳在 60s 窗口内,纯由心跳新鲜度派生,无离线巡检任务。
- 心跳仅服务"看得见节点死活",不参与选节点门槛(§4.1 取舍)。
4.5 默认节点植入(零回归)
启动时 VmsMediaNodeSeeder 若发现 vms.gb28181.zlm.node-id 尚不存在,则把该 yaml 单节点植入默认租户 000000 的节点表。既有单节点部署升级后无需手工录入即继续取流;其余租户在【节点配置】页面自助配置。以 node-id 全局存在性幂等。
4.6 ZLM 侧必配
| 项 | 要求 |
|---|---|
general.mediaServerId | 必须等于该节点在媒体节点表中的「节点编码」node-id |
| hook 列表 | 配置 4 个回调 on_publish / on_stream_changed / on_stream_none_reader / on_server_keepalive,URL 末尾带 ?secret=<hook-secret> |
[api] secret | 等于节点 secret |
4.7 前端
【视频中心 → 节点配置】卡片栅格页:卡片按在线着色,展示名称/编码/地址/在线状态/并发数/最近心跳;抽屉表单含基础地址 + 折叠进阶(内网端口/RTMP/SDP IP/拉流 Host/密钥/TCP 模式),密钥写入项编辑留空保持原值。
五、启动期配置校验
平台启动期对关键配置 fail-fast,避免"配错却跑起来、运行时才黑屏":
| 校验 | 触发条件 | 目的 |
|---|---|---|
hook-secret 非空 | 任意环境 | 防公网部署被任意 IP 伪造 webhook |
sdp-ip(或 host)非回环 | 非 dev profile | 防设备 SDP 媒体 IP 为回环导致无画面 |
public-host 非空 | 非 dev + fail-on-missing-public-host=true | 防浏览器拉流地址回退 127.0.0.1 |
allowed-hook-ips 非空 | 非 dev profile | 生产强制 webhook 源 IP 白名单 |
开发环境可启用 spring.profiles.active=dev 跳过上述生产校验。
