Stream简介
Stream又名消息驱动,为什么要引入Stream?
在微服务架构中,我们可能会因为业务的原因,而要使用到多种消息队列的场景。但是,对于开发者而言。每多学习一门新的技术,就要花费一定的时间与经历。而Stream的引入,就是为了方便开发人员,可以不用在关注不同的MQ之间的方言细节,可以将更多的经历花费在业务逻辑的编写上。(类似于Hibernate)
用一句话来概括Stream–屏蔽底层消息中间件的差异,降低切换成本,统一消息的编程模型。
虽然Stream的设计理念是为了统一消息的编码模型。但是到目前为止,Stream只支持RabbitMQ、Kafka。
Stream常用注解
- @Input 注解标识输入通道,通过该输入通道接受到消息进入程序(对应消费者)
- @Output 注解标识输入通道,发布的消息将通过该通道离开程序(对应生产者)
- @StreamListener 监听队列,用于消费者的队列的消息接受
- @EnableBinding 指通道channel与exchange绑定在一起
Stream对应的模型
对应的生产者与消费者模型
Stream生产者构建
- 新建生产者模块cloud-stream-provider8801,导入stream相关依赖
<!-- 当前使用的是rabbitmq消息中间件,如果使用的Kafka请导入Kafka相关依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
- 配置yml对应的配置文件
spring:
application:
name: cloud-stream-provider
cloud:
stream:
binders: #配置要绑定的rabbitmq相关的信息
defaultRabbit:
type: rabbit #消息中间件rabbit或Kafka
environment:
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
bindings: #服务整合处理
output:
destination: studyExchange #使用的exchange的名称
content-type: application/json #设置发送的消息类型,此处为json格式
binder: defaultRabbit #设置要绑定的消息服务的具体设置
- 编写发送消息的service(核心service上配置绑定的消息通道)
@Service
@EnableBinding(Source.class) //定义消息的推送管道
@Slf4j
public class ProviderServiceImpl implements ProviderService {
@Autowired
private MessageChannel output;
@Override
public String send() {
String serial = UUID.randomUUID().toString();
output.send(MessageBuilder.withPayload(serial).build());
log.info(serial);
return null;
}
}
- 编写访问的接口
@RestController
public class ProviderController {
@Autowired
private ProviderService providerService;
@GetMapping(path = "/send")
public String sendProvider() {
return providerService.send();
}
}
- 测试
访问send接口,观察rabbitmq控制台。发现控制台有消息发送则证明配置成功
Stream消费者构建
单消费者构建
- 新建消费者模块cloud-stream-consumer8802,导入依赖(与生产者相同)
<!-- 当前使用的是rabbitmq消息中间件,如果使用的Kafka请导入Kafka相关依赖 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
- 配置yml依赖
```java
spring:
application:
name: cloud-stream-provider
cloud:
stream:
binders: #配置要绑定的rabbitmq相关的信息
defaultRabbit:
type: rabbit #消息中间件rabbit或Kafka
environment:
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
bindings: #服务整合处理
input: #---------------------与生产不同点,将输出改为输入流
destination: studyExchange #使用的exchange的名称
content-type: application/json #设置发送的消息类型,此处为json格式
binder: defaultRabbit #设置要绑定的消息服务的具体设置
3. 编写监听生产者的controller接口
```java
@Component
@EnableBinding(Sink.class) //编写绑定类型
@Slf4j
public class MessageListenerController {
@Value("${server.port}")
private String port;
@StreamListener(Sink.INPUT) //监听程序的输入
public void input(Message<String> message) { //此处message的泛型需要与生产者端传输的对象相同
log.info(port + message.getPayload());
}
}
重复消费问题与持久化问题
但是,此种配置下。会出现多消费者端,重复消费问题。而Spring Cloud Stream默认的是按照组,来分配给消费者消息的。当消费者两两间是同一个组,则同组消费者轮询发布。不同组,则将同一消息重复发送给不同组成员。如果不进行配置的话Stream会默认给消费者模块,分配一个流水号。所以也就造成成创建组不同,会重复消费者问题。
解决办法,yml中引入分组配置
bindings: #服务整合处理
input:
group: groupA #自定义分组命名
group配置在解决了重复消费的问题同时,还解决了持久化问题。即使消费者端宕机,重启后,消费者端仍能拿到消息。