第二章:架构设计原则——分层架构、高可用与可扩展性

大家好,我是老赵。今天咱们聊聊设备数据平台的骨架——架构设计原则。

说实话,我见过太多平台一开始跑得挺欢,数据量一上来就崩了。为什么?底层架构没想清楚。这一章,我把这些年踩过的坑、总结出的经验,掰开揉碎了讲给你听。

一、分层架构:各司其职,互不干扰

先看一张图,这是我个人习惯用的分层结构:

应用层 API网关 · 可视化大屏 · 告警通知 · 报表系统 计算层 实时流计算 · 离线批处理 · 规则引擎 · 数据清洗 存储层 时序数据库 · 关系数据库 · 对象存储 · 消息队列 传输层 MQTT · Kafka · HTTP/2 · gRPC · 数据压缩与加密 采集层 设备SDK · 边缘网关 · 协议适配 · 数据采集代理 数据流方向:设备 → 采集 → 传输 → 存储 → 计算 → 应用

这张图我每次做架构评审都会拿出来。说白了,分层就是让每一层只干自己的事,不越界。

1. 采集层

这是最贴近设备的一层。我在项目中遇到过,有的团队把采集逻辑和业务逻辑混在一起写,结果设备协议一换,整个系统都得改。正确的做法是:

  • 只负责数据接入和协议转换
  • 不做任何业务判断
  • 支持多种协议:MQTT、CoAP、Modbus、OPC UA
我的经验:采集层最好做成插件化。每接入一种新设备,写一个插件就行,不用动主程序。

2. 传输层

数据从采集层出来,怎么安全、快速地送到后端?这就是传输层的事。

我建议用消息队列做缓冲。为什么?因为设备上报频率不稳定,高峰期可能每秒几万条,低谷期又没数据。消息队列能削峰填谷。

// 伪代码:传输层配置示例
transport:
  protocol: mqtt
  broker: tcp://mqtt-broker:1883
  topic: /devices/{deviceId}/data
  qos: 1
  buffer:
    type: kafka
    partitions: 6
    replication: 3

3. 存储层

嗯,这里要注意。很多新手上来就用关系数据库存时序数据,结果查询慢得要命。

正确的做法是:

  • 时序数据 → 用 InfluxDB、TimescaleDB 或 TDengine
  • 设备元数据 → 用 MySQL 或 PostgreSQL
  • 文件/日志 → 用对象存储(MinIO、S3)
避坑指南:我曾经把设备历史数据全存在一个MySQL表里,半年后单表超过10亿行,查询一次要30秒。后来花了整整两周做数据迁移,教训深刻。

4. 计算层

数据存好了,怎么用?计算层负责处理。

我习惯把计算分为两类:

  • 实时计算:用 Flink 或 Spark Streaming,处理秒级告警、实时聚合
  • 离线计算:用 Spark 或 Hive,做日报、月报、趋势分析

5. 应用层

最上层,面向用户。API网关、可视化大屏、告警通知、报表系统都在这里。

我个人建议:应用层不要直接访问存储层,必须通过计算层或服务层。否则一旦业务逻辑变化,底层存储结构跟着改,维护成本极高。

二、高可用设计:别让单点毁了你的平台

设备数据平台最怕什么?服务挂了,数据丢了。

我在一家工厂遇到过,半夜两点,采集服务挂了,整整6小时的数据全部丢失。第二天厂长差点把我吃了。从那以后,我对高可用设计特别较真。

1. 消除单点故障

每个组件至少部署两个实例。你想想看,如果只有一个采集节点,它挂了,所有设备都连不上。

组件 高可用方案 推荐部署数
采集服务 多节点 + 负载均衡 ≥2
消息队列 Kafka 集群 ≥3
数据库 主从复制 + 自动故障转移 ≥2
API网关 多实例 + DNS轮询 ≥2

2. 数据冗余与备份

数据是平台的命根子。我建议:

  • 关键数据实时同步到备库
  • 每天全量备份,每小时增量备份
  • 备份文件存到不同机房
核心原则:任何一台服务器宕机,系统都能自动切换,用户无感知。

3. 熔断与降级

流量暴增怎么办?不能硬扛。

我常用的策略:

  • 熔断:某个服务响应超时,直接断开,避免雪崩
  • 降级:非核心功能暂时关闭,保证核心功能可用
  • 限流:每秒最多处理多少请求,超出的排队或丢弃
# 限流配置示例(Nginx)
limit_req_zone $binary_remote_addr zone=api_limit:10m rate=100r/s;
location /api/ {
    limit_req zone=api_limit burst=20 nodelay;
    proxy_pass http://backend;
}

三、可扩展性设计:为未来留好接口

做架构设计,不能只看眼前。你想想看,今天接入100台设备,明天可能接入10000台。架构必须能水平扩展。

1. 无状态设计

这是最基本的原则。每个服务实例不保存本地状态,所有状态都放到外部存储(Redis、数据库)。

为什么?因为无状态的服务可以随意加实例。流量大了,加两台机器就行。

2. 数据分片

数据量大了,单库扛不住。我习惯按设备ID哈希分片:

// 分片策略示例
shard_id = hash(device_id) % 8
// 设备ID为1001的数据,存到第3个分片

这样每增加一个分片,容量就增加一份。我曾经帮一个客户从4个分片扩展到32个分片,整个过程在线完成,业务无中断。

3. 异步解耦

层与层之间通过消息队列通信,而不是直接调用。这样做的好处:

  • 采集层挂了,不影响存储层
  • 存储层慢了,不影响采集层
  • 可以随时加新的消费端,扩展功能
小技巧:设计接口时,参数尽量用JSON或Protobuf,别用自定义二进制协议。扩展字段时,JSON加个字段就行,不用改协议。

4. 插件化与微服务

每个功能模块独立部署、独立扩展。比如:

  • 告警服务一个微服务
  • 报表服务一个微服务
  • 设备管理一个微服务

哪个模块压力大,就单独给它加实例。不用整个系统一起扩,省钱又灵活。

小结

这一章的内容,说白了就三句话:

  • 分层:各层职责清晰,互不干扰
  • 高可用:消除单点,数据不丢,服务不挂
  • 可扩展:无状态、分片、异步、微服务

这些原则,我在每个项目中都会反复强调。你照着做,平台至少能扛住百万级设备。不按这个来,早晚要重构——嗯,我见过太多这样的案例了。

专注资料整理