Spring Cloud Stream 消息驱动详解
一、知识概述
Spring Cloud Stream 是构建消息驱动微服务的框架,它提供了统一的编程模型,屏蔽了底层消息中间件的差异。开发者可以使用统一的 API 进行消息的生产和消费,支持 RabbitMQ、Kafka、RocketMQ 等多种消息中间件。
消息驱动的核心概念:
- Binder:消息中间件绑定器
- Binding:消息通道绑定
- Message:消息体
- Channel:消息通道
理解消息驱动的原理,是构建异步、解耦微服务系统的重要技能。
二、知识点详细讲解
2.1 架构模型
应用层
│
├── Input Channel(输入通道)
│ │
│ ▼
│ Binder(绑定器)
│ │
│ ▼
│ 消息中间件(RabbitMQ/Kafka/RocketMQ)
│ │
│ ▼
├── Output Channel(输出通道)
│
应用层
2.2 核心概念
Binder
负责与消息中间件交互,提供:
- 连接管理
- 消息发送/接收
- 序列化/反序列化
Binding
连接应用与 Binder 的桥梁:
- Input Binding:消费者绑定
- Output Binding:生产者绑定
Message
消息的基本单元:
- Header:消息头
- Payload:消息体
2.3 消息中间件对比
| 特性 | RabbitMQ | Kafka | RocketMQ |
|---|---|---|---|
| 吞吐量 | 中 | 高 | 高 |
| 延迟 | 低 | 中 | 低 |
| 顺序性 | ❌ | ✅ | ✅ |
| 事务消息 | ❌ | ✅ | ✅ |
| 延迟消息 | ✅ | ✅ | ✅ |
| 消息回溯 | ❌ | ✅ | ✅ |
2.4 消费模型
发布订阅(Publish-Subscribe)
- 一条消息被多个消费者消费
- 每个消费者独立消费
消费者组(Consumer Group)
- 一条消息只被组内一个消费者消费
- 实现负载均衡
三、代码示例
3.1 基础配置
<!-- pom.xml -->
<!-- RabbitMQ Binder -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
<!-- Kafka Binder -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-kafka</artifactId>
</dependency>
# application.yml
spring:
cloud:
stream:
# 绑定器配置
binders:
rabbit-binder:
type: rabbit
environment:
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
kafka-binder:
type: kafka
environment:
spring:
kafka:
bootstrap-servers: localhost:9092
# 绑定配置
bindings:
# 输出通道
userOutput:
destination: user-topic
content-type: application/json
binder: rabbit-binder
# 输入通道
userInput:
destination: user-topic
group: user-group
binder: rabbit-binder
3.2 定义消息通道
import org.springframework.cloud.stream.annotation.*;
import org.springframework.messaging.*;
import org.springframework.stereotype.Component;
// 消息通道定义
public interface UserChannels {
// 输出通道(生产者)
String USER_OUTPUT = "userOutput";
@Output(USER_OUTPUT)
MessageChannel userOutput();
// 输入通道(消费者)
String USER_INPUT = "userInput";
@Input(USER_INPUT)
SubscribableChannel userInput();
}
// 多通道定义
public interface OrderChannels {
@Output("orderOutput")
MessageChannel orderOutput();
@Input("orderInput")
SubscribableChannel orderInput();
@Output("paymentOutput")
MessageChannel paymentOutput();
@Input("paymentInput")
SubscribableChannel paymentInput();
}
3.3 启用消息通道
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.stream.annotation.EnableBinding;
@SpringBootApplication
@EnableBinding({UserChannels.class, OrderChannels.class})
public class StreamApplication {
public static void main(String[] args) {
SpringApplication.run(StreamApplication.class, args);
}
}
3.4 消息生产者
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
@Service
@EnableBinding(UserChannels.class)
public class UserProducer {
@Autowired
private UserChannels userChannels;
// 发送简单消息
public void sendMessage(UserDTO user) {
userChannels.userOutput().send(
MessageBuilder.withPayload(user).build()
);
}
// 发送带头的消息
public void sendMessageWithHeaders(UserDTO user, Map<String, Object> headers) {
Message<UserDTO> message = MessageBuilder
.withPayload(user)
.copyHeaders(headers)
.setHeader("type", "user-created")
.setHeader("version", "1.0")
.build();
userChannels.userOutput().send(message);
}
// 发送延迟消息(RabbitMQ)
public void sendDelayedMessage(UserDTO user, int delayMs) {
Message<UserDTO> message = MessageBuilder
.withPayload(user)
.setHeader("x-delay", delayMs)
.build();
userChannels.userOutput().send(message);
}
// 发送有序消息(Kafka)
public void sendOrderedMessage(UserDTO user, String key) {
Message<UserDTO> message = MessageBuilder
.withPayload(user)
.setHeader("kafka_messageKey", key)
.build();
userChannels.userOutput().send(message);
}
}
3.5 消息消费者
import org.springframework.cloud.stream.annotation.*;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Service;
@Service
public class UserConsumer {
// 基础消费者
@StreamListener(UserChannels.USER_INPUT)
public void handleUser(UserDTO user) {
System.out.println("收到用户消息: " + user);
// 处理业务逻辑
}
// 处理带头的消息
@StreamListener(UserChannels.USER_INPUT)
public void handleMessage(Message<UserDTO> message) {
UserDTO user = message.getPayload();
String type = (String) message.getHeaders().get("type");
System.out.println("收到消息类型: " + type);
System.out.println("消息内容: " + user);
}
// 条件消费
@StreamListener(
target = UserChannels.USER_INPUT,
condition = "headers['type'] == 'user-created'"
)
public void handleCreatedUser(UserDTO user) {
System.out.println("处理新用户创建: " + user);
}
@StreamListener(
target = UserChannels.USER_INPUT,
condition = "headers['type'] == 'user-updated'"
)
public void handleUpdatedUser(UserDTO user) {
System.out.println("处理用户更新: " + user);
}
// 返回结果(请求-响应模式)
@StreamListener(UserChannels.USER_INPUT)
@SendTo("responseOutput")
public UserDTO processAndRespond(UserDTO user) {
// 处理用户
user.setStatus("PROCESSED");
return user;
}
}
3.6 错误处理
import org.springframework.cloud.stream.annotation.*;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Service;
@Service
public class ErrorHandler {
// 局部错误处理(针对特定输入通道)
@ServiceActivator(inputChannel = "userInput.errors")
public void handleError(ErrorMessage errorMessage) {
System.out.println("处理错误: " + errorMessage.getPayload());
// 记录日志、发送告警等
}
// 全局错误处理
@StreamListener("errorChannel")
public void handleGlobalError(ErrorMessage errorMessage) {
System.out.println("全局错误处理: " + errorMessage);
}
// 消费者重试
@StreamListener(UserChannels.USER_INPUT)
public void handleWithRetry(UserDTO user) {
try {
processUser(user);
} catch (Exception e) {
// 抛出异常会触发重试
throw new RuntimeException("处理失败,触发重试", e);
}
}
}
# 重试配置
spring:
cloud:
stream:
bindings:
userInput:
consumer:
max-attempts: 3 # 最大重试次数
back-off-initial: 1000 # 初始重试间隔
back-off-multiplier: 2 # 重试间隔倍数
back-off-max: 10000 # 最大重试间隔
3.7 消费者组
spring:
cloud:
stream:
bindings:
userInput:
destination: user-topic
group: user-service-group # 同组内只有一个消费者消费
consumer:
concurrency: 3 # 并发消费者数量
partitioned: false # 是否分区消费
@Service
public class GroupConsumer {
@StreamListener("userInput")
public void handle(UserDTO user) {
// 同一个 group 内只有一个实例会收到消息
System.out.println("消费消息: " + user);
}
}
3.8 分区消息
spring:
cloud:
stream:
bindings:
userOutput:
destination: user-topic
producer:
partition-count: 3 # 分区数
partition-key-expression: headers['userId'] # 分区键表达式
userInput:
destination: user-topic
group: user-group
consumer:
partitioned: true
instance-count: 3 # 实例数量
instance-index: 0 # 当前实例索引
@Service
public class PartitionProducer {
@Autowired
private UserChannels userChannels;
public void sendPartitionedMessage(UserDTO user) {
Message<UserDTO> message = MessageBuilder
.withPayload(user)
.setHeader("userId", user.getId()) // 分区键
.build();
userChannels.userOutput().send(message);
}
}
3.9 函数式编程模型(Spring Cloud Stream 3.x)
# application.yml
spring:
cloud:
function:
definition: userConsumer;userSupplier;userFunction
stream:
bindings:
userConsumer-in-0:
destination: user-topic
group: user-group
userSupplier-out-0:
destination: user-topic
userFunction-in-0:
destination: input-topic
userFunction-out-0:
destination: output-topic
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import java.util.function.*;
@Component
public class StreamFunctions {
// 消费者
@Bean
public Consumer<UserDTO> userConsumer() {
return user -> {
System.out.println("消费用户: " + user);
};
}
// 生产者
@Bean
public Supplier<UserDTO> userSupplier() {
return () -> {
UserDTO user = new UserDTO();
user.setId(1L);
user.setName("test");
return user;
};
}
// 处理器(输入 -> 输出)
@Bean
public Function<UserDTO, UserDTO> userFunction() {
return user -> {
user.setName(user.getName().toUpperCase());
return user;
};
}
// 带状态的处理器
@Bean
public Function<Flux<UserDTO>, Flux<UserDTO>> userFluxFunction() {
return flux -> flux.map(user -> {
user.setName(user.getName().toUpperCase());
return user;
});
}
}
3.10 RabbitMQ 特定配置
spring:
cloud:
stream:
rabbit:
bindings:
userOutput:
producer:
exchange-type: direct # 交换机类型
routing-key-expression: headers['routingKey']
delayed-exchange: true # 延迟交换机
dead-letter-routing-key: dlq.user
userInput:
consumer:
auto-bind-dlq: true # 自动创建死信队列
republish-to-dlq: true # 重发到死信队列
prefetch: 10 # 预取数量
max-concurrency: 5 # 最大并发
acknowledge-mode: MANUAL # 手动确认
3.11 Kafka 特定配置
spring:
cloud:
stream:
kafka:
bindings:
userOutput:
producer:
sync: false # 异步发送
batch-size: 16384 # 批量大小
compression-type: gzip # 压缩类型
key-serializer: org.apache.kafka.common.serialization.StringSerializer
userInput:
consumer:
auto-commit-offset: false # 手动提交
start-offset: earliest # 从最早开始
enable-dlq: true # 启用死信队列
dlq-name: user-dlq
binder:
configuration:
key.deserializer: org.apache.kafka.common.serialization.StringDeserializer
value.deserializer: org.springframework.kafka.support.serializer.JsonDeserializer
四、实战应用场景
4.1 订单处理流程
import org.springframework.cloud.stream.annotation.*;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
@Service
@EnableBinding(OrderChannels.class)
public class OrderProcessor {
@Autowired
private OrderChannels channels;
// 创建订单
public void createOrder(OrderDTO order) {
// 发送订单创建消息
channels.orderOutput().send(
MessageBuilder.withPayload(order)
.setHeader("type", "order-created")
.build()
);
}
// 监听订单创建
@StreamListener("orderInput")
public void handleOrderCreated(OrderDTO order) {
// 1. 库存检查
checkInventory(order);
// 2. 发送支付消息
channels.paymentOutput().send(
MessageBuilder.withPayload(order)
.setHeader("type", "payment-required")
.build()
);
}
// 监听支付完成
@StreamListener("paymentInput")
public void handlePaymentCompleted(PaymentDTO payment) {
// 更新订单状态
updateOrderStatus(payment.getOrderId(), "PAID");
// 发送发货消息
// ...
}
private void checkInventory(OrderDTO order) { }
private void updateOrderStatus(Long orderId, String status) { }
}
4.2 广播通知
import org.springframework.cloud.stream.annotation.*;
import org.springframework.stereotype.Service;
// 广播消息(所有实例都收到)
@Service
public class BroadcastService {
@StreamListener("notificationInput")
public void handleNotification(Notification notification) {
// 所有实例都会收到并处理
System.out.println("收到通知: " + notification);
}
}
spring:
cloud:
stream:
bindings:
notificationInput:
destination: notification-topic
# 不设置 group,每个实例都会收到
4.3 消息重试与死信
import org.springframework.cloud.stream.annotation.*;
import org.springframework.stereotype.Service;
@Service
public class RetryConsumer {
@StreamListener("userInput")
public void handleWithRetry(UserDTO user) {
int retryCount = getRetryCount(user);
if (retryCount >= 3) {
// 超过重试次数,发送到死信队列
throw new RuntimeException("超过最大重试次数");
}
try {
processUser(user);
} catch (Exception e) {
// 设置重试次数
setRetryCount(user, retryCount + 1);
throw e;
}
}
// 死信队列消费者
@StreamListener("userDlqInput")
public void handleDeadLetter(UserDTO user) {
System.out.println("处理死信消息: " + user);
// 记录日志、发送告警
}
private int getRetryCount(UserDTO user) { return 0; }
private void setRetryCount(UserDTO user, int count) { }
private void processUser(UserDTO user) { }
}
五、总结与最佳实践
消息中间件选择
| 场景 | 推荐 |
|---|---|
| 简单异步 | RabbitMQ |
| 高吞吐量 | Kafka |
| 事务消息 | RocketMQ |
| 延迟消息 | RabbitMQ/RocketMQ |
最佳实践
- 幂等设计:消息可能重复消费
- 错误处理:配置合理的重试和死信
- 监控告警:监控消息积压
- 消息追踪:添加链路追踪信息
Spring Cloud Stream 提供了统一的消息驱动编程模型,掌握其使用方式和最佳实践,能够构建出异步、解耦、高可用的微服务系统。