📹 第十章 GB/T 28181协议原理
📄 一句话概括

目录查询是级联对接第一个要跑通的流程: 上级平台要知道下级有多少设备、编号是什么、在不在线。 一万路不可能塞进一个报文,所以必须分页——分页不省流量,省的是「单次超时的风险」。

📝 本节和前面几节的关系

上一节是设备说话,这一节回到平台说话。 查询是国标里最「重」的一类交互:它可能返回几万条记录。 所以它最考验「分页」和「异步」的设计。

一、四种查询各查什么

DeviceInfo 查设备本身:厂商、型号、固件版本、通道数。 调试点播前先查这个,能确认设备到底认不认这个编号。 DeviceStatus 查运行状态:是否在线、有没有录像、磁盘剩多少、告警状态。 Catalog 查目录:这是最重的一个,返回设备和通道的完整列表。 ConfigDownload 查配置:基本参数、视频参数、录像计划。 四个查询的返回量差着数量级:DeviceInfo 一个报文就够,Catalog 可能要几百页。 所以对接时先问一句「你们目录有多少条」,比什么都实在。

二、分页的两种设计

标准里给了 SumNum 字段:第一页就告诉对方总共多少条,让平台知道要翻多少页。 但「怎么翻下一页」标准没写死,常见两种做法: 一种是「带上一次的最后一条编号」,另一种是「带页码」。 第一种更可靠:目录在翻页过程中可能变化(设备上线掉线),带游标不会漏也不会重。 第二种简单,但翻页期间有增删就会错位。 这就是为什么目录同步要「可断点续传」:一万路翻两百页,中间断一次,不能从头再来。

三、为什么目录同步要异步

算一下就知道它有多慢: 每页五十条、每页一次往返,一万路要四百次往返,按五十毫秒算就是二十秒。 二十秒的操作绝不能放在注册响应里同步做, 因为注册有自己的超时,超时就重注册,重注册又触发同步,死循环。 正确做法是「先回一条「正在同步」,后台慢慢拉」,同步完再推一条「目录已更新」。 这套模式有个名字叫「最终一致」:平台不保证「此刻目录是全的」,只保证「过一会儿会全」。 接受这一点,很多设计难题就消失了。

四、动手 Demo:一万路目录,一次查不完怎么分页

先算一条设备条目多少字节,看一万路全塞一个报文会多大。 再按四种每页条数算页数、单页大小和总往返次数。

item = ("<Item><DeviceID>34020000001320000001</DeviceID>"
        "<Name>北门摄像机</Name><Status>ON</Status></Item>")
print("一条", len(item), "字节")
print("一万路", "%.1f MB" % (len(item) * 10000 / 1e6))

for size in (50, 100, 500, 1000):
    pages = (10000 + size - 1) // size
    print(size, pages, "页",
          "%.0f KB" % (len(item) * size / 1024.0),
          pages * 2, "次往返")

实跑输出:

--------------------------------------------------------------------
一万路目录,一次查不完怎么分页
--------------------------------------------------------------------
**目录查询是级联对接里第一个要跑通的流程**:**上级平台要知道「下级有多少设备、编号是什么」。**
**一万路设备不可能塞进一个报文**,所以标准留了两个字段:**`SumNum` 告诉对方总共多少条**,
**`DeviceID` 用来指定从哪开始续查。**

  一条设备条目 91 字节
  一万路全塞一个报文 = 0.9 MB

**这个数字就是分页的理由**:**一个将近一兆的 SIP 报文,几乎必然超时或被中间设备丢掉。**

  每页条数           页数           单页大小      总往返
  ------------------------------------------------------------------
  **50 条/页**       **200 页**     **4 KB**      **400 次往返**
  **100 条/页**      **100 页**     **9 KB**      **200 次往返**
  **500 条/页**      **20 页**      **44 KB**     **40 次往返**
  **1000 条/页**     **10 页**      **89 KB**     **20 次往返**

**第一行到第四行,页数差了二十倍**,
**但总字节数几乎一样**——**分页不省流量,省的是「单次超时的风险」。**
**每页五十条最稳,单页才几千字节,任何网络都能过。**
**代价是往返次数:一万路要四百次。**
**按每次五十毫秒算,全程二十秒。**

  按每页 50 条算:400 次往返 × 50 毫秒 = 20 秒

**所以目录同步是个「慢操作」,不能放在注册响应里同步做**,**必须异步、可断点续传。**
**工程上的做法是「先返回一条「正在同步」,后台慢慢拉」。**

**`SN` 的用法要注意**:**它是消息序号,用来配对请求和响应,不是翻页游标。**
**真正的翻页靠「上一次收到的最后一条设备编号」**——**下次查询带上它,表示「从这条之后继续」。**
**这一点各厂商实现不统一,对接时务必先抓包确认。**

一条九十一字节、一万路将近一兆——这个数就是分页的理由。 一个将近一兆的 SIP 报文,几乎必然超时或被中间设备丢掉。 四行的总字节数几乎一样,页数却差二十倍:分页不省流量,它省的是「单次超时的风险」。 每页五十条最稳,单页才四 KB,任何网络都能过;代价是四百次往返、二十秒。 每页一千条只要二十次往返,但单页八十九 KB,中间任何一个环节丢一个包就得重来。 所以这个选择不是「越大越好」也不是「越小越好」,而是要跟「网络的可靠程度」匹配。 专线可以大一点,公网只能小一点。

30 秒实验

  1. 算一下:一万路、每页五十条,要翻多少页?(200 页)
  2. 想一下:为什么带游标翻页比带页码可靠?
  3. 想一下:为什么目录同步不能放在注册响应里做?

常见误区

  1. 以为「分页是为了省流量」。 总字节几乎不变,省的是单次超时风险。
  2. 以为「SN 是翻页游标」。 它是消息序号,翻页靠「上一次的最后一条编号」。
  3. 以为「目录同步可以同步做完」。 一万路要二十秒,必须异步。
自测三问(点开对答案) 1. 四种查询分别查什么? —— **`DeviceInfo` 设备信息、`DeviceStatus` 运行状态、`Catalog` 目录、`ConfigDownload` 配置**。 2. `SumNum` 字段的作用是什么? —— **在第一页就告诉对方总共多少条**,让平台知道要翻多少页。 3. 为什么带游标翻页比带页码可靠? —— **翻页期间目录可能增删**,**游标不会漏也不会重,页码会错位。**

下一节讲心跳:几十字节的报文,决定平台什么时候认「它掉线了」。

相关笔记