MQTT 桥接(Bridging)介绍:解决弱网可靠传输、断网续传


发布者 ourjs  发布时间 1787275549994
关键字 消息中间件 

MQTT 桥接(Bridging)介绍

MQTT 桥接(Bridge)是指一台 MQTT broker 作为客户端连接到另一台 broker,并在两者之间转发消息的机制。常见于「本地(边缘端)→ 云端」的物联网架构中:本地部署一台轻量 broker,负责就近接入现场设备;云端部署一台 broker,负责汇聚所有本地节点的数据。

桥接模式的好处

  1. 弱网可靠传输:本地 broker 与云端断开时,消息先在本地缓存(持久化会话 + QoS),网络恢复后自动续传,做到「断网不丢数据」。
  2. 降低云端连接数:现场大量设备只需连本地 broker,云端只需面对「每个本地节点一条桥接连接」,而不是成百上千的设备直连,大幅降低云端压力与运维复杂度。
  3. 本地自治:即使与云端断连,本地设备之间的消息仍可在本地 broker 内正常路由,现场业务不受影响。
  4. 安全隔离:本地 broker 作为唯一出网出口,可在桥接层统一做认证、加密(TLS)和 ACL 控制,避免设备直接暴露在公网。
  5. 统一接入标准:本地与云端统一使用 MQTT 协议,Topic 规划一致,便于横向扩展多个本地节点。

应用场景

  • 工业现场数据采集:工厂、站点等本地设备采集数据,经本地 broker 桥接到云平台做集中监控与数据分析。
  • 边缘计算协同:本地完成实时处理,云端完成大数据分析、历史存储与全局调度。
  • 远程设备管理:云端通过桥接反向下发指令到本地,实现远程配置、控制与 OTA 升级。
  • 多节点汇聚:大量分布式节点(如分布式站点、分散设备群)通过桥接统一汇入一个云端中心。

几种MQTT broker比较,比较

emqx

  1. 社区版只能单结点部署
  2. 社区版有消息配额

针对本地与云通信架构(本地(边缘端)Mosquitto 桥接 → 云端 VerneMQ),以下是三个问题的具体实现方法,合并到一个带配置文件启动的完整方案中。

VerneMQ

  1. 配制比较复杂
  2. Star数较少,可能有较多BUG

Mosquitto

Mosquitto 用 C 语言编写,单节点性能非常强悍。根据社区实际部署案例和压测数据:

  • 并发连接数:单台云服务器(4核8G)经系统调优(主要是 ulimit -n 和 TCP 参数)后,Mosquitto 可以稳定支撑 5万~10万 的持久 TCP 长连接。1万个本地(边缘端)节点(每个节点一条桥接连接)完全在其能力范围内。
  • 消息吞吐量:单节点每秒可处理 数十万到百万条 小消息的路由,对于遥测类数据(每秒一条甚至更低频率)绰绰有余。
  • 资源占用:维持 1 万连接时,内存占用通常在 数百 MB 到 1GB 左右,CPU 消耗主要取决于消息速率,一般不会成为瓶颈。

结论:单从“接得住”这个角度看,1万个本地(边缘端)节点用一个 Mosquitto 单节点是完全可行的。

Mosquitto HA: 双机冗余部署

Keepalived + 主备自动切换

原理:两台 Mosquitto 服务器,一主一备。通过 keepalived 提供一个虚拟 IP(VIP),本地(边缘端)桥接都指向这个 VIP。主节点宕机时,VIP 自动飘到备节点,并启动备节点的 Mosquitto 服务。本地(边缘端)桥接断开后自动重连,恢复数据上报。

优点

  • 部署简单,不需共享存储。
  • 自动切换,中断时间通常 < 30 秒。
  • 本地(边缘端)上行数据零丢失(本地缓存续传)。

缺点

  • 主备切换期间,下行指令会短暂中断,原暂存的下行消息会丢失(可通过业务重试机制弥补)。
  • 需要编写简单的检测和拉起脚本。

