Files
lingniu-vehicle-ingest/docs/ops/100k-capacity-baseline.md

16 KiB
Raw Blame History

100K 车辆接入容量基线

更新时间2026-07-13

目标

支撑 100,000 台车辆接入,入口服务在高连接数和高帧率下保持可观测、可背压、可恢复。

初始容量假设

目标
连接车辆数 100,000
平均帧间隔 10-30 秒
平均入口 FPS 3,000-10,000
短时 burst FPS 20,000
Gateway p99 parse + enqueue < 50ms
Redis 当前态延迟 < 3s
TDengine 历史写入 lag 可观测且 burst 后下降
MySQL 当前态 异步,不能阻塞 Redis

当前生产观测

当前 ECS 115.29.187.205 在低负载下健康:

  • JT808 连接约 242。
  • GB32960 连接约 2。
  • Kafka lag 为 0。
  • NATS ack pending 为 0。
  • Redis 写入约 0ms。
  • MySQL 投影约 1-2ms。
  • ECS load 约 0.09 / 0.15 / 0.21
  • 可用内存约 6.6GB。
  • 根分区使用约 37%。

当前已知缺口

  • net.core.somaxconn=128,无法作为 100K 连接生产基线。
  • Gateway 默认 TCP_MAX_CONNECTIONS 已调整为 120000,生产仍可通过环境变量覆盖。
  • 已新增可重复的 TCP 连接压测工具,后续需要用它跑 10K/50K/100K 阶段测试并记录结果。
  • 仍缺读超时按协议维度计数Gateway frame duration 已提供 histogram可用于 parse + enqueue + response 的 p95/p99 估算。
  • TDengine history writer 和 NATS fast writer 已使用 batch 写入;后续需要用带帧率压测验证 batch size、flush latency 和存储端承载能力。
  • History、realtime、stat、identity writer 均支持单进程多个同 group consumer默认分别为 HISTORY_WORKERS=3REALTIME_WORKERS=3STATS_WORKERS=3IDENTITY_WRITER_WORKERS=3。四者按 Kafka 分区并行且保持单车分区顺序;后续压测需联合观察 TDengine/MySQL 写延迟、连接数和各 consumer lag再决定是否增加 worker。
  • Kafka topic 当前 12 分区100K 目标下需要结合实际 FPS 再评估分区数。

ECS OS 参数建议

100K 连接目标需要至少以下系统参数作为起点:

sysctl -w net.core.somaxconn=65535
sysctl -w net.ipv4.tcp_max_syn_backlog=65535
sysctl -w net.ipv4.ip_local_port_range="10000 65000"
sysctl -w net.ipv4.tcp_tw_reuse=1

systemd gateway 已配置 LimitNOFILE=1048576,需要持续保持。

验收指标

