Appearance
Bus 详解:消息总线
Spring Cloud Bus 是 Spring Cloud 中的消息总线组件,它通过轻量级消息代理(RabbitMQ / Kafka)将分布式系统的节点连接起来,实现配置变更广播、状态传播等事件驱动功能。
一、Bus 是必须的吗?
不是必须的。 Bus 是一个"锦上添花"的增强组件,不是微服务基础设施。是否引入 Bus 取决于你的配置刷新需求:
| 场景 | 需要 Bus 吗? | 原因 |
|---|---|---|
| 只有 1-2 个服务实例 | ❌ 不需要 | 手动刷新每个实例就几秒钟的事 |
| 用 Nacos 做配置中心 | ❌ 不需要 | Nacos 自带自动推送,无需 Bus |
| 用 Config + 手动逐台刷新 | ❌ 不需要 | 运维脚本逐台调 /actuator/refresh 也行 |
| Config + 几十上百个实例 | ✅ 需要 | 手动刷新不现实,Bus 一键广播 |
| 需要自定义事件广播 | ✅ 需要 | Bus 的 RemoteApplicationEvent 机制 |
Bus 的本质:把"逐台通知"变成"一键广播"
没有 Bus: 有 Bus:
Config Server Config Server
│ │
├── POST /refresh → 实例1 │ POST /busrefresh
├── POST /refresh → 实例2 │
├── POST /refresh → 实例3 ▼
├── ... ┌──────────────┐
└── POST /refresh → 实例N │ MQ (RabbitMQ)│ ← Bus 通过 MQ 广播
逐台操作,O(n) 成本 └──┬──┬──┬──┬──┘
│ │ │ │
▼ ▼ ▼ ▼
实例1 2 3 ... N
一次操作,O(1) 成本一句话:Bus 解决的是"实例多了怎么批量刷新"的问题。实例少的时候不需要,用了 Nacos 也不需要。
1.1 Bus 和 Config 的关系
Bus 的核心使命就是辅助 Config 做配置批量刷新。 两者是黄金搭档,各司其职:
┌──────────────────────────────────────────────────────────────────┐
│ Config + Bus 分工 │
│ │
│ ┌──────────────────────┐ ┌──────────────────────┐ │
│ │ Config │ │ Bus │ │
│ │ │ │ │ │
│ │ 管"存" │ │ 管"传" │ │
│ │ - 配置存在哪(Git) │ │ - 配置变更通知传播 │ │
│ │ - 怎么拉取配置 │ │ - 一键广播全集群 │ │
│ │ - 配置加密解密 │ │ - 自定义事件广播 │ │
│ │ - 配置版本管理 │ │ │ │
│ └──────────────────────┘ └──────────────────────┘ │
│ │ │ │
│ └───────────┬───────────────┘ │
│ │ │
│ ▼ │
│ Config + Bus = 完整的配置治理方案 │
└──────────────────────────────────────────────────────────────────┘Bus 的两大用途:
| 用途 | 占比 | 说明 |
|---|---|---|
| 辅助 Config 批量刷新配置 | 90% | 最核心场景,POST /busrefresh 一键刷新所有实例 |
| 自定义事件广播 | 10% | 如"清空所有实例的本地缓存"、"通知下线"等 |
Bus 本身不依赖 Config——它底层是 Spring Cloud Stream 的封装,可以广播任意自定义事件。但离开了 Config 的配置刷新场景,Bus 基本没人单独用。90% 的项目引入 Bus 就是为了 Config 批量刷新。
二、为什么需要 Bus?
2.1 没有 Bus 的配置刷新
Config Server ← Git 配置变更
│
│ 手动逐个调用 /actuator/refresh
▼
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 实例 1 │ │ 实例 2 │ │ 实例 3 │ │ 实例 4 │
│ refresh │ │ refresh │ │ refresh │ │ refresh │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
问题:服务有 10 个实例,就要调 10 次 refresh,运维成本高2.2 有了 Bus 的配置刷新
Config Server ← Git 配置变更
│
│ 调用一次 /actuator/busrefresh
│
▼
┌──────────────────────────────────────────────────────────────┐
│ Message Broker │
│ (RabbitMQ / Kafka) │
│ │
│ 广播 RefreshRemoteApplicationEvent 事件 │
└──────┬──────────────┬──────────────┬─────────────────────────┘
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐
│ 实例 1 │ │ 实例 2 │ │ 实例 3 │ │ 实例 4 │
│ 自动刷新 │ │ 自动刷新 │ │ 自动刷新 │ │ 自动刷新 │
└──────────┘ └──────────┘ └──────────┘ └──────────┘
一次调用,所有实例自动刷新三、核心架构
┌──────────────────────────────────────────────────────────────────┐
│ Bus 架构图 │
│ │
│ ┌──────────────┐ │
│ │ Git 仓库 │ ← 配置变更 │
│ └──────┬───────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────┐ │
│ │ Config Server │ │
│ │ + Bus 依赖 │ │
│ │ /actuator/ │ │
│ │ busrefresh │──── 发布 RefreshRemoteApplicationEvent ──┐ │
│ └──────────────────┘ │ │
│ │ │
│ ┌──────────────────────────────────────────────────────────┐ │ │
│ │ Message Broker │ │ │
│ │ (RabbitMQ / Kafka) │←──┘ │
│ │ 广播事件 │ │
│ └──────┬──────────────┬──────────────┬─────────────────────┘ │
│ │ │ │ │
│ ▼ ▼ ▼ │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ 实例 1 │ │ 实例 2 │ │ 实例 3 │ │
│ │ + Bus │ │ + Bus │ │ + Bus │ │
│ │ 监听事件 │ │ 监听事件 │ │ 监听事件 │ │
│ │ refresh │ │ refresh │ │ refresh │ │
│ └──────────┘ └──────────┘ └──────────┘ │
└──────────────────────────────────────────────────────────────────┘四、快速上手
4.1 依赖
xml
<!-- Config Server + Bus(RabbitMQ) -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>xml
<!-- Config Client + Bus -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>4.2 配置
yaml
# Config Server (application.yml)
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
management:
endpoints:
web:
exposure:
include: busrefresh # 暴露 busrefresh 端点yaml
# Config Client (bootstrap.yml)
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
cloud:
bus:
enabled: true
refresh:
enabled: true # 启用自动刷新
management:
endpoints:
web:
exposure:
include: refresh, busrefresh4.3 触发刷新
bash
# 方式一:广播所有服务
curl -X POST http://localhost:8888/actuator/busrefresh
# 方式二:指定目标服务(精确刷新)
curl -X POST http://localhost:8888/actuator/busrefresh?destination=order-service:8081
# 方式三:指定目标服务(通配符)
curl -X POST http://localhost:8888/actuator/busrefresh?destination=order-service:**五、工作原理
5.1 事件流
配置变更触发流程:
1. Git Webhook 或手动调用 /actuator/busrefresh
│
2. Config Server 发布 RefreshRemoteApplicationEvent
│
3. Message Broker 广播事件到所有监听的 Bus 节点
│
4. 每个 Config Client 收到事件后调用 ContextRefresher.refresh()
│
5. @RefreshScope 标注的 Bean 被重新初始化
│
6. 新配置生效5.2 远程事件
Bus 支持两种远程事件:
| 事件 | 作用 | 触发方式 |
|---|---|---|
RefreshRemoteApplicationEvent | 刷新配置 | POST /actuator/busrefresh |
EnvironmentChangeRemoteApplicationEvent | 环境变更通知 | 自定义业务逻辑 |
5.3 自定义事件
java
// 自定义远程事件
public class CustomRemoteEvent extends RemoteApplicationEvent {
private String message;
public CustomRemoteEvent(Object source, String originService,
String destinationService, String message) {
super(source, originService, destinationService);
this.message = message;
}
// getter/setter
}
// 发布事件
@RestController
public class EventController {
@Autowired
private ApplicationEventPublisher publisher;
@PostMapping("/publish")
public void publish(@RequestBody String message) {
publisher.publishEvent(new CustomRemoteEvent(
this, "config-server", "**", message));
}
}
// 监听事件
@Component
public class CustomEventListener {
@EventListener
public void handle(CustomRemoteEvent event) {
log.info("收到自定义事件:{}", event.getMessage());
}
}六、Bus vs Stream
| 维度 | Spring Cloud Bus | Spring Cloud Stream |
|---|---|---|
| 定位 | 消息总线(配置刷新、事件广播) | 消息驱动(业务消息处理) |
| 核心功能 | 配置热刷新、状态传播 | 屏蔽 MQ 差异,统一编程模型 |
| 编程模型 | 事件监听(RemoteApplicationEvent) | Supplier / Function / Consumer |
| 使用场景 | 配置刷新、集群通知 | 异步解耦、消息处理、流式计算 |
| 触发方式 | 主动调用端点 | 消息到达自动触发 |
| 与 Config 关系 | 紧密配合(Config + Bus) | 独立使用 |
一句话: Bus 是"控制面"(配置变更广播),Stream 是"数据面"(业务消息处理)。两者可以共存。
七、常见问题
7.1 配置刷新后 @Value 不生效?
java
// ❌ 错误:没有 @RefreshScope
@RestController
public class MyController {
@Value("${app.version}")
private String version;
}
// ✅ 正确:添加 @RefreshScope
@RestController
@RefreshScope
public class MyController {
@Value("${app.version}")
private String version;
}7.2 刷新后数据库连接池不更新?
数据库连接池等基础设施 Bean 的刷新需要重建整个 ApplicationContext,仅 @RefreshScope 不够。可通过 @RefreshScope 包装 DataSourceProperties 类,或使用占位符方式。更好的方案是使用 Nacos(原生支持连接池配置刷新)。
7.3 Git Webhook 自动触发
Git Webhook 配置:
1. Git 仓库设置 Webhook URL: http://config-server:8888/monitor
2. Config Server 添加 spring-cloud-config-monitor 依赖
3. 配置变更 push → Webhook → Config Server → Bus 广播 → 所有实例刷新xml
<!-- Config Server 添加 monitor 依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-monitor</artifactId>
</dependency>八、面试要点
Q1:Bus 如何实现配置批量刷新?
通过 Message Broker 广播 RefreshRemoteApplicationEvent 事件。Config Server 接收 /busrefresh 请求后发布事件,所有订阅该事件的 Bus 节点自动执行 ContextRefresher.refresh(),重建 @RefreshScope Bean。
Q2:Bus 和 Stream 的区别?
Bus 是控制面(配置刷新广播),Stream 是数据面(业务消息处理)。Bus 基于 RemoteApplicationEvent 事件模型,Stream 基于 Supplier/Function/Consumer 函数式模型。两者底层都可以用 RabbitMQ 或 Kafka。
Q3:Bus 支持哪些 MQ?
RabbitMQ 和 Kafka。分别对应 spring-cloud-starter-bus-amqp 和 spring-cloud-starter-bus-kafka。
Q4:如何只刷新指定服务?
使用 destination 参数:/actuator/busrefresh?destination=order-service:** 只刷新 order-service 的所有实例。destination=order-service:8081 只刷新指定的单个实例。
