news 2026/9/4 2:04:19

Microduck的守护进程军团:Unix socket上的JSON-RPC微服务架构

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Microduck的守护进程军团:Unix socket上的JSON-RPC微服务架构

03-守护进程军团:Unix socket上的JSON-RPC微服务架构

引子:为什么一只 800 克的鸭子要拆 7 个进程?

大家好,我是黒漂技术佬。

上一篇文章我们聊了 robotd 的 50Hz 控制环。今天往后退一步,看看全局:一只 800 克、只有一块 RK3566 板子的鸭子,为什么要跑 7 个独立的守护进程?

按"简单"的思路,全部代码塞进一个进程,不是更省事吗?少了进程间通信的麻烦,少了启动顺序的编排,内存还更省。很多小设备确实就是这么干的——一个 main 函数,一堆 if-else。

microduck 偏偏没有。它的选择是:

7 个 Rust daemon,通过 Unix socket 上的 JSON-RPC 2.0(NDJSON)协议通信。

这不是炫技。每一个拆分都有具体的、写在文档里的理由。这篇文章我们就来拆解这套"单板微服务"架构——你会发现,微服务的核心思想(服务边界、故障隔离、接口契约)在单板机上同样成立,只是实现方式更轻


一、军团点将:7 个 daemon 各司其职

先把阵容摆出来(上一篇给过表格,这里带职责重新梳理一遍):

服务一句话职责关键点
robotd控制核心:电机、传感、策略、安全唯一能碰 Dynamixel 总线的进程
configdWiFi、身份、配对 PIN、手柄绑定、重启robotd 挂了它也要能干活
updaterdOTA:验证、安装、切换、健康门控、回滚升级时它说了算
btdBLE 传输通道(手机通过蓝牙控制)纯传输层,不持有机器人状态
padd手柄输入通道只转发原始输入
mediad摄像头/音频管线 + WebRTC 远程访问媒体崩溃不能拖垮电机
tofd头部 ToF 深度传感器独立传感器服务,发布即完事

这个拆分不是拍脑袋定的,它遵循几条非常清晰的边界原则

原则一:按"崩溃半径"切分

这是最重要的原则。每个进程都是故障隔离的单元——一个进程崩溃,不能拖垮其他进程。

  • mediad 为什么独立?因为媒体处理(视频采集、编码、WebRTC)是最容易崩、最吃资源的。如果媒体和电机控制在一个进程里,视频管线一崩,机器人直接失去平衡摔地上。分开之后:媒体崩了,鸭子最多"瞎"一会儿,但不会"瘫"。
  • tofd 为什么独立?因为 ToF 传感器的固件升级要经过 I²C 总线传约 90KB 数据,需要秒级时间,而且很多板子根本没有这个传感器。如果把它塞进 mediad,就得让整个媒体管线陪着它等;塞进 robotd,就得让控制环陪着它等——都不行。独立出来,爱传多久传多久,谁都不影响。
  • padd 为什么不并入 robotd?因为手柄配对需要 root 权限操作 BlueZ(蓝牙栈),而 padd 故意设计成非特权进程。权限边界和功能边界要匹配,这是安全设计的基本功。

原则二:恢复路径必须独立于被救对象

robotd 死了 → configd 还能配 WiFi ✓ robotd 死了 → updaterd 还能刷固件 ✓ robotd 死了 → btd 还能让你连上蓝牙 ✓

细品这三行。configd/updaterd/btd 的代码里没有对 robotd 的依赖(不依赖它的 socket、不调用它的 API、不需要它的库)。为什么?因为最需要"配网"“刷机”"应急连接"的时刻,恰恰是 robotd 出问题的时刻。如果一个系统只能在健康时被维修,那它一旦病了就永远好不了。

这让我想起无人售货柜项目的一个教训:我们的设备管理通道一开始依赖业务主进程,结果主进程 OOM 时,远程维护通道也连不上了,只能跑现场。后来把"管理通道"和"业务进程"彻底分离,才根治。microduck 把这个原则直接刻进了架构。