指标 目标
Gateway vehicle_gateway_active_connections 能稳定到阶段目标连接数
Gateway vehicle_gateway_connection_closes_total{reason} read_error/extract_error/max_connections 不异常增长
Gateway vehicle_gateway_connection_rejections_total 容量目标内不增长
Gateway async sink vehicle_async_sink_queue_depth{sink} 队列深度不持续增长
Gateway async sink vehicle_async_sink_enqueue_total{sink,kind,status} timeout/closed 不增长
Gateway async sink vehicle_async_sink_publish_total{sink,kind,status} error 不增长
Gateway async sink vehicle_async_sink_publish_duration_ms_histogram_bucket p99 不持续上升
Gateway vehicle_gateway_publish_total{status="ok"} 持续增长
Gateway vehicle_gateway_frame_duration_ms_histogram_bucket p99 小于容量目标
History writer vehicle_history_batch_flush_total{status} error 不增长
History writer vehicle_history_batch_rows_total{status} batch 行数持续增长
History writer vehicle_history_batch_pending_messages 稳态接近 0burst 后下降
History writer vehicle_history_batch_pending_rows 稳态接近 0burst 后下降
History writer vehicle_history_batch_flush_duration_ms{status} flush 延迟不持续上升
History writer vehicle_history_config{setting="workers"} 不低于 3
History writer vehicle_history_worker_active 每个配置 worker 均为 1
NATS bridge vehicle_bridge_nats_consumer_ack_pending 稳态为 0
NATS bridge vehicle_bridge_nats_consumer_pending burst 后下降
NATS bridge vehicle_bridge_batch_pending_messages 稳态接近 0burst 后下降
NATS bridge vehicle_bridge_batch_duration_ms_histogram_bucket p99 不持续上升
NATS fast writer vehicle_fast_writer_nats_consumer_ack_pending 稳态为 0
NATS fast writer vehicle_fast_writer_nats_consumer_pending burst 后下降
NATS fast writer vehicle_fast_writer_batch_pending_messages 稳态接近 0burst 后下降
NATS fast writer vehicle_fast_writer_batch_pending_envelopes 稳态接近 0burst 后下降
NATS fast writer vehicle_fast_writer_stage_duration_ms_histogram_bucket TDengine/Redis/ack 各阶段 p99 不持续上升
Kafka consumers vehicle_*_kafka_lag 稳态为 0burst 后下降
Realtime Redis vehicle_realtime_store_update_duration_ms_histogram_bucket{store="redis"} p99 毫秒级且不持续上升
Realtime MySQL vehicle_realtime_store_update_duration_ms_histogram_bucket{store="mysql"} p99 不持续上升
Realtime MySQL async queue depth 不持续增长
Realtime MySQL vehicle_realtime_config{setting="workers"} 不低于 3
Realtime MySQL vehicle_realtime_worker_active 每个配置 worker 均为 1
Stat writer vehicle_stat_write_duration_ms_histogram_bucket p99 不持续上升
Stat writer vehicle_stat_config{setting="workers"} 不低于 3
Stat writer vehicle_stat_worker_active 每个配置 worker 均为 1
Identity writer vehicle_identity_writer_worker_active 每个配置 worker 均为 1

Retention Guardrails

10W 车辆接入时,中间件只承担解耦和短期缓冲职责,不能成为长期历史库。

Component Guardrail
NATS JetStream VEHICLE_INGEST NATS_STREAM_MAX_BYTES=21474836480NATS_STREAM_MAX_AGE_HOURS=24NATS_STREAM_ENSURE_TIMEOUT_SECONDS=60
Kafka vehicle.raw.go.* / vehicle.fields.go.* retention.ms=21600000segment.ms=600000segment.bytes=268435456
Gateway NATS publisher NATS_ASYNC_RAW_WORKERS=128,吸收平台按秒集中上报形成的瞬时突发;以 vehicle_async_sink_queue_wait_recent_p99_ms 校验,不按稳态 queue depth 猜测
Fast writer FAST_WRITER_WORKERS=16FAST_WRITER_BATCH_SIZE=100FAST_WRITER_FETCH_WAIT_MS=5FAST_WRITER_OPERATION_TIMEOUT_MS=1000;继续使用手工 ACK避免以可靠性换延迟
Root disk 使用率低于 80%,超过 85% 进入容量告警

如果 NATS consumer_pending 下降但仍高,说明系统在追历史积压;如果 ack_pending=0 且 Kafka lag 为 0,不要重启服务,重点观察 NATS data size 和 pending 下降斜率。

分阶段压测

压测入口:

# 100 连接 smoke test
/opt/lingniu-go-native/current/load-sim \
  -protocol jt808 \
  -addr 127.0.0.1:808 \
  -connections 100 \
  -connect-rate 100 \
  -send-interval 10s \
  -duration 2m \
  -template 0200 \
  -send=false

load-sim-send=true 时会按连接和帧序号生成可被现有解析器解析的唯一协议帧:

  • JT808 会变更包头手机号、流水号和 0x0200 位置时间,并重新计算转义和校验码。
  • GB32960 会变更 VIN 和实时数据时间,并重新计算 BCC。
  • JT808 发送模式默认持续读取服务端通用应答;禁止关闭 -drain-responses 后用应答写阻塞产生的延迟评价服务端性能。
  • JT808 使用 -cleanup-registration 时会在结束后只清理指定模拟手机号区间且来源为 loopback 的注册记录RAW 仍保留作为压测证据。每轮发送压测必须使用未在 identity-writer 10 分钟 touch 节流窗口内使用过的新号段,否则注册不会重复写入,rows_deleted 可以为 0
  • 低 FPS 全链路压测必须使用该模式,避免重复静态帧导致 event id 冲突。
  • 对生产端口执行 -send=true 会写入合成 raw 数据,只能在明确隔离标识和压测窗口后执行。
  1. 100 连接 smoke test。
  2. 10,000 连接保持测试。
  3. 10,000 连接 + 1 FPS/连接短测。
  4. 50,000 连接保持测试。
  5. 100,000 连接保持测试。
  6. 按真实协议分布进行混合帧率测试。

