SpringCloud Stream消息驱动

目录

1、概述

1.1、基本概念

1.2、SpringCloud Stream设计思想

1.2.1、标准MQ

1.2.2、SpringCloud Stream设计思想

1.3、SpringCloud Stream 的标准流程

1.4、SpringCloud Stream 编码API及常用注解

2、案例(基于RabbitMQ消息中间件)

2.1、消息生产者

2.1.1、POM文件

2.1.2、yaml配置文件

2.1.3、主启动类

2.1.4、业务类

2.1.5、Controller

2.2、消息消费者

2.2.1、POM文件与2.1.1一致

2.2.2、yaml配置文件

2.2.3、主启动类与2.1.3一致

2.2.4、controller(无业务类)

2.3、 分组消费与持久化

2.3.1、问题

2.3.2、解决方法(消息消费者分组)


1、概述

1.1、基本概念

  • SpringCloud Stream 是一个构建消息驱动微服务的框架
  • 应用程序通过 outputs 或 inputs 来与 SpringCloud Stream 中的 binder 对象交互
  • SpringCloud Stream 中的 binder 对象负责与消息中间件交互
  • 通过 SpringCloud Stream 连接消息中间件,以实现消息事件驱动

1.2、SpringCloud Stream设计思想

1.2.1、标准MQ

  • 生产者/消费者之间靠消息媒介传递信息内容:Message
  • 消息必须走特定的通道:MessageChannel
  • 消息通道中消息的收发处理:由MessageHandler消息处理器所订阅

1.2.2、SpringCloud Stream设计思想

 1.消息中间件与系统耦合度较高,当换用不同的消息中间件时,需要实现大量的代码更新工作

 2.SpringCloud Stream 实现了消息中间件的解耦

  • 通过定义绑定层作为消息中间层,实现了应用程序与消息中间件细节之间的隔离
  • 通过向应用程序暴露统一的channel通道,使得应用程序不需要考虑不同的消息中间件的实现

 3.Binder 绑定器

1.3、SpringCloud Stream 的标准流程

  • Binder:很方便的连接中间件,屏蔽差异

  • Channel:通道,是队列Queue的一种抽象,在消息通讯系统中实现存储和转发的媒介

  • Source和Sink:从Stream发布消息就是输出,接受消息就是输入

1.4、SpringCloud Stream 编码API及常用注解

2、案例(基于RabbitMQ消息中间件)

2.1、消息生产者

2.1.1、POM文件

        添加 spring-cloud-starter-stream-rabbit 依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

        <dependency>
            <groupId>com.uclass.springcloud</groupId>
            <artifactId>Api-Commons</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>

        <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.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.2、yaml配置文件

        绑定器(binders)消息通道(bindings)设置

        消息通道设置为 output

server:
  port: 8801

spring:
  application:
    name: cloud-stream-privider
  cloud:
    stream:
      binders:   #自此处配置要绑定的rabbitmq的服务信息
        defaultRabbit: #表示定义的名称,用于binding整合
          type: rabbit #消息组件类型
          environment:  # 设置rabbitmq的相关的环境配置
            spring:
              rabbitmq:
                host: localhost
                port: 5672
                username: guest
                password: guest
      bindings:   #服务的整合处理
        output: #这个名字是一个通道的名称
          destination: studyExchange #表示要使用的exchange名称定义
          content-type: application/json #设置消息类型,本次为json
          binder: {defaultRabbit}  #设置要绑定的消息服务的具体设置
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/
      instance:
        lease-expiration-duration-in-seconds: 5 #如果现在超过了5秒的间隔
        lease-renewal-interval-in-seconds: 2 #设置心跳的时间间隔
        instance-id: send-8801.com
        prefer-ip-address: true #访问的路径变为IP地址

2.1.3、主启动类

@SpringBootApplication
public class StreamMQMain8801 {
    public static void main(String[] args) {
        SpringApplication.run(StreamMQMain8801.class, args);
    }
}