原则三:传输层不持有状态

btd(蓝牙)、padd(手柄)、mediad(媒体)都是传输层——它们只负责把外部世界的输入送进来,或者把机器人状态送出去,本身不拥有机器人状态的任何部分。机器人状态(关节角度、姿态、任务)只存在于 robotd。

这保证了"传输层随便换":今天手柄走 BLE,明天改 USB,后天加一个 UDP 通道——只要接口契约不变,robotd 完全无感。


二、接口契约:JSON-RPC 2.0 over Unix socket

进程拆开了,怎么通信?microduck 的方案:

每个服务一个 Unix socket,客户端直连;线协议是 JSON-RPC 2.0,NDJSON 格式(每行一个 JSON 对象);全异步 + 超时,订阅 = 开放连接上的 notification 流。

2.1 为什么选 Unix socket 而不是 TCP localhost?

这是本篇最值得抄作业的一个决策。官方文档列了理由,我展开讲:

第一,文件系统权限 = 免费的鉴权。

Unix socket 有文件权限位。microduck 的做法:

  • socket 权限设为0660+ 专属 group → 只有属于该组的进程能连上来"交谈";
  • 再配合SO_PEERCRED获取对方的 uid/gid/pid → 用于审计,并做调用级门控allow_uids/allow_gids只限制"写操作"(mutating calls),只读调用不设限。

这套组合拳意味着:进程间通信的鉴权在操作系统层面就完成了——一个不属于机器人群组的进程,根本连不上 socket,连握手都谈不上。如果走 localhost TCP,你得自己实现 token、TLS、权限校验……还容易出漏洞。

第二,永远不可能被外部网络访问到。

监听 localhost TCP 的 127.0.0.1 端口,虽然比 0.0.0.0 好,但依然有被本机其他进程访问的风险。Unix socket 天然只对本机文件系统可见,根本不存在"误绑 0.0.0.0 把固件升级接口暴露到公网"这种事故。SDK 里的第三方代码再烂,也够不着这些 socket。

第三,性能和语义都好。Unix socket 比 TCP loopback 少一层协议栈,延迟更低;而且是"连接即身份",配合 SO_PEERCRED 拿进程信息毫无成本。

对做嵌入式/边缘设备的读者,这条经验可以直接搬:本机多进程通信,优先 Unix socket,别动不动就上 HTTP。

2.2 为什么是 JSON-RPC 而不是 HTTP/WebSocket?

文档里写得很直白:

  • 需要服务端 → 客户端主动推送(订阅机制:robotd 状态变化要实时推给手机 App)。HTTP 是典型的请求-响应模型,做推送要么轮询(浪费)要么上 WebSocket(复杂);
  • NDJSON 持久连接:一行一个 JSON 对象,请求/响应和通知(notification)用同一套格式,没有 HTTP 那套头、状态码、握手的仪式感;
  • 未来想加只读的GET /status也很容易——在同一个 socket 上叠加一个 HTTP 语义即可。

2.3 为什么不是 D-Bus?

D-Bus 在 Linux 桌面系统里是标准 IPC,但 microduck 只在两个地方用了它:操作 BlueZ(蓝牙)和 NetworkManager(WiFi)——因为这两个是系统服务,只能用 D-Bus 去调。

自己的服务间通信则明确不用 D-Bus,理由也很实在:

  • zbus(Rust 的 D-Bus 绑定)要拉 66 个依赖,而自研 JSON-RPC/NDJSON 只要约 30 个;
  • 消息还要经过 BLE、WebRTC 传出去(手机 App 也要能发同样的指令),用 plain serde + JSON 让每一条消息从进程内到远端手机走的都是同一种格式——“每个客户端——App、控制台、手柄、你的脚本——发送完全相同的调用”。这一点非常优雅。

