文章目录
1. Spring Cloud Stream(屏蔽多个消息中间件差异,用Strem整合中间件发送消息)
现在一个很项目可能分为三部分:
前端--->后端---->大数据
而后端开发使用消息中间件,可能会使用RabbitMq
而大数据开发,一般都是使用Kafka,
那么一个项目中有多个消息中间件,对于程序员,因为人员都不友好
而Spring Cloud Stream就类似jpa,屏蔽底层消息中间件的差异,程序员主要操作Spring Cloud Stream即可
不需要管底层是kafka还是rabbitMq
1.1 什么是Spring Cloud Stream
1.2 Spring Cloud Stream是怎么屏蔽底层差异的
绑定器:
1.3 Spring Cloud Streamd 通信模式
1.31 Spring Cloud Stream的业务流程
类似flume中的channel,source,sink 估计是借鉴(抄袭)的
- source用于从外界获取数据(发送到Channel通道,要发送到mq的数据),类似SpringCloudStream的生产者
- channel类似SpringCloudStream中的中间件,即kafka的partition分区或rabbitMQ的queue队列,用于存放source接收到的数据,或者是存放binder拉取的数据 (SpringCloudStream存储消息)
- Sink用于从Channel通道获取数据,然后外界消费者从Sink获取数据(SpringCloudStream的消费者)
- Binder是一个绑定器,用于动态获取不同类型的消息中间件(比如微服务存在kafka、RabbitMQ两种消息中间件,那么为了是两种消息类型一致,需要一个绑定器去实现类型的统一,而Stream中Binder绑定器就可以)
1.32 常用注解和api
2. SpringCloudStream案例演示
屏蔽多个消息中间件差异,用Strem整合中间件发送消息,代码不用消息中间件的API(如RabbitMQ、Kafka等),而是直接使用Stream的一套API去实现消息中间件的使用(生产者、消息队列、消费者)
为了演示Stream,我们需要再创建3个子模块,分别是cloud-stream-rabbitmq-provider8801,cloud-stream-rabbitmq-consumer8802,cloud-stream-rabbitmq-consumer8803,另外,需要把RabbitMQ安装并启动.
2.1 创建生产者8801
(1)pom.xml
添加了Spring cloud stream和rabbitMQ的整合spring-cloud-starter-stream-rabbit
<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.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-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>
(2)8801yaml配置文件
注意:8801生产者使用的是output通道。
(3)8801主启动类
(4)service和实现类
service定义发送消息
接口实现类
这里是消息中间件的绑定使用,不用像之前的业务一样使用@service注解,不用调用Dao数据层,直接引入Channel消息管道
package com.atguigu.springcloud.service.impl;
//这里使用@EnableBinding(Source.class),这是Stream里的注解,定义一个消息推送管道,不需要使用@Service注解
@EnableBinding(Source.class)
public class MessageProviderImpl implements IMessageProvider {
@Resource
private MessageChannel output;// 定义消息发送管道Channel,名称必须是output,否则会报错
@Override
public String send() {
String uuid = UUID.randomUUID().toString();
output.send(MessageBuilder.withPayload(uuid).build());
System.out.println("uuid=" + uuid);
return null;
}
}
这里,就会调用send方法,将消息发送给channel,
然后channel将消费发送给binder,然后发送到rabbitmq中
(5)controller
(6)可以测试
启动rabbitmq
启动7001,8801
确定8801后,会在rabbitmq中创建一个Exchange,就是我们配置文件yaml中配置的destination:studyExchange
访问8801的/sendMessage
2.2 创建消费者8802
(1)8802 pom.xml
和8801生产者一样
(2)8802 yaml配置文件
注意这里通道名称改成input,之前cloud-stream-rabbitmq-provider8801生产者使用的是output。
input就表示,当前服务是一个消费者,需要消费消息,下面就是指定消费哪个Exchange中的消息
(3)8802主启动类
(4)8802业务类(消费数据)
这里没有service,直接定义controller层,从消息队列Channel拿数据
生产者发送消息时,使用send方法发送,send方法发送的是一个个Message,里面封装了数据
(5)测试
启动7001.8801.8802
此时使用生产者生产消息
可以看到,消费者已经接收到消息了
2.3 创建消费者8803
创建8803,
与8802创建一模一样,就不写了
创建8803主要是为了演示重复消费等问题
…
…
…
2.4 重复消费问题(多个消费者配置成一个组)
此时启动7001.8801.8802.8803
此时生产者生产一条消息
但是此时查询消费者,发现8802,8803都消费到了同一条数据
2.4.1 自定义分组
在Stream中,处于同一个group的多个消费者是竞争关系,一条消息只能消费一次,不存在重复消费。
处于不同group组的消费者都可以拿到消息,是可以重复消费的。
通过查看RabbitMQ的管理后台,点击exchange标签,找到我们指定的exchange,发现Bindings里面有两个组,可以重复消费,所以出现了重复消费问题。
所以,解决重复消费的办法也很简单,自定义配置分组分为同一个组group,解决重复消费问题。
(1)修改8802,8803的yaml配置文件,添加分组group
现在将8802,8803都分到了A组
然后去重启02,03
然后此时生产者生产两条消息
可以看到,每人只消费了一条消息,并且没有重复消费,就是轮循机制,每次生产者发送消息,只有一个消费者可以获得。
2.5 持久化问题
就是当服务挂了,怎么消费没有消费的数据??
这里,先将8802移除A组,
然后将02,03服务关闭
此时生产者开启,发送3条消息
此时重启02,03
可以看到,当02退出group:atguiguA组后,它就获取不到在它宕机的时间段内的数据
但是03重启后,直接获取到了宕机期间它没有消费的数据,并且消费了,实现了持久化。
总结:
也就是,当我们没有配置分组时,会出现消息漏消费的问题
而配置分组后,即使我们宕机了,我们也可以自动获取未消费的数据,这就是持久化消费