Spring Cloud Stream 消息驱动
一、简介
Spring Cloud Stream 是一个构建消息驱动的微服务框架。通过 inputs(生产者) 和 outputs (消费者)来与 stream 中的 binder (绑定器)进行交互。
目前官方仅支持 Rabbit 和 Kafka
二、重要概念
2.1 Middleware
中间件,目前仅支持 rabbit 和 kafaka
2.2 Binder
消息中间件与应用之间的中间层,可通过配置文件动态改变消息类型
2.3 group
组,每个组对应一个队列,同组消费存在竞争,不同组消费不存在竞争。
不加分组时,重启时,之前错过的消息不会进行消费,加分组时则不会错过消息。
2.4 常用注解
- @Input
标识输入通道,通过该输入通道接收的消息进入应用程序。
- @Output
标识输出通道,通过该输出通道发送的消息进入消息中间件。
- @StreamListener
监听队列,用于消费者队列的消息接收。
- @EnableBinding
只信道 channel 和 exchange 绑定在一起
三、实例
pom.xml
<!-- 消费者生产者都要引入该依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
3.1 消息生产者
- application.yml
spring:
application:
name: stream-provider
rabbitmq: # 这里需要配置,否则会连接不上 Rabbit
host: localhost
port: 5672
username: user
password: password
cloud:
stream:
bindings: # 服务整合处理
output: # 生产者通道名称
binder: rabbit1 # 设置要绑定的消息服务具体设置
destination: studyExchange # 使用的交换机名称定义
content-type: application/json # 设置消息类型,本地为json,文本设置为 text/plain
binders: # 此处配置要绑定的 rabbitmq 服务信息
rabbit1: # 表示自定义定义名称,用于 binding 整合
type: rabbit # 消息组件类型
environment: # 设置 rabbitmq 环境变量
spring:
rabbitmq:
host: localhost
port: 5672
username: user
password: password
- service
@EnableBinding(Source.class) // 定义消息的推送,即 stream
@Slf4j
public class MessageProviderImpl implements MessageProvider {
@Resource // 信道
private MessageChannel output;
@Override
public String send() {
String uuid = UUID.randomUUID().toString();
output.send(MessageBuilder.withPayload(uuid).build());
log.info("***********************uuid:" + uuid);
return uuid;
}
}
3.2 消息消费者
- application.yml
spring:
application:
name: stream-consumer
rabbitmq:
host: localhost
port: 5672
username: user
password: password
cloud:
stream:
bindings: # 服务整合处理
input: # 通道名称。消费者
binder: rabbit1 # 设置要绑定的消息服务具体设置
destination: studyExchange # 使用的交换机名称定义
content-type: application/json # 设置消息类型,本地为json,文本设置为 text/plain
binders: # 此处配置要绑定的 rabbitmq 服务信息
rabbit1: # 表示自定义定义名称,用于 binding 整合
type: rabbit # 消息组件类型
environment: # 设置 rabbitmq 环境变量
spring:
rabbitmq:
host: 172.16.10.82
port: 5672
username: user
password: password
- controller
@Component
@EnableBinding(Sink.class)
@Slf4j
public class ReceiveMessageController {
@Value("${server.port}")
private String port;
@StreamListener(Sink.INPUT)
public void receiveMessage (Message<String> message){
log.info("消费者2:----------> " + message.getPayload()+"\t port:" + port);
}
}
四、问题
4.1 重复消费
-
原因
默认分组 group 是不同的 组流水好不一样,默认不同组,则全部消费
-
原理
同一个交换机下,不同组会会被共同消费,导致重复消费,同一个组内才会发生竞争关系,只有一个可以消费
-
解决方案
设置相同组
4.2 消息丢失
- 原因