1. SpringCloud Stream消息驱动说明
- 屏蔽底层消息中间件的差异,降低切换成本,同一消息的变成模型
1.1 说明是SpringCloudStream
官方定义 SpringCloud Stream 是一个构建消息驱动微服务的框架
应用程序通过 inputs 或者 outputs 来与 SpringCloud Stream 中的binder对象交互
通过我们配置来绑定,而Stream的binder对象负责与消息中间件交互
我们只需要清除如何与Stream交互就可以方便使用消息驱动的方式
通过使用SpringIntegration来连接消息代理中间件以实现消息事件驱动
SpringCloud Stream为一些供应商的消息中间件产品提供了个性化的自动化配置实现,引用了发布-订阅、消费组、分区的三个核心概念
目前仅支持RabbitMQ、Kafka
1.2 Stream的设计思想
在没有绑定器这个概念的情况下,我们的SpringBoot应用要直接与消息中间件进行信息交互的时候,由于各种消息中间件构建的初衷不同,他们的实现细节上会有较大的差异性,通过定义绑定器Binder作为中间层,实现了应用程序与消息中间件细节之间的隔离。
Stream对消息中间件的进一步封装,可以做到代码层面对中间件的无感知,甚至与动态的切换中间件(rabbitmq切换为kafka),使得微服务开发的高度解耦,服务可以关注更多自己的业务流程。

2. Stream的使用
在Stream3.1往后,都将使用函数式开发,而不是注解开发了,所以以前的@EnableBinding、@Output注解都过时了
pom.xml
xml
<dependencies>
<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>
</dependencies>
2.1 生产者(provider):
yaml文件:
yaml
server:
port: 8801
spring:
application:
name: cloud-stream-provider
rabbitmq:
host: 192.168.21.130
port: 5672
username: admin
password: hejinyuan
cloud:
stream:
bindings:
MyChannel-out-0: # 定义的通道名称
destination: studyExchange #表示要使用Exchange名称定义
contentType: application/json #设置消息类型,本次为json,文本则设置为"text/plain"
eureka:
client: # 客户端进行Eureka注册的配置
service-url:
defaultZone: http://localhost:7001/eureka
通道的名称一般都是:名称-out/in-数字 的方式命名
通道下的destination属性,定义的是该通道使用的交换机
接口与实现类:
java
public interface IMessageService {
public String send();
}
java
@Service
public class MessageServiceImpl implements IMessageService {
@Autowired
private StreamBridge streamBridge;
@Override
public String send() {
String s = UUID.randomUUID().toString();
/**
* .send()
* 参数一:连接的通道名称,在yaml中定义
* 参数二:想要发送的消息
*/
streamBridge.send("MyChannel-out-0", MessageBuilder.withPayload(s).build());
System.out.println("发送消息:"+s);
return null;
}
}
控制器:
java
@RestController
public class SendMessageController {
@Autowired
private IMessageService messageService;
@GetMapping("/sendMessage")
public String sendMessage(){
return messageService.send();
}
}
2.2 消费者(consumer):
yaml文件
yaml
server:
port: 8802
spring:
application:
name: cloud-stream-consumer
rabbitmq:
host: 192.168.21.130
port: 5672
username: admin
password: hejinyuan
cloud:
stream:
bindings: #服务的整合处理
myChannel-in-0: #这个名字是一个通道的名字
destination: studyExchange #表示要使用Exchange名称定义
contentType: application/json #设置消息类型,本次为json,文本则设置为"text/plain"
eureka:
client: # 客户端进行Eureka注册的配置
service-url:
defaultZone: http://localhost:7001/eureka
注意在消费者这边,通道名称由out变为了in,这个必须对接上,还有交换机名称也必须对应
服务层:
java
@Service
public class StreamConsumerService {
/**
* 代码的方法名(即Consumer的bean实例名)需要是yml配置中的通道名,
* 应用程序启动后会自动接收生产者发送的消息
* @return
*/
@Bean
public Consumer<String > myChannel(){
return message -> System.out.println("8802已接收消息:"+message);
}
}
3. group解决消息重复消费
生产者发送消息:发送消息:a8d0f9f0-f2b8-494e-9510-3f3cd400fb97
消费者8802:8802已接收消息:a8d0f9f0-f2b8-494e-9510-3f3cd400fb97
消费者8803:8803已接收消息:a8d0f9f0-f2b8-494e-9510-3f3cd400fb97
一个消息被两个消费者消费,不合理!
微服务一个用放置于一个同一个group中,就能够保证消息只会被其中一个应用消费以此
不同的组是可以消费的,同一个组内会发生竞争关系,只有其中一个可用消费
解决办法就是将消费者放入同一个组中发生竞争关系即可
两个消费者的group组名一致就可以解决重复消费问题了
yaml
myChannel-in-0: #这个名字是一个通道的名字
destination: studyExchange #表示要使用Exchange名称定义
contentType: application/json #设置消息类型,本次为json,文本则设置为"text/plain"
group: group01 # 组名一致,解决重复消费问题
最后再请非常的注意一点!!哪怕重复消息问题不出现,也请为每个通道添加上group组!否则在停机后会造成消息丢失故障!
🥸🏏SpringCloud微服务专栏
- 【SpringCloud(1)】初识微服务架构:创建一个简单的微服务;java与Spring与微服务;初入RestTemplate
- 【SpringCloud(2)】微服务注册中心:Eureka、Zookeeper;CAP分析;服务注册与服务发现;单机/集群部署Eureka;连接注册中心
- 【SpringCloud(3)】Ribbon负载均衡:IRule原理轮询算法;LB负载均衡;loadbalancer和IRule组件;Ribbon和Ngin负载均衡的区别
- 【SpringCloud(4)】OpenFeign客户端:OpenFeign服务绑定;调用服务接口;Feign和OpenFeign
- 【SpringCloud(5)】Hystrix断路器:服务雪崩概念;服务降级、服务熔断和服务限流概念;使用Hystrix完成服务降级与服务熔断
- 【SpringCloud(6)】Gateway路由网关;zuul路由;gateway实现原理和架构概念;gateway工作流程;静态转发配置
- 【SpringCloud(7)】SpringCloud Config分布式配置中心;服务端与客户端配置;SpringCloud Bus总线;bus刷新全局广播