【SpringCloud(8)】SpringCloud Stream消息驱动;Stream思想;生产者、消费者搭建

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微服务专栏

  1. 【SpringCloud(1)】初识微服务架构:创建一个简单的微服务;java与Spring与微服务;初入RestTemplate
  2. 【SpringCloud(2)】微服务注册中心:Eureka、Zookeeper;CAP分析;服务注册与服务发现;单机/集群部署Eureka;连接注册中心
  3. 【SpringCloud(3)】Ribbon负载均衡:IRule原理轮询算法;LB负载均衡;loadbalancer和IRule组件;Ribbon和Ngin负载均衡的区别
  4. 【SpringCloud(4)】OpenFeign客户端:OpenFeign服务绑定;调用服务接口;Feign和OpenFeign
  5. 【SpringCloud(5)】Hystrix断路器:服务雪崩概念;服务降级、服务熔断和服务限流概念;使用Hystrix完成服务降级与服务熔断
  6. 【SpringCloud(6)】Gateway路由网关;zuul路由;gateway实现原理和架构概念;gateway工作流程;静态转发配置
  7. 【SpringCloud(7)】SpringCloud Config分布式配置中心;服务端与客户端配置;SpringCloud Bus总线;bus刷新全局广播

💕👉博客专栏

相关推荐
韩立学长7 小时前
【开题答辩实录分享】以《自动售货机刷脸支付系统的设计与实现》为例进行答辩实录分享
vue.js·spring boot·后端
cj6341181508 小时前
DBeaver连接本地MySQL、创建数据库表的基础操作
java·后端
程序员爱钓鱼9 小时前
Python编程实战—面向对象与进阶语法 | 属性与方法
后端·python·ipython
程序员爱钓鱼9 小时前
Python编程实战——面向对象与进阶语法 | 构造函数与析构函数
后端·python·ipython
逻极9 小时前
Rust之结构体(Structs):构建自定义数据类型
开发语言·后端·rust
四念处茫茫9 小时前
Rust:复合类型(元组、数组)
开发语言·后端·rust
华仔啊9 小时前
为什么你的 @Transactional 不生效?一文搞懂 Spring 事务机制
java·后端
rengang669 小时前
512-Spring AI Alibaba 字段分类分级 Graph 示例
人工智能·spring·分类·spring ai·ai应用编程
逻极9 小时前
Rust 结构体方法(Methods):为数据附加行为
开发语言·后端·rust