每一阶段必须记录:

  • active connections
  • gateway frame/publish counters
  • NATS pending 和 ack pending
  • Kafka lag
  • Redis/MySQL/TDengine 写入耗时和 pending depth
  • CPU/load/memory/file descriptors
  • 错误日志

2026-07-03 阶段压测记录

压测方式:在 ECS 本机使用 /opt/lingniu-go-native/current/load-sim127.0.0.1:808 发起 JT808 hold-only 连接,-send=false,不发送业务帧,不污染 raw 数据。

压测前基线,时间 2026-07-03 18:03:10 CST

服务状态 6 个 Go systemd 服务均 active
JT808 连接 约 240
GB32960 连接 约 2
load average 0.43 / 0.22 / 0.23
available memory 6661 MB
NATS ack pending 0
NATS pending 39
history/stat/realtime Kafka lag 0

阶段结果:

阶段 时间 参数 峰值连接/FD 结果 资源和队列
100 连接 18:03:21-18:04:21 CST connections=100 connect-rate=100 duration=60s send=false JT808 active 340ss341 opened 100failed 0frames 0write_errors 0 load 约 0.33/0.21/0.22available memory 约 6659 MB
1,000 连接 18:04:38-18:05:38 CST connections=1000 connect-rate=500 duration=60s send=false JT808 active 1240ss1241gateway FD 约 1256 opened 1000failed 0frames 0write_errors 0 load 约 0.80/0.33/0.26available memory 约 6645 MB
10,000 连接 18:05:59-18:07:59 CST connections=10000 connect-rate=1000 duration=120s send=false JT808 active 10240ss10241gateway FD 约 10256 opened 10000failed 0frames 0write_errors 0 20 秒时 load 约 0.45/0.33/0.27available memory 约 6409 MBNATS ack pending 0
50,000 连接 18:11:22-18:14:22 CST connections=50000 connect-rate=2000 duration=180s send=false JT808 active 50238ss50239gateway FD 约 50254 opened 50000failed 0frames 0write_errors 0 45 秒时 load 约 0.07/0.23/0.24available memory 约 5590 MBNATS ack pending 短时 29,后续归零
100,000 总连接 18:18:13-18:21:13 CST JT808 50000 + GB32960 50000,各 connect-rate=2000 duration=180s send=false JT808 active 50236GB32960 active 50002,总 active 约 100238gateway FD 约 100252 JT808 opened 50000 / failed 0GB32960 opened 50000 / failed 0frames 0write_errors 0 75 秒时 load 约 0.16/0.30/0.29available memory 约 4511 MBNATS ack pending 短时 10,后续归零

压测后:

  • 2026-07-03 18:08:47 CST JT808 active 回落到 240ss241
  • 2026-07-03 18:15:36 CST 5W 压测后 JT808 active 回落到约 238ss239
  • 2026-07-03 18:16:49 CST NATS ack pending 回到 0history/stat/realtime Kafka lag 总和均为 0
  • 2026-07-03 18:23:02 CST 10W 总连接压测后 JT808 active 回落到 236GB32960 active 回落到 2NATS ack pending 为 0history/stat/realtime Kafka lag 总和均为 0
  • 20211/20212/20213/20214/20215/20216/20200 readyz 均 OK。
  • 压测窗口未出现 gateway error|failed|panic|fatal|rejected 日志。
  • vehicle_gateway_connection_rejections_total 未输出,表示本轮没有连接拒绝计数。

结论:单 ECS 已通过本机 10W 总连接 hold-only 基线测试。该结果证明当前内核参数、gateway 连接上限和 systemd 文件句柄设置可以承载 10W 空闲长连接;下一阶段必须进入带真实帧率的低 FPS 压测验证解析、NATS、Kafka、Redis、TDengine、MySQL 全链路吞吐。

2026-07-13 1000 FPS 全链路压测记录