2.1.4、业务类

  • 未使用 @Service 注解:此处并非调用 dao 层,实现crud操作
  • 使用 @EnableBinding(Source.class) 注解:此处定义与绑定器的交互逻辑
  • Source.class 表示是消息的推送方
//此处的业务逻辑是与绑定器进行信息交互,而不是之前的crud
@EnableBinding(Source.class)    //定义消息的推送管道(消息源)
public class MessageProviderImpl implements IMessageProvider {
    @Resource
    private MessageChannel output;  //消息发送管道

    @Override
    public String send() {
        String serial = UUID.randomUUID().toString();
        output.send(MessageBuilder.withPayload(serial).build());
        System.out.println("serial:" + serial);
        return null;
    }
}

2.1.5、Controller

        调用 Service,实现消息的推送

@RestController
public class SendMessageController {
    @Resource
    private IMessageProvider messageProvider;

    @GetMapping(value = "/sendMessage")
    public String sendMessage() {
        return messageProvider.send();
    }
}

2.2、消息消费者

2.2.1、POM文件与2.1.1一致

2.2.2、yaml配置文件

        绑定器(binders)消息通道(bindings)设置

        消息通道设置为 input

server:
  port: 8803

spring:
  application:
    name: cloud-stream-consumer
  cloud:
    stream:
      binders:   #自此处配置要绑定的rabbitmq的服务信息
        defaultRabbit: #表示定义的名称,用于binding整合
          type: rabbit #消息组件类型
          environment:  # 设置rabbitmq的相关的环境配置
            spring:
              rabbitmq:
                host: localhost
                port: 5672
                username: guest
                password: guest
      bindings:   #服务的整合处理
        input: #这个名字是一个通道的名称
          destination: studyExchange #表示要使用的exchange名称定义
          content-type: application/json #设置消息类型,本次为json
          binder: {defaultRabbit}  #设置要绑定的消息服务的具体设置
eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka/
      instance:
        lease-expiration-duration-in-seconds: 5 #如果现在超过了5秒的间隔
        lease-renewal-interval-in-seconds: 2 #设置心跳的时间间隔
        instance-id: recive-8803.com
        prefer-ip-address: true #访问的路径变为IP地址

2.2.3、主启动类与2.1.3一致

2.2.4、controller(无业务类)

  • 使用 @EnableBinding(Sink.class) 注解:此处定义与绑定器的交互逻辑
  • Sink.class 表示是消息的接收方
@RestController
@EnableBinding(Sink.class)
public class ReceiveMessageListenerController {
    @Value("${server.port}")
    private String serverPort;

    @StreamListener(Sink.INPUT)
    public void input(Message<String> message) {
        System.out.println("消费者2号" + message.getPayload() + "\t port:" + serverPort);
    }
}

2.3、 分组消费与持久化

2.3.1、问题

 1)依照8803,clone出来一份消息消费者:8804

 2)存在问题:

  • 消息重复消费:即8803和8804均接收了消息发送方的消息
  • 消息持久化:当微服务宕机,已发送的消息的处理问题

2.3.2、解决方法(消息消费者分组)

 1)分组原理:

  • 同一个组中的多个微服务,消息只会被其中一个消费一次
  • 不同组中的微服务,均可以实现对同一个消息的消费

 2)分组操作:修改yaml配置文件

        在上述yaml配置文件的 bindings 属性中添加 group 属性,实现分组

 3)测试:

        1、重复消费测试:

        8803/8804实现了轮询分组,每次只有一个消费者。

        8801模块的发的消息,只能被8803或者8804其中一个接受到

        2、持久化测试:

停止8803/8804;除掉8803的分组group:UCLASSA;8004的分组属性保留

8801先发送4条消息到rabbitmq

先启动8803,无分组属性配置,后台没有打出来消息

再启动8804,有分组属性配置,后台打出来了MQ上的消息

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值