利用消息驱动,我们可以不再关注具体哪个MQ中间件的细节,我们只需要一种适配绑定的方式,自动的给我们在各种MQ内进行切换
屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型
Stream
概念
- Spring Cloud Stream是一个构建消息驱动微服务的框架
- 通过inputs或者outputs与Stream的binder对象交互,而binder对象负责与消息中间件交互
- 通过使用Integration来连接消息代理中间件以实现消息时间驱动
- 目前仅支持RabbitMQ、Kafka
- 通过定义绑定器作为中间层,完美实现了应用程序和消息中间件之间的隔离
标准流程
- Binder:连接中间件,屏蔽差异
- Channel:通道,是队列Queue的一种抽象,在消息通讯系统中就是实现存储和转发的媒介,通过Channel对队列进行配置
- Source和Sink:从Stream发布消息就是输出,接收消息就是输入
实操
生产者发送消息
- pom文件引入依赖
<!-- 消息驱动stream-RabbitMQ -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
- yml文件中Stream和RabbitMQ进行绑定
spring:
cloud:
stream:
binders: # 配置要绑定的rabbitmq的服务信息
defaultRabbit: # 自定义的名称,用于与bindings集合
type: rabbit # 消息组件的类型
environment: # 设置rabbitmq的环境
spring:
# rabbitmq配置
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
bindings: # 服务的整合处理
output: # 表示生产者
destination: studyExchange # 通道的名称
content-type: application/json # 设置消息类型,此处为json,文本为text/plain
binder: defaultRabbit # 绑定消息服务的具体设置
- service实现消息的发送
@EnableBinding(Source.class) // 定义消息的推送
public class MessageServiceImpl implements MessageService {
@Resource
private MessageChannel output; // 消息管道
@Override
public String sendMessage() {
String uuid = UUID.randomUUID().toString();
// 通过消息构建,构建出发送的消息,再利用消息管道进行传输
output.send(MessageBuilder.withPayload(uuid).build());
return uuid;
}
}
消费者消费消息
4. pom文件引入依赖
<!-- 消息驱动stream-RabbitMQ -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
- yml文件中Stream和RabbitMQ进行绑定
spring:
cloud:
stream:
binders: # 配置要绑定的rabbitmq的服务信息
defaultRabbit: # 自定义的名称,用于与bindings集合
type: rabbit # 消息组件的类型
environment: # 设置rabbitmq的环境
spring:
# rabbitmq配置
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
bindings: # 服务的整合处理
input: # 表示消费者
destination: studyExchange # 通道的名称
content-type: application/json # 设置消息类型,此处为json,文本为text/plain
binder: defaultRabbit # 绑定消息服务的具体设置
- 实现消息的接收
@Component
@EnableBinding(Sink.class)
public class MessageController {
@Value("${server.port}")
private String serverPort;
@StreamListener(Sink.INPUT)
public void getMessage(Message<String> message) {
System.out.println("消费者serverPort:" + serverPort + "\t\t" + "uuid:" + message.getPayload());
}
}
问题:多个消费者订阅同一个生产者,出现重复消费的情况
- 同一个组:消息竞争,只有一个可以消费
- 不同的组:可重复消费
- 出现该问题的原因,RabbitMQ默认给消费者分为不同的组,导致重复消费的问题
解决:在yml文件中增加group配置(与上yml文件对齐)
bindings: # 服务的整合处理
input: # 表示消费者
destination: studyExchange # 通道的名称
content-type: application/json # 设置消息类型,此处为json,文本为text/plain
binder: defaultRabbit # 绑定消息服务的具体设置
group: xlj-A # 消费者分组
group属性同时还能够解决持久化的问题,是个重要的属性