SpringCloud Stream
书接上文,继续跟着周阳老师学习SpringCloud的消息驱动框架Stream。
一、Spring Cloud Stream
1.1 Stream
是一个用来为微服务应用构建消息驱动能力的框架。它可以基于 Spring Boot 来创建独立的、可用于生产的 Spring 应用程序。Spring Cloud Stream 为一些供应商的消息中间件产品提供了个性化的自动化配置实现,并引入了发布-订阅、消费组、分区这三个核心概念。通过使用 Spring Cloud Stream,可以有效简化开发人员对消息中间件的使用复杂度,让系统开发人员可以有更多的精力关注于核心业务逻辑的处理。但是目前 Spring Cloud Stream 只支持 RabbitMQ 和 Kafka 的自动化配置。
一句话:屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型。
1.2 Stream处理架构
1.3 Stream标准流程套路
Binder:很方便的连接中间件,屏蔽差异。
channel:通道,是队列Queue的一种抽象,在消息通讯系统中就是实现存储和转发的媒介,通过channel对队列进行配置。
source和sink:简单理解为消息的输入输出。
1.4 编码API和注解
二、实战
2.1 新建子模块8801,作为消息驱动的生产者
cloud-stream-rabbitmq-provider8801 消息生产模块
application.yml
server:
port: 8801
spring:
application:
name: cloud-stream-provider
cloud:
stream:
binders: #需要绑定的rabbitmq的服务信息
defaultRabbit: #定义的名称,用于binding整合
type: rabbit #消息组件类型
environment: #环境配置
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
bindings: # 服务的整合处理
output: # 名字是一个通道的名称,发送
destination: studyExchange # 表示要使用的Exchange名称定义
content-type: application/json # 设置消息类型,本次为json
binder: defaultRabbit #设置要绑定的消息服务的具体设置
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
instance:
#renewal表示eureka client向eureka server发送心跳的频率,如果在指定时间内未收到心跳,则移除实例
lease-renewal-interval-in-seconds: 2 #设置心跳时间间隔,默认30秒
#expiration表示eureka server获取上一个心跳之后,等待下一个心跳的秒数,默认90S
#若不能在指定时间间隔内接收到实例,则移除此实例,并禁止此实例的流量
lease-expiration-duration-in-seconds: 5 #设置接收时间间隔
instance-id: send-8801.com # 主机名
prefer-ip-address: true # 显示ip
StreamMQMain8801主启动类
@SpringBootApplication
public class StreamMQMain8801 {
//1.先启动7001 2.再启动8801,连续多次访问http://localhost:8801/sendMessageTest
//查看http://localhost:15672下的Exchanges绑定的交换机信息
public static void main(String[] args) {
SpringApplication.run(StreamMQMain8801.class,args);
}
}
IMessageProvider接口
public interface IMessageProvider {
String send();//消息发送接口
}
MessageProviderImpl
@EnableBinding(Source.class)//定义消息的推送广告,此注解指信道channel和exchange绑定在一起
public class MessageProviderImpl implements IMessageProvider {
@Resource
private MessageChannel output; //消息发送管道
@Override
public String send() {
String serial = UUID.randomUUID().toString();
output.send(MessageBuilder.withPayload(serial).build());
System.out.println("*****serial: " +serial);
return null;
}
}
SendMessageController控制层
@RestController
public class SendMessageInfoController {
@Resource
private IMessageProvider messageProvider;
@GetMapping("/sendMessageTest")
public String sendMessageTest(){
return messageProvider.send();
}
}
8801 消息生产模块项目结构
8801测试步骤及结果
先启动7001 2.再启动8801,连续多次访问http://localhost:8801/sendMessageTest
再查看http://localhost:15672下的Exchanges绑定的交换机信息
2.2 新建子模块8802,作为消息驱动的消费者
cloud-stream-rabbitmq-consumer8802 消息消费模块
application.yml
server:
port: 8802
spring:
application:
name: cloud-stream-consumer
cloud:
stream:
binders: #需要绑定的rabbitmq的服务信息
defaultRabbit: #定义的名称,用于binding整合
type: rabbit #消息组件类型
environment: #环境配置
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
bindings: # 服务的整合处理
input: # 名字是一个通道的名称,消费
destination: studyExchange # 表示要使用的Exchange名称定义
content-type: application/json # 设置消息类型,本次为json
binder: defaultRabbit #设置要绑定的消息服务的具体设置
group: atguiguA
eureka:
client:
service-url:
defaultZone: http://localhost:7001/eureka
instance:
lease-renewal-interval-in-seconds: 2
lease-expiration-duration-in-seconds: 5
instance-id: receive-8802.com
prefer-ip-address: true
StreamMQMain8802
@SpringBootApplication
public class StreamMQMain8802 {
//启动7001、8801、8802,测试8801发送,8802接收,
//访问http://localhost:8801/sendMessageTest
public static void main(String[] args) {
SpringApplication.run(StreamMQMain8802.class, args);
}
}
ReceiveMessageListenerController
@Component
@Slf4j
@EnableBinding(Sink.class) //指信道chanel和exchange绑定一起
public class ReceiveMessageListenerController {
@Value("${server.port}")
private String serverPort;
@StreamListener(Sink.INPUT) //监听队列,用于消费者的队列消息接收
public void input(Message<String> message) {
System.out.println("消费者1号,------>接收到的消息:"+message.getPayload()+"\t port" +serverPort);
}
}
访问http://localhost:8801/sendMessageTest
只访问一次的测试结果
多次频繁访问测试结果
8802控制台输出结果
8801控制台输出结果
2.3 新建子模块8803,作为消息驱动的消费者(clone8802项目)
代码一致,主要区别是application.yml中端口号改为8803
2.3.1 消息重复消费问题
不同的组存在重复消费,相同的组之间竞争消费,只有一个被消费。
application.yml中不增加group组名,测试结果
连续访问http://localhost:8801/sendMessageTest两次
重复消费结果如下
application.yml中增加group组名
测试结果
2.3.1 消息持久化
利用group分组实现消息持久化,演示步骤如下
1、停止8802和8803,将8802的group:atguiguA注释
2、8801线发送4条消息到RabbitMQ
3、先启动8802,无分组属性配置,后台没有打印出来消息
4、再启动8803,有分组属性配置,后台打印出MQ上消息
项目总体结构
源码下载地址
参考文章
https://blog.csdn.net/qq_42107430/article/details/104788858
https://www.cnblogs.com/wwjj4811/p/13628099.html