实现步骤(假设两台机器 192.168.1.10 和 192.168.1.20,VIP 为 192.168.1.100):

  1. 两台机器都安装 Mosquitto 并保持相同配置(特别是 password_fileacl_file、桥接相关的配置没有,这是云端)。两台的 /data/mosquitto-cloud 目录结构一致,密码文件手工同步。
  2. 备机默认不启动 Mosquitto(或启动但监听不同端口),通过 keepalived 的 notify_master 脚本在升主时启动 Mosquitto,在降为备时停止。
  3. keepalived 配置示例(主节点):
    vrrp_instance VI_1 {
        state MASTER
        interface eth0
        virtual_router_id 51
        priority 100
        advert_int 1
        authentication {
            auth_type PASS
            auth_pass 1234
        }
        virtual_ipaddress {
            192.168.1.100
        }
        notify_master "/etc/keepalived/become_master.sh"
        notify_backup "/etc/keepalived/become_backup.sh"
    }
    
    备机类似,state BACKUPpriority 90
  4. become_master.sh 脚本内容:
    #!/bin/bash
    systemctl start mosquitto
    
    become_backup.sh
    #!/bin/bash
    systemctl stop mosquitto
    
  5. 本地(边缘端)桥接配置中 address 一律填写 192.168.1.100

这样,主节点故障后,VIP 漂移,备机启动 Mosquitto,本地(边缘端)桥接会在 restart_timeout 时间内重连,继续上传数据。这是目前社区最成熟的 Mosquitto HA 做法。

云端Mosquitto部署

在云端部署 Mosquitto 作为本地(边缘端)节点接入点,核心任务是开启认证、持久化会话、并准备好接收来自本地(边缘端)Mosquitto 桥接的 MQTT 连接。以下是完整的云端部署过程(使用 Docker)。

1. 目录准备

mkdir -p /data/mosquitto-cloud/config /data/mosquitto-cloud/data /data/mosquitto-cloud/log
# 容器内 mosquitto 用户 uid=1883
chown -R 1883:1883 /data/mosquitto-cloud

2. 编写云端 Mosquitto 配置文件

创建 /data/mosquitto-cloud/config/mosquitto.conf

# 监听 1883 端口
listener 1883 0.0.0.0

# 禁止匿名,使用密码文件
allow_anonymous false
password_file /mosquitto/config/passwd

# 持久化(断网续传需要)
persistence true
persistence_location /mosquitto/data
persistent_client_expiration 30d

# 日志
log_dest file /mosquitto/log/mosquitto.log
log_type error
log_type warning
log_type notice
log_type information

说明

  • 同时监听 1883(明文,内网桥接或测试)和 8883(TLS,公网桥接)。
  • 必须使用 allow_anonymous false 并指定 password_file,所有客户端(包括本地(边缘端)桥接)必须提供用户名密码。
  • 证书文件(ca.crtserver.crtserver.key)请提前准备好,若暂时没有 TLS 证书,可先只开放 1883,生产环境务必启用 8883。

3. 生成密码文件

docker run -d --name mosquitto-temp -v /data/mosquitto-cloud/config:/config eclipse-mosquitto
docker exec -it mosquitto-temp mosquitto_passwd -c /config/passwd ocean-dev
# 完成后删除临时容器
docker rm -f mosquitto-temp

4. 权限

【可选】证书文件放入 /data/mosquitto-cloud/config/certs/ 目录下,例如:

chown -R 1883:1883 /data/mosquitto-cloud/data
chown 1883:1883 /data/mosquitto-cloud/config/passwd

5. 启动容器

前台模式

docker run --rm -it \
  -v /data/mosquitto-cloud/config/mosquitto.conf:/mosquitto/config/mosquitto.conf \
  -v /data/mosquitto-cloud/config/passwd:/mosquitto/config/passwd \
  -v /data/mosquitto-cloud/data:/mosquitto/data \
  -v /data/mosquitto-cloud/log:/mosquitto/log \
  -p 1883:1883 \
  eclipse-mosquitto

后台模式

docker run -d \
  --name mosquitto-cloud \
  --restart unless-stopped \
  -v /data/mosquitto-cloud/config/mosquitto.conf:/mosquitto/config/mosquitto.conf \
  -v /data/mosquitto-cloud/config/passwd:/mosquitto/config/passwd \
  -v /data/mosquitto-cloud/data:/mosquitto/data \
  -v /data/mosquitto-cloud/log:/mosquitto/log \
  -p 1883:1883 \
  eclipse-mosquitto

6. 验证部署

查看日志确认启动正常

docker logs mosquitto-cloud

应无严重错误,并看到监听端口信息。

本地测试认证是否生效

# 无认证会被拒绝
mosquitto_sub -h 192.168.1.100 -t 'test/topic' -v
# 预期报错:Connection Refused: not authorised

# 带正确认证应成功
mosquitto_sub -h 192.168.1.100 -t 'test/topic' -u ocean-dev -P Initpass@word1 -v

