开发心得-信令发送流水线与注意事项

3 阅读4分钟

开发心得:信令发送流水线

本文是 2.4.1.0 VSS流播放详解配套开发笔记

项目地址 github.com/openskeye/g…


1. 三个要点

  1. 国标出站 SIP 不要绕过 SendLogic:业务、HTTP、定时器只往 svcCtx.SipSendXXX 里投递;直接拿 gosip 在别处 Send,等于拆掉「单写出口」,并发事务和排障都会变差。
  2. types.Request + SipCatalogLoopMap 是设备真实来源:没注册进 map,大量 HTTP/信令路径会 DeviceUnregisteredNewGBSSender 第三参在 Invite 等场景常是通道 ID,与 Catalog 用的设备 ID 别混(详见 5.1)。
  3. SipSend* channel 有界(默认 100):洪峰时 <- 会阻塞生产者;要考虑节流、异步、或扩容缓冲,别假设「发进 channel 就等于已经发出 SIP」。

2. 总线模型:一条 select,多路投递

for {
    select {
    case v := <-l.svcCtx.SipSendCatalog:
        go func(v *types.Request) {
            if err := l.catalog(v); err != nil {
                functions.LogError("send catalog failed err: ", err)
            }
        }(v)
    // ... SipSendDeviceInfo / VideoLiveInvite / ...
    }
}
  • 外层一条 select:保证同一时刻只从一个 SipSend* 取一条不会select 里做重逻辑。
  • 内层普遍 go funcInvite / MS / SDP 慢指令不卡住其它信令;代价是 错误大多只打日志,HTTP 层拿不到 SendLogic 的 error—— heart flow 要靠 StepRecord/日志/MS 状态(见 §8)。

3. InitFetchDataState.Wait():和谁对齐?

SendLogic 与其它 gbs_proc 一样DO 开头 Wait()FetchData 两条分支各 Done 一次之后才消费 SipSend*

心得:冷启动结束前,往 SipSendCatalog 里塞任务可能被长时间阻塞在 send 侧(若 nobody 消费);集成测试里要 先等数据就绪mock 缩短启动链


4. Catalog

与 2.4.1.2 及 catalog_loop.go 一致:

来源行为
注册成功(+1s)SipSendCatalog 立即 + SipCatalogLoop 登记
心跳map 无记录时 补 Catalog + 补 loop
CatalogLoop.proc每秒扫表,判据 item.Now%val.Unix()==CatalogInterval 在常见配置下几乎不命中

规划时每 N 秒自动 Catalog,只改 YAML 不够,要对齐 proc;否则线上目录仍主要靠 注册/心跳/手工。若要严格周期,需要catalog_loop.go 或另起定时任务往 SipSendCatalog 投递。


5. ThrottleFixedGridTrailing:Catalog 上的节流器

catalog 里对 同一 req.ID约 3 秒栅格合并,避免短时间多次 Catalog 打爆设备

  • 调试时狂点「刷新目录」,可能只看到 最后一次 真正发出;前后请求在日志里像是被丢弃了。
  • 别在节流键里用错 ID(应用 设备国标 ID,与 NewGBSSender(..., req, req.ID) 一致)。

6. SipCatalogLoopMap:扩展 HTTP 时的必查点

大量 video_live_invite、手工 Catalog、部分诊断 都是 SipCatalogLoopMap.Get(deviceUniqueId)

  • 设备 刚上线 1s 内、或 心跳未补 loop 前,map 可能暂时没有;前端连点播放会得到 未注册
  • 注销路径要 SipCatalogLoop Online:false,否则恶意请求仍可能往里塞(与 register.go 一致性有关)。

7. VideoLiveInvite:不在 GBSSender 里结束

流水线:MS RTPPub → INVITE → ACK → 解析 200 SDP → MS ACKRtpPub → 状态位。并写入 AckRequestMapBYE

  • Invite 成功不等于浏览器已能播;播放器问 /video/stream,信令问 SIP/MS。见 [5.4 流播放开发心得]。
  • From/To/Via/SDP 填错全是身份混淆问题,见 5.1 / 2.4.1.1 SDP

8. 错误可见性

  • SipSendVideoLiveInvite <- msg 只说明 任务进入总线;真正失败可能在 VideoLiveInvite(MS、SIP 状态码、SDP 解析),最终 LogError
  • inviteStep / StepRecord:给前端 时间线;若产品要 HTTP 同步失败码,要在 Invite 之前 或 在轮询、WS设计,不能假设 SendLogic 会把 error 抛回 HTTP。

9. 新增一种出站信令时

  1. types.ServiceContext 增加 SipSendXxx chan *YourReq容量根据需求约定,默认 100)。
  2. service_context.NewServiceContextmake(chan ...)
  3. SendLogic.DO 增加 casego func + 独立 SendLogic 方法
  4. 业务侧只做 svcCtx.SipSendXxx <- ...禁止复制粘贴 GBSSender.Send 到 handler。
  5. 慢调用必须 context/超时 或可取消,避免 goroutine 堆积
  6. 若依赖 已注册:先 SipCatalogLoopMap.Get 再组 GBSSender

10. 联调清单

  • 新逻辑是否只通过已有 SipSend* 发出 SIP?
  • 是否误用 NewGBSSender 第三参(设备 vs 通道)?
  • 是否会在 channel 满 时阻塞 SIP 收包线程 / HTTP 请求
  • Catalog/目录类是否接受 3s 节流定时 proc 可能不触发 的现状?
  • Invite/Media 类是否验证 AckRequestMap 清理(BYE、stop_stream)无泄漏?
  • InitFetchDataState 与集成测试启动顺序是否一致?

11. 相关文档

文档内容
2.4.1.2 信令发送流水线正文设计与表
2.4.1.4 国标设备注册注册与 map 写入
5.1 信令与 SDPINVITE 头与 SDP
5.0 ServiceContextchannel / map 总览

主源码:internal/logic/gbs_proc/send_sip_proc.gocatalog_loop.gointernal/pkg/sip/gbs_send.gointernal/svc/service_context.go