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

心跳是设备主动报「我还活着」,几十字节,定时发。平台靠它判在线,也靠它发现掉线。 判定门槛是「连续丢几个」——丢一个太敏感,丢五个太迟钝,三个周期是工程折中。

📝 本节和前面几节的关系

上一节讲查询,这一节讲心跳。 心跳是国标里最「便宜」的报文,但它是整个平台「在线状态」的唯一依据。 所以它便宜,却重要。

一、心跳里到底有什么

一个心跳报文两百多字节,内容很少:设备编号、消息序号、时间、状态。 很多人把它当「空包」直接丢掉,这是错的: 有些设备会在心跳里附带「通道状态变了」的提示,不看就漏了。 心跳的周期是可配的,常见六十秒。 这个值不宜太短:短了平台要处理得更频繁,收益却不大。 也不宜太长:掉线发现得晚,录像断了都不知道。 六十秒是个经验值:三个周期判死,就是三分钟之内发现,够用。

二、判离线:丢几个才算

丢一个就判离线,等于把每次网络抖动都当故障——一个丢包就能让告警响一次。 丢五个才判,掉线五分钟平台还以为在线,录像断了、报警不来,全都不知道。 三个周期是折中:三分钟之内发现,抖动基本能滤掉。 这个数不是标准规定的,是工程上试出来的。 还有一点要注意:UDP 上的心跳本来就会丢,这不是故障。 所以「丢了」和「断了」必须靠「连续」来区分,单次丢失绝不能改状态。

三、真正的负担在写库

一千路设备、六十秒周期,每秒十几个心跳,每天三百多兆——相对视频流量,这根本不值一提。 所以「心跳会不会太多」这个担心是多余的。 真正的负担在「平台为每个心跳做什么」:更新在线状态、写数据库、刷新缓存。 如果每个心跳都写一次库,一千路就是每秒十几次写, 换成五千路就是每秒八十多次——数据库很快就会成为瓶颈,日志也会膨胀。 标准做法是「只在状态变化时写库」: 心跳只更新内存里的「最后活跃时间」,只有「在线」变「离线」或反过来时才落盘。 内存里那个时间戳,才是判断在线的真正依据。 数据库里的状态字段只是「结果」,不是「依据」。

四、动手 Demo:心跳停了多久才算离线

按四个判定门槛算离线延迟,看哪个合理。 再算一千路设备的心跳量和写库压力。

period = 60
for miss in (1, 2, 3, 5):
    print("丢", miss, "个 →", miss * period, "秒")

n, ka = 1000, 240
print("千路每秒心跳 %.1f 个" % (n / float(period)))
print("千路每天 %.1f MB" % (n * ka * 86400 / float(period) / 1e6))
print("每个都写库 = 每秒 %.0f 次写" % (n / float(period)))

实跑输出:

--------------------------------------------------------------------
心跳停了多久才算离线
--------------------------------------------------------------------
**心跳是设备主动报「我还活着」**:**一个 `Keepalive` 报文,几十个字节,定时发。**
**平台靠它判断在线,也靠它发现掉线。**
**判定的门槛是「连续丢了几个」,这个数直接决定误报率。**

  判定条件        离线延迟       评价
  ---------------------------------------------
  **丢 1 个**     **60 秒**      **太敏感**
  **丢 2 个**     **120 秒**     **偏灵敏**
  **丢 3 个**     **180 秒**     **常用**
  **丢 5 个**     **300 秒**     **太迟钝**

**丢一个就判离线,等于把网络的每一次抖动都当成故障**——**一个丢包就能让告警响一次。**
**丢五个才判,掉线三分钟平台还以为在线,录像断了都不知道。**
**三个周期是工程上的折中:三分钟之内发现,抖动基本能滤掉。**

**心跳本身的量很小**:
  一个心跳 240 字节、周期 60 秒
  千路设备每秒 16.7 个心跳、3.9 KB/s
  千路设备每天 345.6 MB

**每天三百多兆,相对视频流量完全不是负担**——**所以「心跳会不会太多」这个担心是多余的。**
**真正的负担在「平台要为每个心跳做什么」**:**更新在线状态、写数据库、刷新缓存。**
**如果每个心跳都写一次库,一千路就是每秒十几次写**——**这才是要优化的地方。**

  做法                     每秒写库次数     后果
  ------------------------------------------------------------------------
  **每个心跳都写**         **约 17 次**     **数据库压力大、日志膨胀**
  **只在状态变化时写**     **接近 0**       **推荐做法**
  **批量合并写入**         **约 1 次**      **折中,延迟几秒**

**第二行是标准做法**:
**心跳只更新内存里的「最后活跃时间」,只有当「在线」变「离线」或反过来时才写库。**
**内存里那个时间戳,才是判断在线的真正依据。**

**最后提醒一个细节**:**心跳报文里也带 `DeviceID` 和 `SN`**,
**别只当它是「空包」直接丢掉**——**有些设备会在心跳里附带「通道状态变了」的提示,不看就漏了。**

第一行到第四行是四个门槛:六十秒、一百二十秒、一百八十秒、三百秒。 丢一个就判,等于每次抖动都报警;丢五个才判,掉线五分钟还不知道。 三个周期是折中,也是绝大多数平台的实际配置。 心跳量那一行是每秒十六点七个, 每天三百多兆——看着不少,但相对一千路视频(每天几十个 TB)就是零头。 最后一行才是关键:每秒十几次写库,五千路就是八十多次。 这不是「能不能扛」的问题,是「完全没必要」的问题——心跳只用来更新内存里的时间戳就够了。 所以那张表里「只在状态变化时写」才是标准做法: 写库次数接近零,代价是「状态变化有几十毫秒延迟」,而这个延迟对在线状态判断毫无影响。

30 秒实验

  1. 算一下:心跳六十秒、丢三个判死,离线延迟多少?(180 秒)
  2. 想一下:为什么单次丢心跳不能改状态?
  3. 想一下:心跳只更新内存而不写库,会不会丢状态?(重启会,但可接受)

常见误区

  1. 以为「心跳是空包可以忽略」。 有些设备在心跳里带通道状态变化。
  2. 以为「心跳丢了就是掉线」。 UDP 上丢包正常,要看「连续丢几个」。
  3. 以为「心跳多了会压垮带宽」。 压垮的是数据库写,不是带宽。
自测三问(点开对答案) 1. 判离线的门槛为什么取三个周期? —— **一个太敏感、五个太迟钝**,**三个周期能在三分钟内发现且滤掉抖动。** 2. 心跳报文里可能带什么额外信息? —— **通道状态变化**,当空包丢掉就会漏。 3. 为什么心跳不该每个都写库? —— **一千路每秒十几次写,五千路八十多次**,**而写库对「判在线」毫无必要。**

下一节讲历史:录像怎么查、怎么放、怎么下载,三件事三套流程。

相关笔记