ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

JT/T 808 协议网关的设计与实现(第 2 篇 · 后端技术)

JT/T 808 协议网关的设计与实现(第 2 篇 · 后端技术) 1、实时定位2、轨迹查询3、告警管理4、围栏设置5、看板与报表Netty RocketMQ PostgreSQLJT/T 808 协议网关的设计与实现第 2 篇 · 后端技术上篇讲了平台的五层骨架和业务闭环。这篇钻进后端核心我们自研的两个模块——jt-gateway808/1078/809 三个协议网关只翻译协议不认识业务和jt-server装下全部业务不碰一个字节。它们之间唯一的对话方式是 RocketMQ 信封。这篇的内容比较硬核Netty 管线怎么搭、为什么要绕一圈 MQ、下行指令怎么路由到正确的网关实例、1078 流媒体网关为什么有六个端口、94 张表怎么组织。所有设计都有对应的真实教训不是教科书照搬。一、模块怎么切协议面与业务面严格分离图2-1 业务架构图整个 JT 后端 两个自研模块 两个公共模块边界划得非常洁癖协议接入面jt-gateway三个独立进程一个协议一个808 网关Netty 长连接接入 → 拆包 → 转义还原 → 校验和 → 解码 → 30 消息处理器分发。它知道 0x0200 是定位但不知道定位数据最终进了哪张表1078 流媒体网关RTP 收流 → 解复用 → 转码/转封装 FLV → 按端分发。它知道怎么把一帧 H.265 喂给浏览器但不知道是谁在看809 网关作为客户端连上级监管平台主从双链路、双版本兼容、断链自动重连补传业务面jt-server一个服务装四大域基础域车辆档案/组织树/设备台账、核心域轨迹/监控/报警/围栏、动作域指令/视频/推送、外延域开放 API/报表/车务/809 转发策略。它消费协议对象产出业务结果从头到尾见不到字节流。中间靠两个公共模块粘合jt-core契约模块——MQ 信封结构、Topic 命名、Redis Key 规范全定义在这。网关和业务都只依赖它互不依赖jt-codec编解码唯一实现零三方依赖的纯 jar被一个依赖扫描单测把守红线这套切法的好处在上篇说过发版不断连、独立扩容这篇讲它具体怎么运转。二、808 网关内部一条报文的 IO 之旅808 网关的 Netty 管线每个 handler 职责单一顺序有讲究帧解码器按0x7e标志位拆包。TCP 是流协议一条报文可能被切成两个包、也可能三条报文粘在一个包里这层负责还原出完整帧转义还原808 规定报文体里出现0x7e/0x7d要转义成两字节序列这层把它们还原回去。顺序不能反——必须先拆包再反转义校验和验证从消息头到消息尾逐字节异或对不上直接丢弃并计数。别心疼丢帧校验不过的帧解析下去只会污染更下游codec 解码校验通过的纯字节交给 jt-codec按消息 ID 找到对应的协议类注解驱动地解析成 Java 对象。三个协议版本2011/2013/2019的差异全部收敛在 codec 内部网关无感业务分发30 多个消息处理器按消息 ID 注册成分发表——定位走定位处理器、报警走报警处理器、应答走应答处理器。处理器的产出统一是包信封、投 MQ两道保险丝贯穿全程IO 线程零阻塞任何可能慢的操作一律扔进 MQ 或异步线程池慢 SQL、慢 HTTP 都不允许出现在管线上和空闲连接检测长时间没心跳的连接主动踢掉把文件描述符还给活连接。终端侧的两条入门手续也在网关完成注册0x0100新设备领鉴权码和鉴权0x0102每次上线验明正身。鉴权通过的那一刻网关做了一件影响全局的事——往 Redis 写一条会话tid → 本网关实例 ID。这条数据是第七节下行路由的根基。三、1078 流媒体网关平台的第二心脏图2-3 技术架构图如果说 808 网关管的是车的状态1078 网关管的就是车的眼睛。它是整个后端最重的进程六个端口各司其职端口职责6802实时视频收流终端主动推 RTP 上来6803历史录像回放收流6805双向对讲6899浏览器播放WebSocket-FLV6810小程序/H5 播放HTTP-FLV6809对内 APIjt-server 来要流、关流一路视频的生命周期是这样的调度员在页面上点开某辆车的摄像头 → jt-server 通过 6809 告诉流媒体网关给我拉这台车的 1 号通道 → 网关通过 808 链路向终端下发实时流请求 → 终端开始向 6802 推 RTP 流 → 网关解 RTP、取出 H.264/H.265 裸流、转封装成 FLV → 浏览器从 6899 拉 WS-FLV小程序从 6810 拉 HTTP-FLV。为什么收流和播放要分开因为一对多一路车载流可能同时被调度员的浏览器、安全员的小程序、还有录像存储三方消费收流端只收一份分发端各拉各的。视频流是最怕被忘了关的资源——一路流没人看还挂着带宽、内存、终端流量全在烧。所以有一条硬规则一路流 30 秒没有任何消费者自动回收同时给终端发停流指令。这条规则上线前测试环境一晚上被忘关的流吃掉了几十 GB 流量上线后这个问题彻底绝迹。四、809 网关向上级监管平台汇报809 是平台对平台协议我们的网关在监管平台面前是客户端。设计要点三个主从双链路主链路传定位、报警等业务数据从链路传链路检测和补传请求。监管平台就靠从链路判断你活着没有双版本兼容不同省份的监管平台 809 版本不一致网关内部做了版本适配对上呈现哪种版本可配置断链补传网络抖一下数据就缺一段监管会考核。掉线期间的定位数据进补传队列链路恢复后自动追平809 网关没什么性能压力它的难点全在运维韧性——重连、补传、对账全是状态机细节。五、MQ 信封跨进程的唯一语言网关和业务两个进程之间只允许一种东西流动统一信封 JtMessageEnvelope。它的关键字段字段作用msgId协议消息 ID如 0x0200tid终端设备号网关实例 ID这条消息从哪个网关实例来的 / 要发给哪个网关实例payload解码后的协议对象业务方永远见不到字节租户多租户隔离Topic 规划分两类上行类按实例 消息类型命名如tp_{实例}_0x0200。定位这种海量消息按网关实例分 topic消费端可以按实例并行扩容互不抢下行类统一jt-downstream信封里带目标实例 ID所有网关都订阅只有实例 ID 匹配的才真正下发其余直接丢弃为什么下行要广播 过滤而不是定向投递因为简单可靠订阅关系固定不用维护哪类消息去哪个 topic的路由表网关实例上下线时订阅关系自动就位不需要额外的注册逻辑。代价是多投递了几份无用消息——相对定位上行的量级下行的这点浪费可以忽略。六、上行全链路为什么 MQ 回环是线程模型的一部分图2-2 流程架构图上行链路完整走一遍终端定位帧 → Netty IO 线程解码 → codec 注解解析 → 包信封 → MQ → 消费线程池三路并行轨迹入库 / 围栏计算 / WS 推送。很多人第一次看这个链路会问网关解码完直接调业务不行吗为什么非要绕一圈 RocketMQ因为MQ 在这里不是跨进程通信是线程模型的一部分。三个理由IO 线程必须零阻塞。Netty 的 IO 线程数量是按 CPU 核数配的一个线程要伺候上千个连接的读写事件。一次慢 SQL哪怕只要 200ms卡住 IO 线程这上千个连接的读事件全部延迟终端判断超时批量断线重连——上篇说的惊群就是这么来的削峰。早高峰全城车辆集中上线每秒数千条定位洪峰冲进来。没有队列缓冲业务线程池瞬间打满、拒绝、丢数据有了 MQ洪峰变平台消费端按自己的能力匀速拉三路并行。同一条定位要干三件事存历史慢写 PG、算围栏中等空间计算、推实时快写 WS。三条路耗时差一个数量级必须拆开并行消费谁也别拖累谁一句话Netty 管接得住MQ 管不堵车线程池管干得完。七、下行全链路Redis 会话 定向投递下行比上行难难在一个根本问题jt-server 想给终端发指令但它根本不知道这台终端的 TCP 连接挂在哪个网关实例上。解法三步上线记账终端鉴权通过时网关在 Redis 会话表写入tid → 网关实例 ID断线时删除。这张表永远反映此刻谁连在哪儿路由查询jt-server 组装好指令信封带上查出来的目标实例 ID投进jt-downstreamtopic过滤下发所有网关实例都消费这个 topic信封里的实例 ID 和自己的对上就真正下发对不上直接丢弃。终端应答0x0001通用应答或带结果的拍照应答走上行链路回来落库指令台账这套机制带来一个免费的福利水平扩容。车多了要加网关实例只需要启动新实例、注册到 Nacos新连上来的终端自然会把会话写到新实例名下下行路由自动生效——不需要改任何配置不需要数据迁移。八、数据设计信封、Redis 键、月分区、双轨 ORM图2-4 数据架构图数据层四个关键决策每个都对应一类真实事故1. 统一信封消灭方言。早期没有信封概念网关给业务传数据各传各的字段名、单位、坐标口径全靠口头约定接一个新消息类型要两边对着改。信封统一后新增消息类型 codec 里加一个协议类 业务侧加一个消费者网关代码一行不动。2. Redis 键分两类各管一件事。终端会话键管下行路由第七节最后位置快照键管看车不查库上篇第四节。两类键的 TTL 策略、淘汰策略完全不同绝不混用。3. PG 月分区管住数据量。轨迹、报警两张表按月分区jt_track_202607、jt_track_202608……XXL-Job 每月 1 号凌晨自动预建下月分区永不断档查询历史轨迹按时间范围自动只扫相关分区超期分区整月 detach 归档。对比传统的DELETE WHERE create_time ...快几个数量级、不产生死元组、不撑爆 WAL。4. ORM 双轨制。低频 CRUD档案、组织、规则一天几百次走 MyBatis-Plus保留租户拦截器和 CRUD 脚手架高频写入轨迹每秒数百上千条走 JdbcTemplate 直写绕过整个拦截器链。工具没有高低级匹配频率的才是对的。九、高可用每个组件怎么挂了也不出事808/1078 网关无状态多实例。挂一个实例终端 TCP 断开自动重连到存活实例新会话重新写 Redis分钟级自愈。唯一代价是重连瞬间的小洪峰MQ 正好接住jt-server无状态多实例MQ 消费天然负载均衡挂一个实例消费组自动重平衡RocketMQ / Redis / PG走中间件自己的高可用方案主从/哨兵/流复制不在应用层发明轮子809 链路断链自动重连 补传队列兜底监管侧无感知原则一句话自研的部分全部无状态有状态的事全交给专业中间件。十、运维与排障经验三个真实好用的手段信封就是最好的日志。全链路只认信封排查某台车某时刻的数据到哪了拿 tid 时间点顺着网关日志 → MQ 消息轨迹 → 消费日志 → 台账一路查下去五分钟定位。链路里每多一种数据格式排障成本翻一倍分区表预建要监控。XXL-Job 预建分区这事忘了就出人命跨月那一刻写入全失败所以给预建任务本身加了告警——调度器也可能挂兜底的任务也要有兜底压测用模拟器别用真车。多车压测场景全部跑在 PC 模拟器上第 3 篇细讲几千台虚拟终端并发上报削峰、分区写入、WS 推送全链路压到目标水位才敢上线十一、本篇小结后端的核心思想一句话协议面/业务面分离管住复杂度MQ 信封管住通信Redis 会话管住路由月分区管住数据量。四个管住背后是四次真实事故的学费。下篇《前端与测试工程篇》数据到达前端之后的故事——上万辆车在地图上实时动是怎么渲染不卡的自研 WS 协议长什么样没有真车怎么做联调和验收两台以假乱真的终端模拟器是答案。文中部署地址、密钥、域名等敏感信息均已脱敏架构图为作者基于实际项目整理绘制。
返回列表