模块化、组件化、插件化与伪微服务治理:为什么装备软件一定要学会“拆”
-
有的模块越做越大,最后成了“巨石模块”
-
有的服务越拆越碎,最后一调用就是一长串
-
有的“微服务”其实只是把原来的一个系统打成镜像,再暴露几个接口
-
有的公共能力本应做成组件,却被拆成远程服务,徒增调用和运维复杂度
-
有的高变化能力本该插件化,却被写死在主程序里,导致每次改动都要动主干
-
模块化、组件化、插件化,到底有什么区别? -
为什么装备软件尤其要学会“拆”? -
微服务化过程中,如何识别“伪微服务”? -
如何建立一套适合装备软件的平台粒度治理方法?


-
生命周期更长
-
软硬件耦合更深
-
型号差异更明显
-
外设与协议适配更多
-
测试、追溯、审计要求更严格
-
后续维护和升级成本更高



-
任务管理模块
-
设备控制模块
-
数据采集模块
-
协议通信模块
-
状态监控模块
-
日志审计模块
-
让职责能说清
-
让边界能画清
-
让影响范围可控制
-
让测试和调试更容易
-
日志组件
-
参数配置组件
-
审计追踪组件
-
升级管理组件
-
数据记录回放组件
-
通信协议封装组件
-
板卡资源管理组件
-
有独立版本
-
有独立构建
-
有独立测试
-
有独立发布工件
-
有使用文档和接口约束
-
不同型号使用不同算法
-
不同设备接入不同协议
-
不同板卡需要不同驱动适配
-
不同任务场景有不同规则策略
-
主程序不断膨胀
-
每次新增能力都要改主干
-
重新编译、重新联调、重新回归测试
-
不同型号之间的差异越来越难管



-
一处改动,波及全系统
-
新型号复用不了旧能力
-
同样的问题在不同项目里反复重做
-
适配越做越多,主干越来越重
-
测试难、回归难、审计更难
-
稳定部分保持稳定
-
通用部分能够复用
-
高变化部分可以隔离
-
复杂子系统边界清楚
-
不同团队协作有明确责任面


-
`device/`
-
`protocol/`
-
`task/`
-
`common/`
-
`if board_type == A`
-
`else if board_type == B`
-
`else if board_type == C`
-
有的服务特别大,本质上是一个子系统伪装成微服务
-
有的服务特别小,本质上只是把原来进程内函数调用变成了远程调用



-
设备注册
-
设备占用与释放
-
板卡状态采集
-
串口管理
-
电源控制
-
任务下发
-
结果回传
-
告警和日志
-
用户权限和审计

-
独立业务边界
-
独立数据边界
-
独立发布
-
独立伸缩
-
独立故障隔离
-
独立团队责任
-
内部结构复杂
-
内部仍需要模块化和组件化
-
对外以整体能力协作
-
短期不一定值得继续切小


-
状态机模块
-
参数校验模块
-
数据解析模块
-
任务调度策略模块
-
审计组件
-
协议编解码组件
-
日志组件
-
配置管理组件
-
回放组件
-
板卡驱动插件
-
协议插件
-
算法插件
-
告警策略插件
-
数据处理策略插件
-
制品管理服务
-
任务编排服务
-
测试执行服务
-
资源调度服务
-
嵌入式资源管理子系统
-
自动化测试执行子系统
-
型号配置管理子系统


-
采集板卡温度、电压、电流
-
判断是否超阈值
-
生成告警信息
-
通过不同通道上报告警
-
支持不同型号板卡
-
支持不同协议适配
-
后续还可能替换告警策略
-
板卡采集逻辑和告警逻辑耦合
-
告警策略和具体通道耦合
-
板卡类型差异写在 if-else 里
-
后面每支持一种板卡、协议、告警通道,都得改主程序


