问题背景:设备侧为何要跑 spec-kit
把 spec-kit 这类接口规范工具从云端挪到设备侧,最初不是为了追新,而是被现场逼出来的。我们有一批边缘网关和工控机,部署在客户内网,出口只留了一个 443 端口给运维通道,设备上线后基本处于半离线状态。原先的做法是:设备采集数据后上报到中心,中心用 spec-kit 做请求校验、字段映射和协议转换,再把结果回写。问题在于,一旦专线抖动或者中心侧发布窗口撞上业务高峰,设备侧的采集队列就会堆积,最严重的一次单台网关积压了 47 分钟的数据,恢复后补传又把中心入口打出一波尖峰。
更麻烦的是合规。部分客户要求原始报文不出园区,中心只能拿到脱敏后的聚合结果。这意味着校验和转换必须发生在设备侧,否则要么违反约定,要么把明文送出去。于是我们开始评估:把 spec-kit 的运行时裁剪后直接塞进设备,让设备自己完成规范校验和轻量转换,中心只负责汇总和下发规范版本。这个决定带来一串工程问题——设备资源有限、部署形态不统一、升级通道窄,任何一个没处理好,都会变成运维的夜间电话。
方案选型:三种部署形态的取舍
我们对比了三种形态,都是围绕 spec-kit 的运行时展开,区别在于进程边界和依赖来源。
第一种是嵌入宿主进程,把 spec-kit 作为库直接链进设备侧的主采集程序。优点是零额外进程、内存共享、调用延迟最低,实测单次校验在 0.3 毫秒以内。缺点是耦合太深:规范更新要重新编译主程序,而主程序由另一个团队维护,发版节奏对不上;一旦 spec-kit 运行时出现异常,整个采集进程会被拖垮,故障隔离为零。
第二种是独立边车进程,spec-kit 跑成单独的可执行文件,主程序通过本地 Unix Socket 或环回 HTTP 调用。隔离性好,规范更新只需替换边车二进制并重启边车,主程序无感。代价是多一份常驻内存和一次进程间通信开销,实测环回 HTTP 单次调用约 1.8 毫秒,Unix Socket 约 0.9 毫秒。对采集频率在每秒几十次的场景,这个开销可以接受。
第三种是按需拉起,平时不常驻,主程序在有校验需求时 fork 一个短生命周期进程。内存峰值最低,空闲时几乎为零占用。但冷启动要加载规范文件和运行时,实测首次调用 210 毫秒,之后依赖页缓存能降到 40 毫秒左右。设备侧采集是持续流,按需拉起会让延迟抖动变得不可预测,最终被否掉。
取舍的结论是:采集主链路用边车常驻,低频的管理面接口用按需拉起。这个混合形态在我们 6 个机型上都能跑,最小的一台是 2 核 2G 的 ARM 网关,边车常驻内存稳定在 38MB 左右,CPU 空闲占用低于 1.5%。规范文件按版本号做目录隔离,边车启动时只加载当前激活版本,历史版本留在磁盘上供回滚,单个规范包平均 1.2MB,保留三个版本对 eMMC 的压力可以忽略。
落地步骤:从裁剪到灰度
落地分四步,每步都有对应的验收口径,避免“装上了但没真正生效”。
第一步是运行时裁剪。 完整 spec-kit 带了不少面向服务端的特性,比如多租户隔离、远程规范拉取、审计日志落盘。设备侧只需要校验、字段映射和版本切换,其余全部编译期裁掉。裁剪后二进制从 61MB 降到 14MB,启动加载的规范解析时间从 900 毫秒降到 180 毫秒。裁剪不是删代码了事,我们保留了一个 --selftest 子命令,用内置的 32 条样例规范跑一遍校验链路,确保裁掉的只是没用的分支。
第二步是部署形态固化。 边车用 systemd 管理,配 Restart=on-failure 和内存上限 MemoryMax=128M,超限直接重启而不是拖垮整机。主程序与边车的通信走 Unix Socket,路径固定在 /run/spec-kit/spec.sock,权限设为 0660,属组给采集进程。规范版本通过一个软链接 current 指向具体版本目录,切换版本就是改软链接再给边车发 SIGHUP,实测切换生效时间 1.4 秒,期间请求排队不丢。
第三步是灰度与回滚。 我们先在 3 台非生产网关上跑了两周,用旁路方式把边车的校验结果和中心侧的结果做比对,差异率从首日的 0.7% 降到第三天的 0.02%,剩下的差异都是规范版本不一致导致的,对齐后归零。灰度期间保留了一键回滚脚本:停边车、切回旧软链接、拉起边车,全程 6 秒以内。确认稳定后才推到生产批次,每批不超过 50 台,批间间隔 24 小时。
第四步是观测。 边车暴露一个本地 metrics 端点,采集进程每 30 秒抓一次,只保留校验次数、失败率、P99 延迟、常驻内存四个指标,随业务数据一起回传。这样中心侧能看到每台设备的规范执行情况,而不用登录设备。上线首月靠这个指标发现了两台设备因为磁盘写满导致规范加载失败,提前换了盘。
效果与成本:实测数据
改造后跑了三个月,几个可量化的结果比较有说服力。
中心侧入口成本下降 41%。 原先所有原始报文都要打到中心做校验转换,改造后设备侧完成校验,中心只收聚合结果和异常样本。中心入口的峰值 QPS 从 3200 降到 1890,按入口规格折算,省下了两台 4 核 8G 的常驻实例和对应的带宽,月度成本下降 41%。这个比例是拿改造前后各一个完整月的账单对比出来的,不含一次性人力。
端到端延迟从 380 毫秒降到 95 毫秒。 延迟口径是采集点到中心可查询的时间。设备侧校验把原来“上报—中心校验—回写”的往返砍掉,只保留一次单向聚合上报。P99 从 1.2 秒降到 310 毫秒,抖动明显收敛。对客户来说,最直观的变化是告警从“事后几分钟”变成“事后十几秒”。
单台设备并发承载提升到 120 路。 边车常驻后,单台 2 核 2G 网关能稳定处理 120 路采集流的校验,CPU 峰值 62%,内存峰值 71MB。改造前主程序内嵌校验只能跑到 70 路左右就开始丢采样。这个数字是在压测环境跑出来的,生产上按 80 路配置,留了余量。
成本侧也要说清楚:每台设备多了一个常驻进程,内存多占约 38MB,eMMC 多留 5MB 给规范版本,运维多了一个需要观测的进程。这些代价换来的是合规达标、中心减负和延迟收敛,在我们场景里是划算的。需要提醒的是,边车形态对进程管理有要求,如果设备侧没有可靠的 init 系统,建议退回嵌入形态并接受发版耦合,别为了隔离硬上边车,否则进程挂了没人拉起,问题会更隐蔽。