用Java实现嵌入式设备远程运维平台:批量监控边缘节点的运行状态
嵌入式边缘节点远程运维别让每台设备都开HTTP轮询。合理架构是“边缘Agent轻量采集→MQTT/TLS上送→Java接入服务聚合→Redis存最新状态、时序库存历史→心跳健康状态机判在线/假死/离线→批量命令/配置/OTA下发→WebSocket推大屏”。千级节点用Spring Boot+MQTT+Redis可落地,万级再拆接入集群。
一、边缘Agent:资源与心跳一起采
Agent跑在ARM Linux/工控机,定周期采CPU、内存、磁盘、进程、网络RTT、业务队列深度,心眺包和业务包分开。用Eclipse Paho:
public class EdgeAgent {
MqttClient client;
ScheduledExecutorService sch = Executors.newScheduledThreadPool(2);
public void start(String broker, String id, String token) throws MqttException {
client = new MqttClient(broker, id, new MemoryPersistence());
MqttConnectOptions o = new MqttConnectOptions();
o.setUserName(id); o.setPassword(token.toCharArray());
o.setAutomaticReconnect(true); o.setConnectionTimeout(5);
o.setWill("edge/"+id+"/status", "offline".getBytes(), 1, true);
client.connect(o);
client.subscribe("edge/"+id+"/cmd", 1);
sch.scheduleAtFixedRate(this::reportHealth, 0, 30, TimeUnit.SECONDS);
}
void reportHealth() {
Runtime r = Runtime.getRuntime();
Health h = new Health();
h.cpu = OshiHelper.cpuPercent(); // OSHI/Proc采集
h.memUsed = r.totalMemory()-r.freeMemory();
h.memTotal = r.totalMemory();
h.ts = System.currentTimeMillis();
h.aliveTasks = LocalTaskRegistry.runningCount(); // 业务线程/任务数
try { client.publish("edge/"+h.id+"/health", JsonUtil.toJson(h).getBytes(), 1, false); }
catch (Exception e) { LocalBuffer.store(h); } // 断网落本地,重连补传
}
}
心跳不能只看TCP Keepalive:应用死锁、MQTT连着但业务停摆,平台仍会误判在线。健康模型用三层——链路心跳、任务心跳、数据新鲜度。
二、Java接入:聚合、去重、状态机
平台订阅edge/+/health、edge/+/telemetry,异步写Redis最新值,异步落InfluxDB/MySQL历史;同一条消息按deviceId+ts做幂等,QoS1重投不重复入库。
@Component
public class HealthConsumer {
@Autowired StringRedisTemplate redis;
@Autowired AlertService alert;
public void onHealth(Health h) {
redis.opsForValue().set("edge:health:"+h.id, JsonUtil.toJson(h), 5, TimeUnit.MINUTES);
DeviceState s = StateCache.get(h.id);
if (s == null) s = new DeviceState(h.id);
s.lastHealthTs = h.ts;
s.cpu = h.cpu; s.mem = h.memUsed*100/h.memTotal;
s.taskAlive = h.aliveTasks;
s.link = "UP";
if (h.cpu > 95 && s.cpuHighCount++ > 3) alert.fire(h.id, "CPU持续过高");
if (h.aliveTasks == 0 && s.expectedTasks > 0) alert.fire(h.id, "业务任务假死");
StateCache.put(s);
}
}
离线判定用“心跳周期×容错次数”,例如30s一次、连续3次未到判离线,避免抖动误报; 群体同网段/同固件集中离线单独升P0,单台按业务等级升P1/P2。
定时巡检补刀,处理“MQTT连着但健康包不来的假在线”:
@Scheduled(fixedDelay = 30000)
public void sweep() {
for (DeviceState s : StateCache.all()) {
long gap = System.currentTimeMillis() - s.lastHealthTs;
if (gap > 90000) { s.link = "OFFLINE"; alert.fire(s.id, "心跳超时离线"); }
else if (gap > 45000) { s.link = "DEGRADED"; }
}
}
三、批量监控与命令下发
批量重启、批量拉配置、批量查版本,用线程池按分组并发,单台失败不影响整体,结果汇总:
public Map<String,String> batchRestart(List<String> ids) {
Map<String,String> r = new ConcurrentHashMap<>();
ids.parallelStream().forEach(id->{
try {
mqtt.publish("edge/"+id+"/cmd",
JsonUtil.toJson(new Cmd("restart","trace-"+UUID.randomUUID())).getBytes(),1,false);
r.put(id,"sent");
} catch (Exception e) { r.put(id,"ERR:"+e.getMessage()); }
});
return r;
}
命令带traceId,设备执行完回edge/{id}/cmd/ack,平台更新状态;超时未回自动重投一次,仍失败转人工。配置/固件走灰度:先1台→同组10%~20%→全量,版本号不匹配不下发,弱网用本地缓存+断点续传,写临时文件rename防断电损坏。
四、大屏与弱网闭环
WebSocket只推状态变更,不轮询:Redis状态变→SimpMessagingTemplate推/topic/edge/{group};前端按设备、站点、固件版本聚合在线率、CPU TOP10、离线分布。
弱网要点:Agent本地有界队列缓存健康/告警,重连后按ts补传关键事件;MQTT持久会话+QoS1,配置用QoS2或“版本号拉取”兜底;TLS/mTLS一机一密,敏感命令服务端留操作审计。 这样千台边缘网关可做到30s级健康可见、分钟级批量处置,假死、离线、配置漂移都能远程收敛。





