工业后端项目复盘

项目复盘

C++ 工业数据服务后端项目复盘:从高频数据写入到实时报警推送

1. 项目背景

这个项目是一个面向工业设备和上位机系统的数据服务后端,主要负责接收电解槽、MEA、CCM 等生产和测试环节产生的数据,并将这些数据稳定写入 PostgreSQL 数据库。

系统除了基础的数据入库,还提供了动态查询、更新、删除、用户登录认证、设备写入权限控制、实时报警推送等能力。整体目标是让前端大屏、上位机、生产管理系统能够通过统一的 HTTP API 访问后端数据。

2. 技术栈

模块技术
开发语言C++17
HTTP 框架Pistache
数据库PostgreSQL
数据库访问libpq 原生 C API
JSON 处理nlohmann/json
并发处理自定义 ThreadPool、后台 Worker
数据写入批量队列、事务提交、失败降级
认证JWT、libsodium 密码校验
日志spdlog
实时推送SSE
外部通知libcurl、飞书机器人 Webhook
构建系统CMake
配置管理JSON 配置文件
扩展依赖OpenCV

这套技术栈的特点是比较偏底层,尤其是数据库访问没有使用 ORM,而是直接基于 libpq 封装,这样可以更明确地控制 SQL、连接生命周期和性能开销。

3. 系统整体架构

系统可以分成四层:

系统架构图

HTTP 层负责接收请求和路由分发,Handler 层负责业务处理。查询类接口通过数据库连接池访问 PostgreSQL,写入类接口则先进入内存队列,再由后台 Worker 批量写入数据库。

这样设计的好处是:上位机上报数据时不需要等待数据库完整写入,可以快速返回;后台再统一进行批处理,提高吞吐能力。

4. 核心模块设计

4.1 配置管理

项目通过 config/config.json 管理运行参数,包括服务端口、线程数、数据库连接串、JWT 参数、写入队列大小、批量写入大小、飞书机器人地址等。

这样可以避免把环境参数写死在代码里,方便在开发、测试、部署环境中切换。

4.2 元数据驱动的数据映射

项目中有一个比较关键的配置文件:metadata.json。

它定义了逻辑表名、真实数据库表名、主键、字段映射和写入白名单。例如前端或上位机传入的是中文字段名,后端会根据元数据映射到数据库字段。

这种设计带来了几个好处:

4.3 批量写入引擎

数据写入没有直接在 HTTP 请求线程里执行,而是进入 BatchIngestor 队列。

写入流程大致是:

  1. 上位机调用 /api/test_data;
  2. 后端校验 JWT,并提取 client_id;
  3. 根据 metadata.json 校验表名和写入权限;
  4. 将数据转换成 RowTask 放入队列;
  5. 后台 Worker 从队列中批量取数据;
  6. 按表分组;
  7. 使用事务批量写入 PostgreSQL;
  8. 如果批量写入失败,则降级为逐条写入。

数据写入流程图:

数据写入流程图

这个设计解决了高频数据上报时 HTTP 请求被数据库阻塞的问题,也避免了单条插入带来的性能浪费。

4.4 数据库连接管理

查询接口使用连接池,连接通过 RAII 的方式自动归还。每次获取连接时会检查连接是否有效,如果连接断开,会尝试重连或重建连接。

写入 Worker 则使用独立长期连接,避免后台写入线程和前台查询线程争抢同一个连接池。

4.5 动态 CRUD

系统提供了通用接口:

这些接口不是直接拼接任意 SQL,而是先通过 metadata.json 校验逻辑表和字段,再由后端生成参数化 SQL。

这样既保留了动态查询的灵活性,也降低了 SQL 注入风险。

4.6 JWT 认证与设备权限

登录接口 /api/login 支持普通用户和上位机设备两种模式。

普通用户 Token 使用配置文件中的过期时间;上位机 Token 会额外带上 client_id,有效期默认一年。

设备上报数据时,后端会读取 Token 中的 client_id,再和 metadata.json 中的 write_permission 进行匹配。这样可以限制不同设备只能写入自己被授权的数据表。

4.7 实时报警推送

报警模块不是直接在数据写入逻辑里硬编码判断,而是通过 PostgreSQL 的 LISTEN / NOTIFY 机制接收数据库侧产生的报警事件。

后端的 PgListener 会监听 electrolyzer_issue_channel。当数据库发出通知时,后端解析报警 JSON,然后交给 AlarmManager 处理。

AlarmManager 做了三件事:

报警链路图:

报警链路图

5. 项目中的工程思考

这个项目里比较有价值的点,不是单独某个接口,而是整体工程结构:

第一,写入链路做了异步化。HTTP 请求只负责接收和入队,数据库写入交给后台 Worker,降低了上位机请求超时的风险。

第二,使用元数据驱动表结构。新增业务表时,可以更多地通过配置扩展,而不是复制一套新的 Handler。

第三,数据库操作尽量做了安全边界。动态查询、更新、删除都要求表名和字段经过白名单校验,避免完全开放 SQL 能力。

第四,报警链路和业务写入链路解耦。报警由数据库通知触发,后端负责转发和限流,这样后续扩展短信、邮件、企业微信等渠道也比较自然。

6. 当前不足与后续优化

这个项目目前已经具备核心能力,但仍然有一些可以继续优化的地方:

7. 总结

这个后端项目主要完成了工业数据服务中的几个关键问题:高频数据接入、批量写入、动态查询、权限控制、实时报警和外部通知。

对我来说,这个项目的价值不仅在于实现了一组 API,更重要的是完整经历了一个后端服务从路由设计、配置管理、数据库封装、并发写入、安全控制到报警推送的工程化过程。

它也让我更清楚地理解了:在实际业务系统里,稳定性、可维护性和可扩展性往往比单个功能点更重要。一个好的后端服务,不只是能把数据写进去,还要能在高频请求、异常数据、数据库波动、权限隔离和报警通知这些场景下持续工作。

载入内容