Spring Cloud Stream:消息驱动架构的统一编程之道
前言
在微服务架构蓬勃发展的今天,服务间的异步通信已成为系统解耦、流量削峰和实时数据处理的核心技术手段。然而,面对Kafka、RabbitMQ、RocketMQ等纷繁复杂的消息中间件,开发者往往陷入两难:如何在不被特定技术绑定的前提下,高效实现消息驱动的业务逻辑? 当需要在不同消息中间件之间迁移或适配多平台时,重复的代码和差异化的API是否让您的团队疲于应付?
这正是Spring Cloud Stream试图解决的痛点。作为Spring家族中面向消息驱动架构的“粘合剂”,它通过抽象化的编程模型和开箱即用的中间件适配能力,重新定义了微服务间消息通信的开发范式。
和Spring Kafka比较
简单比较一下Spring Cloud Stream和单一消息中间件Spring Kafka的区别。
架构
消息传递流程
生产者侧(服务A)
- Source生成消息:服务A通过**
Source接口(如调用MessageChannel.send()**)创建消息。 - Channel传递:消息进入**
Output Channel**,准备发送到中间件。 - Binder绑定中间件:Binder根据配置将**
Channel映射到消息中间件的具体Topic**或队列。 - 消息发送至中间件:通过中间件客户端(如**
Kafka Producer**)将消息持久化到中间件。
消费者侧(服务B)
- Binder监听中间件:**
Binder从消息中间件订阅指定Topic**或队列的消息。 - Channel接收消息:消息通过**
Input Channel**传递到服务B。 - Sink处理消息:服务B通过**
Sink接口(如@StreamListener**)消费并处理消息。
**Spring Cloud Stream的架构决定了它具有与中间件解耦的特点。开发者只需关注Source和Sink的业务逻辑,无需直接操作Kafka或RabbitMQ的API。例如,替换消息中间件时,只需修改Binder**配置,无需重构代码。
快速入门
使用Spring Initializr或您喜欢的IDE来创建项目,我使用的Spring Boot版本是2.7.18 。
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.7.18</version>
<relativePath/>
</parent>
添加依赖
Spring Boot的版本号和spring-cloud.version 要匹配,这点很重要。以下是Maven依赖:
<properties>
<java.version>11</java.version>
<spring-cloud.version>2021.0.8</spring-cloud.version>
</properties>
<dependencies>
<!-- Spring Boot Web(可选,仅需 Web 功能时添加) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Cloud Stream 核心 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream</artifactId>
</dependency>
<!-- 绑定器 -->
<!-- <dependency>-->
<!-- <groupId>org.springframework.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-stream-rabbit</artifactId>-->
<!-- </dependency>-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-kafka</artifactId>
</dependency>
<!-- 测试依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-test-support</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
消息通道
创建一个名为MessageChannels的接口,用于定义输入和输出的消息通道。这些通道允许您的Spring Cloud Stream应用程序与消息中间件进行通信。
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;
public interface MessageChannels {
@Output("output1")
MessageChannel output1();
@Input("input1")
SubscribableChannel input1();
@Output("output2")
MessageChannel output2();
@Input("input2")
SubscribableChannel input2();
}
@Output注解和@Input里的参数需要在项目配置文件(例如application.properties)中映射相应的中间件Topic。我这里配置了两种场景:单个中间件、多个中间件。
首先在application.properties文件中配置spring.profiles.active ,用于切换不同场景的配置文件。
spring.application.name=cloudstreamdemo
#spring.profiles.active=mq
spring.profiles.active=multimq
单个中间件配置文件application-mq.yaml
spring:
kafka:
bootstrap-servers: localhost:9092
cloud:
stream:
bindings:
input1:
destination: test-topic1
group: my-message-group
output1:
destination: test-topic1
input2:
destination: test-topic2
group: my-message-group
output2:
destination: test-topic2
多个中间件配置文件application-multimq.yaml。spring.cloud.stream.binders用于配置不同环境的中间件,spring.cloud.stream.bindings 用于将中间件(Topic)和生产者消费者(@Input、@Output)绑定。
spring:
cloud:
stream:
binders:
# 接下来的kafka1和kafka2就是两个kafka broker的环境配置,配置完成后可以应用kafka1、kafka2这个定义的名字
# 在别的地方引用。功能和profile中的dev环境、test环境、prod环境一个意思。
kafka1:
type: kafka
environment:
spring:
cloud:
stream:
kafka:
binder:
brokers: localhost:9092
kafka2:
type: kafka
environment:
spring:
cloud:
stream:
kafka:
binder:
brokers: localhost:9093
bindings:
input1:
# 没有指定binder,使用默认环境,下面的配置有指定默认环境。
destination: test-topic1
group: my-message-group
output1:
destination: test-topic1
# content-type: application/json
input2:
# 指定使用kafka2的环境
binder: kafka2
destination: test-topic2
group: my-message-group
output2:
binder: kafka2
destination: test-topic2
# content-type: application/json
kafka:
binder:
# 自动创建不存在的topic
autoCreateTopics: true
# 默认使用哪个kafka环境,如下配置是在没有指定kafka环境的时候(如output_2),使用kafka1的配置
default-binder: kafka1
生产者
写一个生产者接口,通过HTTP接口请求调用消息通道是send方法生产消息。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;
@RestController
@EnableBinding(MessageChannels.class)
public class MessageProducerController {
@Autowired
private MessageChannels messageChannels;
@PostMapping("/send")
public String sendMessage(@RequestBody String message) {
messageChannels.output1().send(MessageBuilder.withPayload(message).build());
return "Message sent: " + message;
}
}
消费者
创建一个MessageConsumerService类,使用@StreamListener注解监听输入消息通道,@SendTo注解用来将消息发送给输出消息通道。
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.messaging.handler.annotation.SendTo;
@EnableBinding(MessageChannels.class)
public class MessageConsumerService {
@StreamListener("input1")
@SendTo("output2")
public String input1(String message) {
String msg = "test-topic1 received message: " + message;
System.out.println(msg);
return message;
}
@StreamListener("input2")
public void inputTestTopic2(String message) {
String msg = "test-topic2 received message: " + message;
System.out.println(msg);
}
}
测试结果
调用接口[http://localhost:8080](http://localhost:8080/)/send 参数如下:
{
"hello": "cloud stream"
}
可以看到日志打印出来,output1接口发送的消息被input1 监听到,并发送给output2 ,再由input2监听到,完全符合MessageConsumerService 代码的预期。
参考
Spring Cloud Stream 中文文档 参考手册 中文版
Spring Cloud Stream:打造强大的微服务事件驱动架构引言 随着云计算、微服务和大数据技术的快速发展,构建 - 掘金