2.4 为什么不用 ROS 2?

这个问题肯定有人问。官方没有专门论述,但从架构可以推断:ROS 2 是为多机、异构、需要丰富工具链的机器人生态设计的;microduck 是单板、7 个自有进程、需要极致轻量的场景。在单板机上引入 ROS 2 的 DDS 中间件,等于开着一台重卡去送快递。

选型的第一原则是匹配规模。这不是说 ROS 不好——恰恰相反,做复杂机器人系统 ROS 2 几乎是必选。但 microduck 的规模用不上它,就像我们的智慧农业网关不需要上 Kubernetes 一样。


三、控制平面 vs 数据平面:27MB/s 的教训

这是上一篇提过、但值得单独拎出来的一条:数据平面绝不跨 socket。

平面内容大小/频率传输方式
控制平面命令、配置、状态、感知事件几十字节,≤100HzUnix socket RPC
数据平面视频/音频帧640×480 RGB @30fps ≈ 27MB/s不跨 socket

27MB/s 的原始视频帧如果走 Unix socket,会发生什么?

  • 延迟:帧传输耗时会被打进控制链路的等待里(虽然 robotd 用 last-value-wins 不阻塞,但带宽被挤占是实打实的);
  • CPU 开销:序列化、拷贝、内核往返,全在烧 CPU;
  • 架构腐化:一旦开了"传原始数据"的口子,后面就会有越来越多的大块数据走 socket,控制平面的低延迟保证就没了。

microduck 的做法是:数据流在进程内就近处理,跨进程只传特征。

  • robotd 不需要视频帧,它只需要 mediad 里视觉模块推导出的结果——“球在 (x,y)”,10~30Hz,几十字节;
  • 视频帧只在 mediad 内部流转(采集 → 编码 → WebRTC 发出);
  • 真到了万不得已要跨进程传帧,文档里的兜底方案是shm/dmabuf 环形缓冲——共享内存,不是 socket。

这条原则,做监控、做无人零售、做任何"摄像头 + 控制"组合的工程师都该背下来:进程间只传特征,不传原始数据。


四、状态所有权:谁写的,就得写清楚

微服务架构最怕什么?状态散落各处,没人知道谁该为哪份状态负责。microduck 的文档把状态的所有权画得清清楚楚:

状态存放位置拥有者
板级配置(robotd.toml、updater.toml)/etc/robot/安装时写入,永不覆盖
机器人名、配对 PIN/var/lib/robot/config/config.jsonconfigd(用文件 + flock 锁)
WiFi 凭据NetworkManager 配置从不自己存储
二进制/策略/默认配置/opt/robot/daemon/releases/<版本>/updaterd,原子替换
当前版本链接/opt/robot/daemon/current(symlink)updaterd
各 daemon 实际运行身份/run/<service>/identity.json各 daemon 自己

注意两个细节:

  1. 配置即文件,不是服务。configd 管理配置用的是"文件 + flock + rename(2)“,而不是搞一个配置数据库服务。文档明确说没加 inotify 监听——因为只有一个写者(configd),不需要事件通知机制。能用文件解决的,不上服务——这是嵌入式开发的"奥卡姆剃刀”。

  2. 版本目录设计。固件以版本号目录整体存放,current是个软链接指向当前版本。这个设计直接支撑了后面的 OTA 原子切换和回滚(第 04 篇细讲)。“软链接 = 状态切换”是 Unix 哲学里极其经典的手法:切换状态不搬数据,只改一个链接。


五、我们能学到什么?