# 消息订阅发送测试
mosquitto_sub -h 10.4.96.16 -t 'test/topic' -u ocean-dev -P Initpass@word1 -v
mosquitto_pub -h 10.4.96.16 -t 'test/topic' -m "hello world from EMVQ" -u ocean-dev -P Initpass@word1

本地(边缘端)Mosquitto 部署

在本地(边缘端)部署一个 Mosquitto,通过桥接连接到云端 Mosquitto,是保障弱网下数据不丢失。 以下操作在本地的 Linux 主机或虚拟机上完成(假设使用 Docker)。

1. 目录准备与权限设置

mkdir -p /data/mosquitto-local/config /data/mosquitto-local/data /data/mosquitto-local/log
# 容器内 mosquitto 用户 uid=1883
chown -R 1883:1883 /data/mosquitto-local

2. 编写本地 Mosquitto 配置文件

创建 /data/mosquitto-local/config/mosquitto.conf

# 网络隔离; 供本地调用;本地局域网 IP 和云端局域网连接
# listener 1883 127.0.0.1
# listener 1883 192.168.1.100
listener 1883 0.0.0.0
allow_anonymous true

# 持久化
persistence true
persistence_location /mosquitto/data
max_queued_messages 100000
persistent_client_expiration 30d

# 日志
log_dest file /mosquitto/log/mosquitto.log
log_type error
log_type warning
log_type notice
log_type information

# ========== 桥接到云端 ==========
connection cloud-bridge
address 10.4.96.16:1883
# topic <主题> <方向> <QoS> <本地前缀> <远程前缀>; 方向 both:双向同步; QoS 2:桥接传输使用 QoS 2(精确一次)
topic # both 2 "" ""
cleansession false
notifications true
start_type automatic
restart_timeout 10 30
keepalive_interval 60
bridge_protocol_version mqttv311
remote_username ocean-dev
remote_password Initpass@word1

注意

  • address 改为你云端 Mosquitto 的实际 IP。
  • remote_username 和 remote_password 必须与云端 passwd 文件中配置的用户名密码一致。
  • 如果云端只开启了 TLS(8883),则需要将 address 改为 192.168.1.100:8883 并添加 bridge_cafile 等 TLS 参数。

3. 启动本地 Mosquitto 容器

# debug
docker run -it \
  --name mosquitto-local \
  --restart unless-stopped \
  -p 1883:1883 \
  -v /data/mosquitto-local/config/mosquitto.conf:/mosquitto/config/mosquitto.conf \
  -v /data/mosquitto-local/data:/mosquitto/data \
  -v /data/mosquitto-local/log:/mosquitto/log \
  eclipse-mosquitto

# deploy
docker run -d \
  --name mosquitto-local \
  --restart unless-stopped \
  -p 1883:1883 \
  -v /data/mosquitto-local/config/mosquitto.conf:/mosquitto/config/mosquitto.conf \
  -v /data/mosquitto-local/data:/mosquitto/data \
  -v /data/mosquitto-local/log:/mosquitto/log \
  eclipse-mosquitto

4. 验证桥接是否成功

查看容器日志

docker logs mosquitto-local | grep -i bridge

正常时会看到类似:

Notice: Connecting bridge cloud-bridge (192.168.1.100:1883)
Notice: Bridge cloud-bridge is connected

功能测试

  1. 在本地发布一条消息:
    docker exec mosquitto-local mosquitto_pub -t local/test -m "hello from local" -q 2
    
  2. 在云端订阅验证是否能收到:
    # 在云端服务器上
    docker exec mosquitto-cloud mosquitto_sub -t 'local/#' -u bridge_user -P YourPassword -v
    
  3. 反向测试:云端发布,本地订阅,确认双向通信正常。

断网续传测试

  1. 断开本地网络(或临时停止云端 Mosquitto)。
  2. 本地继续发布几条消息到本地 Broker。
  3. 恢复网络后,检查云端是否收到所有离线消息,确保没有丢失。

5. 可选:本地也启用密码认证

如果希望本地也要求客户端认证,可添加:

allow_anonymous false
password_file /mosquitto/config/passwd

并使用之前的方法生成密码文件并挂载。不过,桥接使用的 remote_username 和 remote_password 是云端认证,本地认证与桥接无关。


部署完成后,mosquitto-local 即成为本地消息枢纽,同时自动与云端保持同步,实现弱网下的可靠通信。









  开源的 OurJS
OurJS开源博客已经迁移到 OnceOA 平台。

  关注我们
扫一扫即可关注我们:
OnceJS

OnceOA