SpringCloud Stream消息驱动
1、消息驱动概述
-
是什么
一句话:屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型
-
设计思想
1、标准MQ
- 生产者/消费者之间靠消息媒介传递信息内容:Message
- 消息必须走特定的通道:MessageChannel
- 消息通道里的消息如何被消费呢?谁负责收发。处理消息通道MessageChannel的子接口SubscribeChannel,由MessageHandler消息处理器所订阅
2、为什么用CloudStream
-
Stream凭什么可以统一底层差异
-
Binder
==通过定义绑定器Binder作为中间件,实现了应用程序与消息中间件细节之间的隔离。==
==INPUT适用于消费者==
==OUTPUT适用于生产者==
3、Stream中的消息通讯方式遵循了发布-订阅模式
-
在RabbitMQ就是Exchange
-
在Kafka中就是Topic
-
Springcloud Stream标准流程套路
Binder:很方便的连接中间件,屏蔽差异
Channel:通道,是队列Queue的一种抽象,在消息通讯系统中就是实现存储和转发的媒介,通过channel对队列进行配置
Source和Sink:简单的可理解为参照对象是Springcloud Stream自身,从Stream发布消息就是输出,接受消息就是输入
- 编码API和常用注解
2、案例说明
- cloud-stream-rabbitmq-provider8801,作为生产者进行消息模块
- cloud-stream-rabbitmq-consumer8802,作为消费者接受模块
- cloud-stream-rabbitmq-consumer8803,作为消费者接受模块
3、消息驱动之生产者
-
新建module:cloud-stream-rabbitmq-provider8801
-
POM
<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.atguigu.springcloud</groupId> <artifactId>cloud-api-common</artifactId> <version>${project.version}</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> </dependencies>
-
yml
server: port: 8801 spring: application: name: cloud-stream-privider cloud: stream: binders: #自此处配置要绑定的rabbitmq的服务信息 defaultRabbit: #表示定义的名称,用于binding整合 type: rabbit #消息组件类型 environment: # 设置rabbitmq的相关的环境配置 spring: rabbitmq: host: localhost port: 5672 username: guest password: guest bindings: #服务的整合处理 output: #这个名字是一个通道的名称 destination: springboot-mq #表示要使用的exchange名称定义 content-type: application/json #设置消息类型,本次为json binder: defaultRabbit #设置要绑定的消息服务的具体设置 eureka: client: service-url: defaultZone: http://localhost:7001/eureka instance: lease-expiration-duration-in-seconds: 5 #如果现在超过了5秒的间隔 lease-renewal-interval-in-seconds: 2 #设置心跳的时间间隔 instance-id: send-8801.com prefer-ip-address: true #访问的路径变为IP地址
-
主启动类
@EnableEurekaClient @SpringBootApplication public class StreamMQMain8801 { public static void main(String[] args) { SpringApplication.run(StreamMQMain8801.class,args); } }
-
业务类
1、接口
/* *@author:C1q */ public interface IMessageProvider { public String send(); }
2、实现类
@EnableBinding(Source.class) public class IMessageProviderImpl 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; } }
-
controller
@RestController public class SendMessageController { @Resource private IMessageProvider messageProvider; @GetMapping("/sendMessage") public String sendMessage(){ return messageProvider.send(); } }
-
测试
4、消息驱动之消费者
-
新建module:cloud-stream-rabbitmq-provider8801
-
POM
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!--基础配置--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
-
yml
server: port: 8802 spring: application: name: cloud-stream-consumer cloud: stream: binders: # 在此处配置要绑定的rabbitmq的服务信息; defaultRabbit: # 表示定义的名称,用于于binding整合 type: rabbit # 消息组件类型 environment: # 设置rabbitmq的相关的环境配置 spring: rabbitmq: host: localhost port: 5672 username: guest password: guest bindings: # 服务的整合处理 input: # 这个名字是一个通道的名称 destination: studyExchange # 表示要使用的Exchange名称定义 content-type: application/json # 设置消息类型,本次为对象json,如果是文本则设置“text/plain” binder: defaultRabbit # 设置要绑定的消息服务的具体设置 eureka: client: # 客户端进行Eureka注册的配置 service-url: defaultZone: http://localhost:7001/eureka instance: lease-renewal-interval-in-seconds: 2 # 设置心跳的时间间隔(默认是30秒) lease-expiration-duration-in-seconds: 5 # 如果现在超过了5秒的间隔(默认是90秒) instance-id: receive-8802.com # 在信息列表时显示主机名称 prefer-ip-address: true # 访问的路径变为IP地址
-
主启动类
@SpringBootApplication public class StreamMQMain8802 { public static void main(String[] args) { SpringApplication.run(StreamMQMain8802.class,args); } }
-
controller
@Component @EnableBinding(Sink.class) 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); } }
-
测试
5、分组消费与持久化
依照8802创建另外的消费着8803
-
重复消费的问题
默认分配到不同的组内
Queue studyExchange.anonymous.ylrmAknBR_-k7fRGw14iBg
Queue studyExchange.anonymous.FDLWrJWsRUG9479vFRO8Ug
微服务应用放置于同一个group中,就能够保证消息只会被其中一个应用消费一次。
不同的组是可以消费的,同一个组内会发生竞争关系,只有其中 一个可以消费。 -
自定义分组
1、分为不同的组配置yml
group: 名字
bindings: # 服务的整合处理
input: # 这个名字是一个通道的名称
destination: studyExchange # 表示要使用的Exchange名称定义
content-type: application/json # 设置消息类型,本次为对象json,如果是文本则设置“text/plain”
binder: defaultRabbit # 设置要绑定的消息服务的具体设置
group: atguiguB
input: # 这个名字是一个通道的名称
destination: studyExchange # 表示要使用的Exchange名称定义
content-type: application/json # 设置消息类型,本次为对象json,如果是文本则设置“text/plain”
binder: defaultRabbit # 设置要绑定的消息服务的具体设置
group: atguiguA
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BMCW08kI-1594442650744)(SpringCloud.assets/1587561017103.png)]
2、相同的组配置yml
group: 名字 多个配置名字相同
微服务应用放置于同一个group中,就能够保证消息只会被其中一个应用消费一次。不同的组是可以消费的,同一个组内会发生竞争关系,只有其中一个可以消费。
-
持久化
拥有group分组的可以持久化数据,重启可以继续消费没有消费的内容