1、概述
Spring Cloud Stream由一个中间件中立的核组成。应用通过Spring Cloud Stream插入的input(相当于
消费者consumer,它是从队列中接收消息的)和output(相当于生产者producer,它是从队列中发送消
息的。)通道与外界交流。通道通过指定中间件的Binder实现与外部代理连接。业务开发者不再关注具
体消息中间件,只需关注Binder对应用程序提供的抽象概念来使用消息中间件实现业务即可。
2、消息提供者
1、新建项目,添加pom依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
2、编写配置文件
server:
port: 8801
spring:
application:
name: stream-rabbitmq-provider
cloud:
stream:
binders: # 在此处配置要绑定的rabbitmq的服务信息;
defaultRabbit: # 表示定义的名称,用于于binding整合
type: rabbit # 消息组件类型
environment: # 设置rabbitmq的相关的环境配置
spring:
rabbitmq:
host: xxx.xxx.xxx.xxx
port: 5672
username: guest
password: guest
bindings: # 服务的整合处理
output: # 这个名字是一个通道的名称
destination: studyExchange # 表示要使用的Exchange名称定义
content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
default-binder: defaultRabbit # 设置要绑定的消息服务的具体设置
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
instance:
prefer-ip-address: true
instance-id: provider-8801.com
3、编写service
@EnableBinding(Source.class)
@Slf4j
public class MessageServiceImpl implements MessageService {
@Autowired
private MessageChannel output;
@Override
public String send() {
String serial = IdUtil.randomUUID();
log.info("*****流水号:" + serial);
output.send(MessageBuilder.withPayload(serial).build());
return serial;
}
}
4、编写controller
@Autowired
private MessageService service;
@Value("${server.port}")
private String port;
@GetMapping("/sendMsg")
public String sendMsg(){
String msg = service.send();
return "端口:" + port + ",消息发送者发送消息:" + msg;
}
5、访问rabbitmq的15672端口可以看到发送的信息波峰
3、消息消费者
1、新建项目,添加pom依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
2、编写配置文件
server:
port: 8803
spring:
application:
name: stream-rabbitmq-consumer
cloud:
stream:
binders: # 在此处配置要绑定的rabbitmq的服务信息;
defaultRabbit: # 表示定义的名称,用于于binding整合
type: rabbit # 消息组件类型
bindings: # 服务的整合处理
myInput: # 这个名字是一个通道的名称
destination: studyExchange # 表示要使用的Exchange名称定义
content-type: application/json # 对象json,文本则设置“text/plain”
default-binder: defaultRabbit # 设置要绑定的消息服务的具体设置
rabbitmq:
host: xxx.xxx.xxx.xxx
port: 5672
username: guest
password: guest
eureka:
instance:
prefer-ip-address: true
instance-id: consumer-8803.com
client:
service-url:
defaultZone: http://localhost:7001/eureka
注意上面的bindings下的myInput可以自己指定名字,提供者使用的output可以通过Source.class直接引用,如果使用自定义的名字,需要编写一个接口:
3、编写接口
public interface MyStream {
String myInput = "myInput";
@Input(MyStream.myInput)
SubscribableChannel input();
}
4、编写消费者类
@Component
@EnableBinding(MyStream.class)
@Slf4j
public class ReceviceMessageController {
@Value("${server.port}")
private String port;
@StreamListener(MyStream.myInput)
public void input(Message<String> msg){
log.info("消费者8803,端口:" + port + ",收到消息:" + msg.getPayload());
}
}
注意这里的@EnableBinding(MyStream.class)不是用的Sink.class,Sink.class引用的名字为input,此处使用的是自定义名字。
5、启动提供者和消费者,提供者发送信息,消费者可以接收到。
4、消息重复消费问题
1、将上述消费者clone一份,发现发送消息会被消费两次,因为广播的类型是topic,凡是订阅的都可以接收
2、在两个消费者的配置文件上给其分组,添加相同的group即可
spring:
cloud:
stream:
bindings: # 服务的整合处理
myInput: # 这个名字是一个通道的名称
group: groupA
3、相同的分组之间只会被一个消费者消费,例如出现第三个消费者,分组是groupB,发送消息后,第一和第二个消费者只有一个人可以消费到消息,第三个消费者可以一直收到信息,因为他自己一组。
5、消息持久化
1、没有被分组的消费者在关闭服务的时候发送消息,启动消费者不会收到已经发送的消息
2、被分组的消费者在关闭服务的时候发送消息,启动消费者会收到之前已经发送的消息