把这一篇的精华提炼成可迁移的经验:

  1. 进程边界 = 故障隔离边界。拆进程的第一理由永远是"崩溃半径",其次是权限边界和生命周期边界。别为了"省事"把所有东西塞进一个进程,也别为了"微服务"盲目拆。
  2. 单板机也能玩微服务思想。服务边界、接口契约、状态所有权、故障隔离——这些概念不依赖 K8s,一套 Unix socket + JSON-RPC 就够。
  3. 本机 IPC 优先 Unix socket。权限免费、外部不可达、性能更好。除非需要跨机器,否则别轻易上 HTTP/TCP。
  4. 进程间只传特征,不传原始数据。这条救过我们的售货柜项目,也会救你的视频类项目。
  5. 状态所有权必须白纸黑字。谁拥有、存哪里、怎么更新,都要写清楚。配置能用文件解决就别上服务。

小结

7 个 daemon、一套 JSON-RPC、一个 Unix socket 网络——microduck 用最轻的方式,把"微服务架构"的核心思想完整地实践在了一块单板上。

它的拆分逻辑清晰到可怕:崩溃半径、恢复路径、权限边界、状态所有权,每个进程的"为什么存在"都有文档可查。这种"每个决策都有理由"的工程文化,比任何一行代码都珍贵。

但这里还埋着一个伏笔:7 个进程里有一个"管生杀大权"的家伙——updaterd。它负责把新固件刷进鸭子,还要保证刷不坏。这背后的签名验证、原子切换、健康门控、自动回滚,是嵌入式 OTA 的教科书级设计。

下一篇,就拆它。

我是黒漂技术佬,咱们下篇见。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/4 2:04:10

从意图经济到旅行Agent:用大模型工具调用实现“甩手掌柜”

“想做旅行里的甩手掌柜”&#xff0c;这其实是当下「意图经济」讨论里最容易被误读的一句话。多数人以为&#xff0c;所谓意图经济就是“AI帮我搜攻略、推荐酒店、拼一条行程”。如果只做到这一步&#xff0c;那它仍然是搜索&#xff0c;不是执行。真正让“甩手掌柜”成为一个…

作者头像 李华
网站建设 2026/9/4 2:01:27

基于PyTorch的数学公式识别:从编码器-解码器到LaTeX生成

简介&#xff1a;本资源是一套面向本科毕业设计与深度学习初学者的数学公式识别实践项目&#xff0c;基于Python与神经网络模型实现图像及文本中数学表达式的端到端识别&#xff0c;适用于学术出版、在线教育、智能阅卷等场景。压缩包共93个文件&#xff0c;含35个核心Python脚…

作者头像 李华
网站建设 2026/9/4 2:01:04

华为认证备考攻略:从HCIA到HCIE刷题工具与高效刷题方法论

打开招聘网站&#xff0c;网络工程师岗位要求里经常出现一行字&#xff1a;有华为认证者优先。对想转行或刚入行的朋友来说&#xff0c;HCIA、HCIP、HCIE 这三个缩写&#xff0c;既是技术阶梯&#xff0c;也是简历能不能被筛出来的“硬通货”。但真正开始复习时&#xff0c;很多…

作者头像 李华
网站建设 2026/9/4 1:58:58

移动端图片视频上传与保存相册全流程实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华
网站建设 2026/9/4 1:55:06

两阶段鲁棒优化在主动配电网动态无功优化中的Matlab实现与应用

简介&#xff1a;本资源是一套面向电力系统优化方向研究生与科研工程师的MATLAB开源代码&#xff0c;聚焦主动配电网在分布式电源与负荷不确定性下的动态无功优化问题。针对传统确定性模型鲁棒性不足的痛点&#xff0c;代码完整实现了文献《两阶段鲁棒优化的主动配电网动态无功…

作者头像 李华
网站建设 2026/9/4 1:54:57

工业安全AI视觉数据集构建实战:从危险品识别到模型部署

简介&#xff1a;这是一份面向工业安全、物流监管与AI安防领域的多类别目标检测数据集&#xff0c;专为危险品标志识别任务设计&#xff0c;适用于YOLO系列等主流目标检测模型的训练与验证。资源共1014个文件&#xff0c;含506张实际场景JPEG图像、506个对应YOLO格式txt标注文件…

作者头像 李华