#include<stdio.h>#include<string.h>voidmonitor_board(constchar* board_type, float temperature, constchar* alarm_channel) {char alarm_msg[128];if (strcmp(board_type, "RADAR_A") == 0) {if (temperature > 80.0f) {sprintf(alarm_msg, "[RADAR_A] 温度告警: %.1f C", temperature);if (strcmp(alarm_channel, "SERIAL") == 0) {printf("串口发送告警: %s\n", alarm_msg); } elseif (strcmp(alarm_channel, "ETHERNET") == 0) {printf("以太网上报告警: %s\n", alarm_msg); } else {printf("未知告警通道\n"); } } } elseif (strcmp(board_type, "CONTROL_B") == 0) {if (temperature > 70.0f) {sprintf(alarm_msg, "[CONTROL_B] 温度告警: %.1f C", temperature);if (strcmp(alarm_channel, "SERIAL") == 0) {printf("串口发送告警: %s\n", alarm_msg); } elseif (strcmp(alarm_channel, "ETHERNET") == 0) {printf("以太网上报告警: %s\n", alarm_msg); } else {printf("未知告警通道\n"); } } } else {printf("未知板卡类型\n"); }}intmain() { monitor_board("RADAR_A", 85.5f, "ETHERNET");return0;}
-
板卡差异和监测逻辑写死在一起
-
告警消息构建和告警发送写死在一起
-
通道扩展需要改主函数
-
每新增一种板卡,都得继续堆 if-else
-
板卡监测模块
-
告警生成模块
-
告警发送模块
#ifndef ALARM_MESSAGE_H#define ALARM_MESSAGE_Hvoidbuild_alarm_message(char* buffer, int size,constchar* board_name,float temperature);#endif
#include<stdio.h>#include"alarm_message.h"voidbuild_alarm_message(char* buffer, int size,constchar* board_name,float temperature) {snprintf(buffer, size, "[%s] 温度告警: %.1f C", board_name, temperature);}
#ifndef ALARM_SENDER_H#define ALARM_SENDER_Hvoidsend_alarm_serial(constchar* msg);voidsend_alarm_ethernet(constchar* msg);#endif
#include<stdio.h>#include"alarm_sender.h"voidsend_alarm_serial(constchar* msg) {printf("串口发送告警: %s\n", msg);}voidsend_alarm_ethernet(constchar* msg) {printf("以太网上报告警: %s\n", msg);}
#include<stdio.h>#include<string.h>#include"alarm_message.h"#include"alarm_sender.h"voidmonitor_board(constchar* board_type, float temperature, constchar* channel) {float threshold = 0.0f;char msg[128];if (strcmp(board_type, "RADAR_A") == 0) { threshold = 80.0f; } elseif (strcmp(board_type, "CONTROL_B") == 0) { threshold = 70.0f; } else {printf("未知板卡类型\n");return; }if (temperature > threshold) { build_alarm_message(msg, sizeof(msg), board_type, temperature);if (strcmp(channel, "SERIAL") == 0) { send_alarm_serial(msg); } elseif (strcmp(channel, "ETHERNET") == 0) { send_alarm_ethernet(msg); } else {printf("未知告警通道\n"); } }}
-
告警消息构建职责独立
-
告警发送职责独立
-
监测逻辑更清楚
-
每一块代码更容易单独测试
alarm_message#ifndef ALARM_COMPONENT_H#define ALARM_COMPONENT_H#define ALARM_COMPONENT_VERSION "1.0.0"voidalarm_component_build_message(char* buffer, int size,constchar* board_name,constchar* alarm_type,float value);#endif
#include<stdio.h>#include"alarm_component.h"voidalarm_component_build_message(char* buffer, int size,constchar* board_name,constchar* alarm_type,float value) {snprintf(buffer, size, "[%s] %s 告警: %.1f", board_name, alarm_type, value);}
-
告警能力有独立版本
-
可以跨多个项目共享
-
可独立维护和测试
-
未来支持更多告警类型时,不必每个项目各改各的
-
板卡型号不同,阈值规则不同
-
不同平台使用不同告警通道
-
后续还可能加新板卡、新协议、新输出方式
#ifndef BOARD_PLUGIN_H#define BOARD_PLUGIN_Htypedefstruct {constchar* board_name;float (*get_temp_threshold)(void);} BoardPlugin;#endif
#ifndef CHANNEL_PLUGIN_H#define CHANNEL_PLUGIN_Htypedefstruct {constchar* channel_name;void (*send_alarm)(constchar* msg);} ChannelPlugin;#endif
#include"board_plugin.h"staticfloatradar_a_threshold(void) {return80.0f;}BoardPlugin radar_a_plugin = { .board_name = "RADAR_A", .get_temp_threshold = radar_a_threshold};
#include"board_plugin.h"staticfloatcontrol_b_threshold(void) {return70.0f;}BoardPlugin control_b_plugin = { .board_name = "CONTROL_B", .get_temp_threshold = control_b_threshold};
#include<stdio.h>#include"channel_plugin.h"staticvoidserial_send(constchar* msg) {printf("串口发送告警: %s\n", msg);}ChannelPlugin serial_channel_plugin = { .channel_name = "SERIAL", .send_alarm = serial_send};
#include<stdio.h>#include"channel_plugin.h"staticvoidethernet_send(constchar* msg) {printf("以太网上报告警: %s\n", msg);}ChannelPlugin ethernet_channel_plugin = { .channel_name = "ETHERNET", .send_alarm = ethernet_send};
#include<stdio.h>#include<string.h>#include"board_plugin.h"#include"channel_plugin.h"#include"alarm_component.h"/* 外部插件实例 */extern BoardPlugin radar_a_plugin;extern BoardPlugin control_b_plugin;extern ChannelPlugin serial_channel_plugin;extern ChannelPlugin ethernet_channel_plugin;BoardPlugin* board_plugins[] = { &radar_a_plugin, &control_b_plugin};ChannelPlugin* channel_plugins[] = { &serial_channel_plugin, ðernet_channel_plugin};BoardPlugin* find_board_plugin(constchar* name) {int i;for (i = 0; i < sizeof(board_plugins)/sizeof(board_plugins[0]); i++) {if (strcmp(board_plugins[i]->board_name, name) == 0) {return board_plugins[i]; } }returnNULL;}ChannelPlugin* find_channel_plugin(constchar* name) {int i;for (i = 0; i < sizeof(channel_plugins)/sizeof(channel_plugins[0]); i++) {if (strcmp(channel_plugins[i]->channel_name, name) == 0) {return channel_plugins[i]; } }returnNULL;}voidmonitor_board(constchar* board_name, float temperature, constchar* channel_name) {char msg[128]; BoardPlugin* board = find_board_plugin(board_name); ChannelPlugin* channel = find_channel_plugin(channel_name);if (!board) {printf("未找到板卡插件: %s\n", board_name);return; }if (!channel) {printf("未找到通道插件: %s\n", channel_name);return; }if (temperature > board->get_temp_threshold()) { alarm_component_build_message(msg, sizeof(msg), board_name, "温度", temperature); channel->send_alarm(msg); } else {printf("[%s] 温度正常: %.1f C\n", board_name, temperature); }}intmain() { monitor_board("RADAR_A", 85.5f, "ETHERNET"); monitor_board("CONTROL_B", 65.0f, "SERIAL");return0;}


