@@ -0,0 +1,759 @@
<!doctype html>
< html lang = "zh-CN" >
< head >
< meta charset = "utf-8" / >
< meta name = "viewport" content = "width=device-width, initial-scale=1" / >
< title > Lingniu Vehicle Ingest Go Version Data Flow< / title >
< style >
: root {
--bg : #f6f7f9 ;
--ink : #17202a ;
--muted : #5f6b7a ;
--line : #ccd3dd ;
--panel : #ffffff ;
--blue : #1f6feb ;
--green : #16803c ;
--amber : #a15c00 ;
--red : #c23131 ;
--purple : #7c3aed ;
--teal : #087f8c ;
--shadow : 0 10 px 30 px rgba ( 25 , 33 , 45 , .08 ) ;
}
* { box-sizing : border-box ; }
body {
margin : 0 ;
color : var ( - - ink ) ;
background : var ( - - bg ) ;
font : 14 px / 1.55 - apple-system , BlinkMacSystemFont , "Segoe UI" , Arial , "PingFang SC" , "Microsoft YaHei" , sans-serif ;
}
header {
padding : 28 px 34 px 22 px ;
background : #111827 ;
color : white ;
}
h1 {
margin : 0 0 8 px ;
font-size : 28 px ;
line-height : 1.2 ;
letter-spacing : 0 ;
}
. subtitle {
max-width : 1180 px ;
color : #d1d5db ;
font-size : 15 px ;
}
main {
width : min ( 1480 px , calc ( 100 vw - 36 px ) ) ;
margin : 22 px auto 48 px ;
}
h2 {
margin : 0 0 14 px ;
font-size : 20 px ;
line-height : 1.25 ;
}
h3 {
margin : 0 0 10 px ;
font-size : 15 px ;
}
. section {
margin : 18 px 0 ;
padding : 20 px ;
background : var ( - - panel ) ;
border : 1 px solid #e4e8ef ;
border-radius : 8 px ;
box-shadow : var ( - - shadow ) ;
}
. legend {
display : flex ;
flex-wrap : wrap ;
gap : 10 px ;
margin-top : 16 px ;
}
. tag {
display : inline-flex ;
align-items : center ;
gap : 7 px ;
padding : 4 px 8 px ;
border : 1 px solid #d8dee8 ;
border-radius : 6 px ;
background : #fff ;
color : var ( - - muted ) ;
font-size : 12 px ;
}
. dot {
width : 10 px ;
height : 10 px ;
border-radius : 50 % ;
background : var ( - - blue ) ;
}
. dot . source { background : var ( - - amber ) ; }
. dot . gateway { background : var ( - - blue ) ; }
. dot . bus { background : var ( - - purple ) ; }
. dot . store { background : var ( - - green ) ; }
. dot . api { background : var ( - - teal ) ; }
. dot . warning { background : var ( - - red ) ; }
. flow {
display : grid ;
grid-template-columns : 1.15 fr 1.35 fr 1.15 fr 1.2 fr 1.4 fr 1.1 fr ;
gap : 14 px ;
align-items : stretch ;
overflow-x : auto ;
padding-bottom : 8 px ;
}
. lane {
min-width : 190 px ;
display : flex ;
flex-direction : column ;
gap : 12 px ;
}
. lane-title {
padding : 8 px 10 px ;
border-radius : 6 px ;
color : white ;
font-weight : 700 ;
text-align : center ;
background : #374151 ;
}
. node {
position : relative ;
min-height : 88 px ;
padding : 13 px 13 px 12 px ;
border : 1 px solid #d9e0ea ;
border-left : 5 px solid var ( - - blue ) ;
border-radius : 8 px ;
background : #fff ;
}
. node . source { border-left-color : var ( - - amber ) ; }
. node . gateway { border-left-color : var ( - - blue ) ; }
. node . bus { border-left-color : var ( - - purple ) ; }
. node . store { border-left-color : var ( - - green ) ; }
. node . api { border-left-color : var ( - - teal ) ; }
. node . warning { border-left-color : var ( - - red ) ; }
. node strong {
display : block ;
margin-bottom : 5 px ;
font-size : 14 px ;
}
. node p {
margin : 0 ;
color : var ( - - muted ) ;
font-size : 12 px ;
}
. arrow {
display : flex ;
align-items : center ;
justify-content : center ;
height : 28 px ;
color : var ( - - muted ) ;
font-size : 12 px ;
white-space : nowrap ;
}
. arrow : before , . arrow : after {
content : "" ;
height : 1 px ;
background : var ( - - line ) ;
flex : 1 ;
margin : 0 8 px ;
}
. arrow : after {
max-width : 16 px ;
height : 0 ;
border-top : 5 px solid transparent ;
border-bottom : 5 px solid transparent ;
border-left : 8 px solid var ( - - line ) ;
background : transparent ;
margin-left : 0 ;
}
. grid-2 {
display : grid ;
grid-template-columns : repeat ( 2 , minmax ( 0 , 1 fr ) ) ;
gap : 14 px ;
}
. grid-3 {
display : grid ;
grid-template-columns : repeat ( 3 , minmax ( 0 , 1 fr ) ) ;
gap : 14 px ;
}
table {
width : 100 % ;
border-collapse : collapse ;
overflow : hidden ;
border : 1 px solid #e1e6ef ;
border-radius : 8 px ;
background : white ;
}
th , td {
padding : 10 px 11 px ;
border-bottom : 1 px solid #edf1f6 ;
text-align : left ;
vertical-align : top ;
font-size : 13 px ;
}
th {
background : #f0f4f8 ;
color : #263241 ;
font-weight : 700 ;
}
tr : last-child td { border-bottom : 0 ; }
code {
padding : 1 px 5 px ;
border-radius : 4 px ;
background : #eef2f7 ;
color : #1f2937 ;
font-family : ui-monospace , SFMono-Regular , Menlo , Consolas , monospace ;
font-size : 12 px ;
}
. pill {
display : inline-block ;
margin : 2 px 4 px 2 px 0 ;
padding : 2 px 7 px ;
border-radius : 999 px ;
background : #eef2ff ;
color : #3730a3 ;
font-size : 12 px ;
white-space : nowrap ;
}
. note {
padding : 11 px 12 px ;
border-left : 4 px solid var ( - - amber ) ;
background : #fff7ed ;
color : #61410b ;
border-radius : 6 px ;
}
. ok {
padding : 11 px 12 px ;
border-left : 4 px solid var ( - - green ) ;
background : #effaf1 ;
color : #164b25 ;
border-radius : 6 px ;
}
. mini-flow {
display : grid ;
grid-template-columns : repeat ( 7 , minmax ( 128 px , 1 fr ) ) ;
gap : 8 px ;
overflow-x : auto ;
padding-bottom : 8 px ;
}
. mini-step {
min-height : 82 px ;
padding : 10 px ;
border : 1 px solid #dfe6f1 ;
border-radius : 8 px ;
background : #fff ;
}
. mini-step b {
display : block ;
margin-bottom : 4 px ;
color : #111827 ;
font-size : 13 px ;
}
. mini-step span {
color : var ( - - muted ) ;
font-size : 12 px ;
}
. footer {
color : var ( - - muted ) ;
font-size : 12 px ;
text-align : center ;
padding : 20 px ;
}
@ media ( max-width : 980px ) {
. grid-2 , . grid-3 { grid-template-columns : 1 fr ; }
header { padding : 22 px 18 px ; }
main { width : calc ( 100 vw - 20 px ) ; }
. section { padding : 14 px ; }
}
< / style >
< / head >
< body >
< header >
< h1 > Go 版本车辆数据处理与流转全图< / h1 >
< div class = "subtitle" >
当前 Go 架构以 gateway 做高性能协议接入, NATS JetStream 做入口缓冲和解耦, nats-kafka-bridge 保持现有 Kafka 下游兼容,最终进入 TDengine、Redis、MySQL, 并由 realtime-api 对外提供查询能力。
< / div >
< div class = "legend" >
< span class = "tag" > < span class = "dot source" > < / span > 外部数据源< / span >
< span class = "tag" > < span class = "dot gateway" > < / span > Go 接入与解析< / span >
< span class = "tag" > < span class = "dot bus" > < / span > NATS / Kafka 解耦< / span >
< span class = "tag" > < span class = "dot store" > < / span > 存储< / span >
< span class = "tag" > < span class = "dot api" > < / span > 查询 API< / span >
< span class = "tag" > < span class = "dot warning" > < / span > 可靠性边界< / span >
< / div >
< / header >
< main >
< section class = "section" >
< h2 > 1. 总览图:从车辆平台到 API< / h2 >
< div class = "flow" >
< div class = "lane" >
< div class = "lane-title" > 外部输入< / div >
< div class = "node source" >
< strong > GB/T 32960 平台< / strong >
< p > TCP 连接到 ECS < code > :32960< / code > ,包含登录、登出、实时信息、补发等帧。< / p >
< / div >
< div class = "node source" >
< strong > JT/T 808 平台< / strong >
< p > TCP 连接到 ECS < code > :808< / code > ,包含注册、鉴权、位置上报 < code > 0x0200< / code > 等。< / p >
< / div >
< div class = "node source" >
< strong > 宇通 MQTT< / strong >
< p > gateway 作为 MQTT client 订阅 < code > /ytforward/shln/+< / code > 。< / p >
< / div >
< / div >
< div class = "lane" >
< div class = "lane-title" > gateway 接入层< / div >
< div class = "node gateway" >
< strong > TCPServer / MQTTClient< / strong >
< p > 读取连接、拆包、处理半包/粘包、按协议路由到解析器。< / p >
< / div >
< div class = "node gateway" >
< strong > 协议解析器< / strong >
< p > < code > gb32960< / code > 、< code > jt808< / code > 、< code > yutongmqtt< / code > 解析 RAW, 抽取核心 fields。< / p >
< / div >
< div class = "node gateway" >
< strong > 身份解析< / strong >
< p > 通过 MySQL < code > vehicle_identity_binding< / code > 将 phone/device/plate 尽量映射到 VIN。< / p >
< / div >
< div class = "node gateway" >
< strong > 协议响应< / strong >
< p > 32960/808 需要应答时,在成功发布后写 ACK。< / p >
< / div >
< / div >
< div class = "lane" >
< div class = "lane-title" > 统一消息< / div >
< div class = "node gateway" >
< strong > FrameEnvelope< / strong >
< p > 统一封装: protocol、message_id、vin、phone、device_id、source_endpoint、raw、parsed、fields。< / p >
< / div >
< div class = "node warning" >
< strong > StableEventID< / strong >
< p > 基于协议、消息、车辆键、序号、时间、raw 生成稳定 ID, 用于去重和追踪。< / p >
< / div >
< / div >
< div class = "lane" >
< div class = "lane-title" > 入口解耦< / div >
< div class = "node bus" >
< strong > NATS JetStream< / strong >
< p > gateway 发布到 < code > VEHICLE_INGEST< / code > stream, 文件存储, 24 小时保留。< / p >
< / div >
< div class = "node bus" >
< strong > Subjects< / strong >
< p > < code > vehicle.raw.go.gb32960.v1< / code > < br > < code > vehicle.raw.go.jt808.v1< / code > < br > < code > vehicle.raw.go.yutong-mqtt.v1< / code > < br > < code > vehicle.event.go.unified.v1< / code > < / p >
< / div >
< div class = "node warning" >
< strong > Async + Retry< / strong >
< p > gateway 内部异步队列发布 NATS, 降低协议连接被 Kafka 慢写拖住的风险。< / p >
< / div >
< / div >
< div class = "lane" >
< div class = "lane-title" > Kafka 兼容层< / div >
< div class = "node bus" >
< strong > nats-kafka-bridge< / strong >
< p > durable pull consumer 批量拉 NATS, 写 Kafka 成功后才 ACK NATS。< / p >
< / div >
< div class = "node bus" >
< strong > Kafka Topics< / strong >
< p > 与 NATS subject 同名: raw 三个协议 + unified 一个实时主题。< / p >
< / div >
< div class = "node warning" >
< strong > 失败语义< / strong >
< p > Kafka 写失败不 ACK, NATS 保留消息等待重试;未知 subject 不 ACK, 防止静默丢数据。< / p >
< / div >
< / div >
< div class = "lane" >
< div class = "lane-title" > 落库与查询< / div >
< div class = "node store" >
< strong > history-writer< / strong >
< p > 消费 RAW topic, 写 TDengine: raw_frames、payload_chunks、locations、mileage_points。< / p >
< / div >
< div class = "node store" >
< strong > stat-writer< / strong >
< p > 消费 RAW topic, 根据总里程差值写 MySQL 每日指标。< / p >
< / div >
< div class = "node store" >
< strong > realtime-api 消费器< / strong >
< p > 消费 unified topic, 写 Redis 在线状态、实时合并快照、各协议 realtime-raw。< / p >
< / div >
< div class = "node api" >
< strong > HTTP API< / strong >
< p > realtime-api 暴露实时、历史、统计查询接口,端口 < code > :20200< / code > 。< / p >
< / div >
< / div >
< / div >
< / section >
< section class = "section" >
< h2 > 2. 单条报文的完整生命周期< / h2 >
< div class = "mini-flow" >
< div class = "mini-step" > < b > 1. 到达< / b > < span > 32960/808 TCP 报文或 MQTT 消息到 gateway。< / span > < / div >
< div class = "mini-step" > < b > 2. 拆包< / b > < span > TCP 按协议提取完整帧; MQTT 直接按消息处理。< / span > < / div >
< div class = "mini-step" > < b > 3. 解析< / b > < span > 生成 parsed 全量结构化数据和 fields 核心字段。< / span > < / div >
< div class = "mini-step" > < b > 4. 绑定 VIN< / b > < span > 根据 VIN/phone/device_id/plate 生成 vehicle_key。< / span > < / div >
< div class = "mini-step" > < b > 5. 发 NATS< / b > < span > 写 RAW subject; 同时写 unified subject。< / span > < / div >
< div class = "mini-step" > < b > 6. 桥接 Kafka< / b > < span > bridge 写 Kafka 成功后 ACK NATS。< / span > < / div >
< div class = "mini-step" > < b > 7. 多路消费< / b > < span > 历史、实时、统计各自消费 Kafka, 互不阻塞。< / span > < / div >
< / div >
< / section >
< section class = "section" >
< h2 > 3. FrameEnvelope: Go 版本内部统一数据格式< / h2 >
< div class = "grid-2" >
< div >
< table >
< thead >
< tr > < th > 字段< / th > < th > 含义< / th > < th > 来源< / th > < / tr >
< / thead >
< tbody >
< tr > < td > < code > event_id< / code > < / td > < td > 稳定事件 ID< / td > < td > 已有则保留,否则按协议/消息/车辆/时间/raw 计算< / td > < / tr >
< tr > < td > < code > protocol< / code > < / td > < td > 协议< / td > < td > < code > GB32960< / code > 、< code > JT808< / code > 、< code > YUTONG_MQTT< / code > < / td > < / tr >
< tr > < td > < code > message_id< / code > < / td > < td > 协议消息类型< / td > < td > 如 808 < code > 0x0200< / code > , 32960 命令标识等< / td > < / tr >
< tr > < td > < code > vin< / code > < / td > < td > VIN< / td > < td > 协议原始字段或 MySQL binding 反查< / td > < / tr >
< tr > < td > < code > phone< / code > < / td > < td > 808 终端手机号< / td > < td > 808 消息头 BCD 解析后去前导 0< / td > < / tr >
< tr > < td > < code > device_id< / code > < / td > < td > 设备标识< / td > < td > 协议内设备号、MQTT 设备字段等< / td > < / tr >
< tr > < td > < code > plate< / code > < / td > < td > 车牌< / td > < td > 注册帧或 binding 表< / td > < / tr >
< tr > < td > < code > source_endpoint< / code > < / td > < td > 来源地址< / td > < td > TCP remote ip:port 或 MQTT endpoint/topic< / td > < / tr >
< / tbody >
< / table >
< / div >
< div >
< table >
< thead >
< tr > < th > 字段< / th > < th > 含义< / th > < th > 去向< / th > < / tr >
< / thead >
< tbody >
< tr > < td > < code > raw_hex< / code > / < code > raw_text< / code > < / td > < td > 原始报文< / td > < td > TDengine < code > raw_frames< / code > ,超长进入 chunks< / td > < / tr >
< tr > < td > < code > parsed< / code > < / td > < td > 全量解析 JSON< / td > < td > TDengine < code > parsed_json< / code > ; Redis < code > realtime-raw< / code > < / td > < / tr >
< tr > < td > < code > fields< / code > < / td > < td > 最小核心字段< / td > < td > TDengine 位置/里程点; Redis 合并快照;统计计算< / td > < / tr >
< tr > < td > < code > event_time_ms< / code > < / td > < td > 车端事件时间< / td > < td > TDengine 主时间; Redis field time< / td > < / tr >
< tr > < td > < code > received_at_ms< / code > < / td > < td > 平台接收时间< / td > < td > 延迟分析、在线状态 last_seen< / td > < / tr >
< tr > < td > < code > parse_status< / code > < / td > < td > 解析状态< / td > < td > < code > OK< / code > 、< code > PARTIAL< / code > 、< code > BAD_FRAME< / code > < / td > < / tr >
< tr > < td > < code > parse_error< / code > < / td > < td > 解析错误< / td > < td > RAW 查询排查使用< / td > < / tr >
< tr > < td > < code > vehicle_key< / code > < / td > < td > 车辆主键< / td > < td > 优先 VIN; 否则 < code > PROTOCOL:phone/device< / code > < / td > < / tr >
< / tbody >
< / table >
< / div >
< / div >
< / section >
< section class = "section" >
< h2 > 4. 三个协议的处理差异< / h2 >
< table >
< thead >
< tr >
< th > 协议< / th >
< th > 接入方式< / th >
< th > 身份主线< / th >
< th > RAW 数据< / th >
< th > 核心 fields< / th >
< th > 特殊动作< / th >
< / tr >
< / thead >
< tbody >
< tr >
< td > < strong > GB32960< / strong > < / td >
< td > gateway TCP < code > :32960< / code > < / td >
< td > 报文本身通常有 VIN; 平台账号用于登录鉴权< / td >
< td > 完整 parsed JSON 写 < code > raw_frames.parsed_json< / code > < / td >
< td > 速度、经纬度、SOC、累计里程等按帧类型抽取< / td >
< td > 登录/数据帧需要平台应答,成功发布后 ACK< / td >
< / tr >
< tr >
< td > < strong > JT808< / strong > < / td >
< td > gateway TCP < code > :808< / code > < / td >
< td > 消息头 phone 为主;注册/鉴权/位置帧结合 binding 找 VIN< / td >
< td > 完整 parsed JSON 写 < code > raw_frames.parsed_json< / code > < / td >
< td > 位置、速度、方向、状态、报警、GPS 总里程< / td >
< td > 注册/鉴权/通用应答;每日里程按总里程差值统计< / td >
< / tr >
< tr >
< td > < strong > YUTONG_MQTT< / strong > < / td >
< td > gateway MQTT client 订阅< / td >
< td > MQTT payload 中 VIN/设备字段; source endpoint 为 MQTT endpoint/topic< / td >
< td > 完整 parsed JSON 写 < code > raw_frames.parsed_json< / code > < / td >
< td > 速度、经纬度、总里程、SOC 等< / td >
< td > 不需要 TCP ACK; 实时多帧在 Redis 按协议合并< / td >
< / tr >
< / tbody >
< / table >
< / section >
< section class = "section" >
< h2 > 5. NATS + Kafka 的职责分工< / h2 >
< div class = "grid-3" >
< div class = "node bus" >
< strong > NATS JetStream: 入口缓冲层< / strong >
< p > 部署在 Kafka ECS, Docker Compose 管理;监听内网 < code > 172.17.111.56:4222< / code > ,监控 < code > 172.17.111.56:8222< / code > 。gateway 只需要把事件快速、可靠地写入 NATS。< / p >
< / div >
< div class = "node bus" >
< strong > nats-kafka-bridge: 可靠桥接层< / strong >
< p > 使用 durable pull consumer; 批量写 Kafka; 只有 Kafka 写入成功才 ACK NATS。Kafka 短暂异常时, NATS 消息保留并重试。< / p >
< / div >
< div class = "node bus" >
< strong > Kafka: 业务消费层< / strong >
< p > 保持现有 topic 和消费者模型, history/realtime/stat 不直接依赖 gateway, 也不直接依赖 NATS API。< / p >
< / div >
< / div >
< div class = "note" style = "margin-top:14px;" >
当前阶段没有让下游直接消费 NATS, 这是为了最小化切换风险: 入口换成 NATS 后,下游仍沿用 Kafka。后续如果要进一步实时化, 可以让实时缓存或控制命令直接使用 NATS subject。
< / div >
< / section >
< section class = "section" >
< h2 > 6. 存储模型:哪些数据进入哪里< / h2 >
< table >
< thead >
< tr >
< th > 存储< / th >
< th > 表/Key< / th >
< th > 写入方< / th >
< th > 内容< / th >
< th > 用途< / th >
< / tr >
< / thead >
< tbody >
< tr >
< td > < strong > TDengine< / strong > < / td >
< td > < code > raw_frames< / code > < / td >
< td > history-writer< / td >
< td > 原始报文、完整 parsed JSON、fields JSON、parse status、source endpoint< / td >
< td > RAW 查询、追溯、排查解析问题< / td >
< / tr >
< tr >
< td > < strong > TDengine< / strong > < / td >
< td > < code > raw_frame_payload_chunks< / code > < / td >
< td > history-writer< / td >
< td > 超长 raw/parsed payload 分片< / td >
< td > 避免 BINARY 长度限制导致完整 JSON 丢失< / td >
< / tr >
< tr >
< td > < strong > TDengine< / strong > < / td >
< td > < code > vehicle_locations< / code > < / td >
< td > history-writer< / td >
< td > 经纬度、速度、方向、状态、报警、总里程等核心字段< / td >
< td > 高频历史位置分页查询< / td >
< / tr >
< tr >
< td > < strong > TDengine< / strong > < / td >
< td > < code > vehicle_mileage_points< / code > < / td >
< td > history-writer< / td >
< td > 累计里程点、速度、经纬度< / td >
< td > 里程曲线、区间差值分析< / td >
< / tr >
< tr >
< td > < strong > Redis< / strong > < / td >
< td > < code > vehicle:realtime:{vehicle_key}< / code > < / td >
< td > realtime-api Kafka consumer< / td >
< td > 跨协议合并后的实时字段快照< / td >
< td > 查 VIN 是否在线、查 VIN 实时数据< / td >
< / tr >
< tr >
< td > < strong > Redis< / strong > < / td >
< td > < code > vehicle:realtime:{vehicle_key}:{protocol}< / code > < / td >
< td > realtime-api Kafka consumer< / td >
< td > 单协议实时快照< / td >
< td > 区分 32960/808/MQTT 的实时状态< / td >
< / tr >
< tr >
< td > < strong > Redis< / strong > < / td >
< td > < code > vehicle:realtime-raw:{vehicle_key}:{protocol}< / code > < / td >
< td > realtime-api Kafka consumer< / td >
< td > 单协议最新 parsed 全量字段< / td >
< td > 实时 RAW 字段查看,不走 TDengine 历史扫描< / td >
< / tr >
< tr >
< td > < strong > Redis< / strong > < / td >
< td > < code > vehicle:online:{vehicle_key}< / code > 、< code > vehicle:last_seen< / code > < / td >
< td > realtime-api Kafka consumer< / td >
< td > 在线状态、最后接收时间、协议列表< / td >
< td > 在线判断和近期车辆列表< / td >
< / tr >
< tr >
< td > < strong > MySQL< / strong > < / td >
< td > < code > vehicle_identity_binding< / code > < / td >
< td > 人工/导入维护, gateway 读取< / td >
< td > phone/device/plate 到 VIN 的映射< / td >
< td > 808 等缺 VIN 协议反查正确 VIN< / td >
< / tr >
< tr >
< td > < strong > MySQL< / strong > < / td >
< td > < code > jt808_registration< / code > < / td >
< td > gateway< / td >
< td > 808 注册、鉴权、首次/最新上报、phone、device、plate、vin 解析结果< / td >
< td > 定位哪些手机号没有映射 VIN, 排查注册鉴权< / td >
< / tr >
< tr >
< td > < strong > MySQL< / strong > < / td >
< td > < code > vehicle_daily_metric< / code > < / td >
< td > stat-writer< / td >
< td > 每日里程等指标,按总里程差值计算< / td >
< td > 统计查询< / td >
< / tr >
< / tbody >
< / table >
< / section >
< section class = "section" >
< h2 > 7. 查询入口< / h2 >
< div class = "grid-2" >
< div >
< h3 > 实时查询< / h3 >
< table >
< thead > < tr > < th > 接口类型< / th > < th > 数据源< / th > < th > 说明< / th > < / tr > < / thead >
< tbody >
< tr > < td > VIN 是否在线< / td > < td > Redis online key< / td > < td > TTL 内有数据即在线,默认 TTL 600 秒。< / td > < / tr >
< tr > < td > VIN 实时数据< / td > < td > Redis merged snapshot< / td > < td > 跨协议合并 fields, 并保留各协议 ProtocolData。< / td > < / tr >
< tr > < td > 单协议实时 RAW< / td > < td > Redis realtime-raw< / td > < td > 查看某 VIN/phone 在某协议下最新 parsed 全量字段。< / td > < / tr >
< / tbody >
< / table >
< / div >
< div >
< h3 > 历史查询< / h3 >
< table >
< thead > < tr > < th > 接口类型< / th > < th > 数据源< / th > < th > 说明< / th > < / tr > < / thead >
< tbody >
< tr > < td > RAW 帧查询< / td > < td > TDengine raw_frames + chunks< / td > < td > 按协议、VIN/phone、时间、消息类型分页。< / td > < / tr >
< tr > < td > 位置历史< / td > < td > TDengine vehicle_locations< / td > < td > 高频位置分页查询,避免每次扫完整 JSON。< / td > < / tr >
< tr > < td > 里程点< / td > < td > TDengine vehicle_mileage_points< / td > < td > 区间里程、曲线、异常总里程排查。< / td > < / tr >
< tr > < td > 每日指标< / td > < td > MySQL vehicle_daily_metric< / td > < td > 按日期、协议、指标查询统计结果。< / td > < / tr >
< / tbody >
< / table >
< / div >
< / div >
< / section >
< section class = "section" >
< h2 > 8. 生产部署视图< / h2 >
< table >
< thead >
< tr > < th > 节点< / th > < th > 组件< / th > < th > 地址/端口< / th > < th > 说明< / th > < / tr >
< / thead >
< tbody >
< tr >
< td > Gateway ECS< / td >
< td > < code > lingniu-go-gateway< / code > < / td >
< td > 公网 < code > 115.29.187.205< / code > ;生产端口 < code > 32960< / code > 、< code > 808< / code > < / td >
< td > 承接外部平台真实连接,发布到 NATS。< / td >
< / tr >
< tr >
< td > Gateway ECS< / td >
< td > < code > lingniu-go-nats-kafka-bridge< / code > < / td >
< td > 连接 NATS < code > 172.17.111.56:4222< / code > ; Kafka < code > 172.17.111.56:9092< / code > < / td >
< td > 当前部署在 gateway ECS, 负责 NATS 到 Kafka 桥接。< / td >
< / tr >
< tr >
< td > Gateway ECS< / td >
< td > < code > history-writer< / code > 、< code > stat-writer< / code > 、< code > realtime-api< / code > < / td >
< td > API < code > 115.29.187.205:20200< / code > < / td >
< td > 消费 Kafka, 写 TDengine/Redis/MySQL, 并提供查询。< / td >
< / tr >
< tr >
< td > Kafka ECS< / td >
< td > Kafka + NATS< / td >
< td > 公网 < code > 114.55.58.251< / code > ;内网 < code > 172.17.111.56< / code > < / td >
< td > NATS 使用 Docker Compose, 端口只绑定内网 < code > 4222< / code > /< code > 8222< / code > 。< / td >
< / tr >
< tr >
< td > TDengine ECS< / td >
< td > TDengine< / td >
< td > 内网 < code > 172.17.111.57:6041< / code > < / td >
< td > 历史 RAW、位置、里程点时序存储。< / td >
< / tr >
< tr >
< td > 云服务< / td >
< td > MySQL / Redis< / td >
< td > RDS 内网、Redis 内网< / td >
< td > 身份绑定、808 注册、每日指标、实时缓存。< / td >
< / tr >
< / tbody >
< / table >
< / section >
< section class = "section" >
< h2 > 9. 关键可靠性设计< / h2 >
< div class = "grid-3" >
< div class = "ok" >
< strong > 接入不直接等待 Kafka< / strong > < br >
gateway 先写 NATS, Kafka 慢写由 bridge 消化,降低 32960/808 连接积压风险。
< / div >
< div class = "ok" >
< strong > NATS ACK 在 Kafka 成功之后< / strong > < br >
bridge 写 Kafka 失败时不 ACK, 消息仍在 JetStream, 恢复后继续拉取。
< / div >
< div class = "ok" >
< strong > RAW 完整 JSON 保留< / strong > < br >
< code > parsed< / code > 全量写 RAW, 核心表只存查询常用字段, 避免位置查询每次扫完整 JSON。
< / div >
< div class = "ok" >
< strong > 实时多协议合并< / strong > < br >
Redis 同时保留 merged snapshot、protocol snapshot、realtime-raw, 既能看统一实时, 也能追单协议原始字段。
< / div >
< div class = "ok" >
< strong > 身份解析降级< / strong > < br >
VIN 优先,其次 phone、vehicle_key_hint、device_id, 保证无 VIN 时仍可临时归档和查询。
< / div >
< div class = "ok" >
< strong > 数据分层查询< / strong > < br >
高频查询走 TDengine 核心字段表和 Redis; 完整追溯走 RAW JSON, 性能和完整性分开处理。
< / div >
< / div >
< / section >
< section class = "section" >
< h2 > 10. 当前运行链路一句话< / h2 >
< p >
外部 32960/808/MQTT 数据进入 Go gateway 后,被解析成 < code > FrameEnvelope< / code > ,先写入 NATS JetStream;
< code > nats-kafka-bridge< / code > 把 NATS 消息可靠转写到 Kafka; Kafka 再分发给 < code > history-writer< / code > 写 TDengine、
< code > stat-writer< / code > 写 MySQL 每日指标、< code > realtime-api< / code > 写 Redis 实时缓存并提供 HTTP 查询。
< / p >
< / section >
< / main >
< div class = "footer" >
Generated for lingniu-vehicle-ingest Go architecture. Update this file when gateway subjects, storage tables, or deployment topology changes.
< / div >
< / body >
< / html >