Skip to content

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, busrefresh

4.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 BusSpring 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-amqpspring-cloud-starter-bus-kafka

Q4:如何只刷新指定服务?

  使用 destination 参数:/actuator/busrefresh?destination=order-service:** 只刷新 order-service 的所有实例。destination=order-service:8081 只刷新指定的单个实例。