SpringCloud-消息驱动(Stream)
Stream
概述
https://spring.io/projects/spring-cloud-stream#overview
https://docs.spring.io/spring-cloud-stream/docs/current/reference/html/
Stream解决什么问题?
当系统中使用多个不同的MQ时,使用spring cloud stream可以屏蔽 不同消息中间件的差异,统一消息的编程模型;
what
Spring Cloud Stream is a framework for building highly scalable event-driven microservices connected with shared messaging systems.
Spring Cloud Stream 是一个消息驱动的微服务框架;
应用程序 通过inputs/outputs 与 SpringCloudStream的binder进行交互;
通过配置进行binding,SpringCloudStream的binder负责与MQ交互;
目前仅支持 RabbitMQ、Kafka;
设计思想
标准MQ
生产者与消费者 通过Message 传递内容;
Message走特定的通道MessageChannel;
Message生产者生产Message,Message订阅者消费Message;
为什么要用SpringCloudStream?
假设系统同时使用RabbitMQ、Kafka,由于这2个MQ的架构不同(比如RabbitMQ有exchange、Kafka有Topic和Partitions);
不同MQ的差异导致实际项目开发造成了困扰,比如要将一种MQ的内容迁移至另一种MQ,无疑是一场灾难;
此时MQ与系统严重耦合,而SpringCloudStream提供了一种解耦的方式;
How
在没有Binder的情况下,Spring应用要与具体的MQ进行交互,不同的MQ有差异;
通过 定义Binder作为中间层,完美实现了 应用程序与MQ 的隔离;
通过 向应用程序暴露统一的channel,使得应用程序不需要考虑不同MQ的差异;
(Input:消费者、Output:生产者)
SpringCloudStream标准流程
Binder:
连接MQ,屏蔽不同MQ的差异;
Channel:
Source/Sink:
输出:从SpringCloudStream发出消息
输入:接收消息
编码API及常用注解
How
消息生产者
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency> spring: application: name: stream-rabbit-provider cloud: stream: binders: #要绑定的RabbitMQ的服务信息 defaultRabbit: #定义的名称,用于binding整合 type: rabbit #MQ类型 environment: #RabbitMQ的相关环境配置 spring: rabbit: host: localhost port: 5672 username: guest password: guest bindings: #服务的整合处理 output: #消息生产者 destination: studyExchange #Rabbit的Exchange名称 content-type: application/json #消息类型 binder: defaultRabbit @EnableBinding(value = Source.class) public class MessageSender { @Autowired private MessageChannel output; public String send(){ output.send(MessageBuilder.withPayload("hhh").build()); return UUID.randomUUID().toString(); } } @RestController public class MessageController { @Autowired private MessageSender messageSender; @GetMapping(value = "/sendMsg") public String send(){ return messageSender.send(); } } @SpringBootApplication public class StreamRabbitProvider8801 { public static void main(String[] args) { SpringApplication.run(StreamRabbitProvider8801.class, args); } } http://localhost:8801/sendMsg
消息消费者
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency> spring: application: name: stream-rabbit-consumer cloud: stream: binders: #要绑定的RabbitMQ的服务信息 defaultRabbit: #定义的名称,用于binding整合 type: rabbit #MQ类型 environment: #RabbitMQ的相关环境配置 spring: rabbit: host: localhost port: 5672 username: guest password: guest bindings: #服务的整合处理 input: #消息消费者 destination: studyExchange #Rabbit的Exchange名称 content-type: application/json #消息类型 binder: defaultRabbit @Component @EnableBinding(value = Sink.class) public class MessageConsumer { @StreamListener(value = Sink.INPUT) public void consume(Message<String> message){ System.out.println("msg: "+ message.getPayload()); } } @SpringBootApplication public class StreamRabbitConsumer8802 { public static void main(String[] args) { SpringApplication.run(StreamRabbitConsumer8802.class, args); } }
分组消费/持久化
前言
当微服务是集群部署时,相同业务功能的微服务都会消费消息,存在重复消费的问题;
重复消费解决
使用SpringCloudStream的group;
(Stream中处于同一个group中的多个消费者是竞争关系,这样能保证消息只会被其中一个服务消费)
如果不显式多消费者进行分组,默认一个消费者一个组;
分组
原理:
相同业务功能的微服务可以分为一个group,这样同一个group内多个微服务是竞争关系,保证仅有一个消费者可以消费;
spring: application: name: stream-rabbit-consumer cloud: stream: binders: #要绑定的RabbitMQ的服务信息 defaultRabbit: #定义的名称,用于binding整合 type: rabbit #MQ类型 environment: #RabbitMQ的相关环境配置 spring: rabbit: host: localhost port: 5672 username: guest password: guest bindings: #服务的整合处理 input: #消息消费者 destination: studyExchange #Rabbit的Exchange名称 content-type: application/json #消息类型 binder: defaultRabbit group: testGroup #消费者group
持久化
对微服务 显式指定group,应用重启后 会自动获取未消费的消息;
如果不显式指定group,由于微服务故障,将会导致消息丢失;