-
监测归监测
-
消息构建归消息构建
-
通道发送归通道发送
-
代码更容易理解
-
单元测试更容易做
-
改动影响面更可控
-
调试定位更简单
alarm_component-
统一格式
-
独立版本
-
跨项目复用
-
统一升级
-
新增一种板卡,不改主程序,增加一个板卡插件即可
-
新增一种通道,不改主程序,增加一个通道插件即可



-
模块
-
组件
-
插件
-
业务子系统
-
有明确业务能力边界
-
有独立数据边界
-
能独立演进
-
值得独立发布
-
有独立故障隔离意义
-
有稳定责任团队
-
任务编排服务
-
测试执行服务
-
制品管理服务
-
资源调度服务
-
格式转换
-
通用校验
-
参数解析
-
编解码库
-
驱动适配
-
板卡规则


-
发布还得跟上下游一起发
-
改一点点要联动多个系统
-
回滚也得整体回滚


-
真正微服务
-
业务子系统


|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|


-
哪些是模块
-
哪些是组件
-
哪些是插件
-
哪些是真服务
-
哪些其实是业务子系统
-
先梳理内部模块边界
-
先收敛内部依赖
-
先抽出公共组件
-
先识别高变化插件点
-
板卡差异
-
协议差异
-
算法差异
-
告警规则差异
-
它的独立业务能力是什么?
-
它的数据边界是什么?
-
它独立发布的价值是什么?
-
它独立伸缩的价值是什么?
-
它的责任团队是谁?


-
稳定部分保持稳定
-
通用部分沉淀复用
-
高变化部分隔离扩展
-
复杂能力承认为子系统
-
真正独立的能力再服务化

夜雨聆风