压测参数JT808 0x02001000 个连接,每连接 1 FPS持续 60 秒,手机号区间 139000000000-139000000999;开启服务端应答读取和注册自动清理。每轮均打开 1000 个连接、失败 0发送 59,500 帧、写错误 0读取应答约 1.19 MB、读错误 0结束后清理 1000 条模拟注册。

未读取 JT808 应答的早期结果不作为延迟基线:客户端接收缓冲区会填满并反向阻塞 Gateway 写应答,测到的是压测器缺陷而非服务端真实链路。

版本 观测点 Gateway 应答 p99 Redis p99 Bridge RAW p99 TDengine history p99 MySQL stat p99
topic 串行写 Kafka 压测中段 约 32ms 约 212ms 约 304ms 约 466ms 约 332ms
topic 串行写 Kafka 压测结束 约 10ms 约 162ms 约 326ms 约 427ms 约 313ms
topic 并行写 Kafkaconcurrency=6 压测中段 约 24ms 约 207ms 约 311ms 约 409ms 约 247ms
topic 并行写 Kafkaconcurrency=6 压测结束 约 29ms 约 184ms 约 288ms 约 368ms 约 316ms

并行版本在同一 NATS 批次内按 Kafka topic 分组并发写RAW 与派生 fields 全部成功后才 ACK 原 NATS 消息;任一 topic 失败仍保留对应源消息待重放。低负载 Bridge p99 从约 110ms 降到约 65ms1000 FPS 下 history p99 下降约 14%Bridge RAW p99 下降约 12%。压测中段 NATS pending 短时峰值 71Gateway async queue 短时 285结束时均归零Kafka lag 和各 writer retry 均为 0。主机为 4 核,瞬时 load average 约 9.68,但采样时 CPU 仍约 52% idle、无 D 状态进程、可用内存约 6.4 GiB。

增加队列等待指标后,使用五个全新 JT808 号段进行了 worker A/B每轮仍是 59,500 帧、发送/读取错误为 0、清理注册 1000 条。以下均为压测中段最近 512 样本窗口,秒级集中发送会比均匀流量更严格:

Gateway RAW worker Fast writer worker / fetch Gateway queue wait p99 Gateway 应答 p99 Redis p99 结论
32 8 / 20ms 约 95ms 约 92ms 约 234ms 入口突发排队明显
64 8 / 20ms 约 84ms 约 79ms 约 254ms Gateway 有小幅收益,下游未改善
128 16 / 5ms 约 42ms 约 17ms 约 151ms 当前最佳组合
128 32 / 5ms 约 50ms 约 32ms 约 221ms pull worker 过多产生调度竞争,回退

最终保留 NATS_ASYNC_RAW_WORKERS=128FAST_WRITER_WORKERS=16FAST_WRITER_FETCH_WAIT_MS=5。Redis 批写自身 p99 低于 5ms,约 151ms 的剩余尾延迟主要位于 JetStream 持久化发布和 consumer deliveryNATS/Kafka pending、Kafka lag 与 writer retry 在每轮结束后均归零。

2026-07-14 WAL Outbox 验证

Gateway 已切换为分段 WAL + 异步 JetStream PubAck。WAL 使用 16MiB/5s 分段、1ms/256 条 group commit、fsync=true、10000 最大 inflight本地 Apple M4 并发基准约 1926 records/s519144 ns/op),没有以关闭 fsync 换取吞吐。

ECS 生产使用 JT808 0x0200、1000 个连接、每连接 1 FPS、持续 60 秒验证:连接成功 1000、失败 0发送 59500 帧、写错误 0、读错误 0WAL submitted=acked=66776(含同期真实三协议流量),结束后 WAL backlog/inflight、NATS pending、Kafka lag 全部为 0。压测结束 Gateway JT808 响应 recent p99 约 80ms。

随后在约 1000 FPS 下对 Gateway 执行真实 SIGKILL。systemd 约 5 秒后自动拉起;启动后立即从崩溃前 WAL 重放并确认约 563 条记录,最终 WAL backlog/inflight、NATS pending 和 history/stat/realtime/identity Kafka lag 均回到 0日志没有 CRC 损坏、publish error 或 panic。故障窗口内压测客户端的写/读错误是 TCP 连接被强制中断的预期结果,不代表已持久接受的服务端数据丢失。