11 消息驱动 Stream

消息驱动 Stream

SpringCloud Stream 可以屏蔽底层消息中间件的差异,降低切换成本,同意消息的编程模型

官网:https://spring.io/projects/spring-cloud-stream#overview

API:https://cloud.spring.io/spring-cloud-static/spring-cloud-stream/3.0.1.RELEASE/reference/html/

中文指导手册:https://m.wang1314.com/doc/webapp/topic/20971999.html

设计思想


Binder:

Spring Cloud Stream标准流程套路

编码API和常用注解

消息驱动之生产者

  1. 新建模块cloud-stream-rabbitmq-provider8801
  2. pom
<dependencies>
    <!--stream rabbit -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
    </dependency>
    <!--eureka client-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </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>

  1. yml
server:
  port: 8801

spring:
  application:
    name: cloud-stream-provider
  cloud:
    stream:
      binders: #在此处配置要绑定的rabbitmq的服务信息
        defaultRabbit: #表示定义的名称,用于binding整合
          type: rabbit #消息组件类型
          environment: #设置rabbitmq的相关环境配置
            spring:
              rabbitmq:
                host: 112.124.22.24  #RabbitMQ在本机的用localhost,在服务器的用服务器的ip地址
                port: 5672
                username: admin
                password: 123
      bindings: #服务的整合处理
        output: #这个名字是一个通道的名称
          destination: studyExchange #表示要使用的Exchange名称定义
          content-type: application/json #设置消息类型,本次为json,本文要设置为“text/plain”
          binder: defaultRabbit #设置要绑定的消息服务的具体设置(爆红不影响使用,位置没错)

eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    lease-renewal-interval-in-seconds: 2 #设置心跳的时间间隔(默认是30S)
    lease-expiration-duration-in-seconds: 5 #如果超过5S间隔就注销节点 默认是90s
    instance-id: send-8801.com #在信息列表时显示主机名称
    prefer-ip-address: true #访问的路径变为IP地址

  1. 主启动类
@SpringBootApplication
public class StreamMQMain8801 {

    public static void main(String[] args) {
        SpringApplication.run(StreamMQMain8801.class, args);
    }
}
  1. 新建service.IMessageProvider接口 发送消息接口、
public interface IMessageProvider {
    public String send();
}
  1. 在service下新建impl.IMessageProviderImpl实现类
import com.atuigu.springcloud.service.IMessageProvider;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.messaging.MessageChannel;
import javax.annotation.Resource;
import java.util.UUID;

@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());
        //发送消息到 RabbitMQ
        System.out.println("*****serial"+serial);
        return null;
    }
}
  1. 新建controller.SendMessageController
@RestController
public class SendMessageController {

    @Resource
    private IMessageProvider messageProvider;

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

}

  1. 测试 启动 7001 RabbitMQ 8801

在浏览器输入:http://localhost:8801/sendMessage,多次刷新,后台打印的数据:

在RabbitMQ后台可以看到新生成的交换机 和 消息的曲线图

消息驱动之消费者

  1. 新建模块cloud-stream-rabbitmq-consumer8802
  2. pom
<dependencies>
    <!--stream rabbit -->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-stream-rabbit</artifactId>
    </dependency>
    <!--eureka client-->
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </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>

  1. yml
server:
  port: 8802

spring:
  application:
    name: cloud-stream-consumer
  cloud:
    stream:
      binders: #在此处配置要绑定的rabbitmq的服务信息
        defaultRabbit: #表示定义的名称,用于binding整合
          type: rabbit #消息组件类型
          environment: #设置rabbitmq的相关环境配置
            spring:
              rabbitmq:
                host: 112.124.22.24  #RabbitMQ在本机的用localhost,在服务器的用服务器的ip地址
                port: 5672
                username: admin
                password: 123
      bindings: #服务的整合处理
        input: #这个名字是一个通道的名称
          destination: studyExchange #表示要使用的Exchange名称定义
          content-type: application/json #设置消息类型,本次为json,本文要设置为“text/plain”
          binder: defaultRabbit #设置要绑定的消息服务的具体设置(爆红不影响使用,位置没错)

eureka:
  client:
    service-url:
      defaultZone: http://localhost:7001/eureka
  instance:
    lease-renewal-interval-in-seconds: 2 #设置心跳的时间间隔(默认是30S)
    lease-expiration-duration-in-seconds: 5 #如果超过5S间隔就注销节点 默认是90s
    instance-id: receive-8802.com #在信息列表时显示主机名称
    prefer-ip-address: true #访问的路径变为IP地址

  1. 主启动类
@SpringBootApplication
public class StreamMQMain8802 {

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

}
  1. 新建controller.ReceiveMessageListenerController
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.messaging.Sink;
import org.springframework.messaging.Message;
import org.springframework.stereotype.Component;

@Component
@EnableBinding(Sink.class)
public class ReceiverMessageListenerController {

    @Value("${server.port}")
    private String serverPort;

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

  1. 测试
    启动7001,8801,8802

http://localhost:8801/sendMessage(8801发送消息)

8802接收到消息:控制台可以看到输出的信息

分组消费与持久化


又根据8802 创建了一个 消费者 8803



同组是竞争关系,只有其中一个可以消费

8802 8803 在不同的组

故障现象:重复消费

导致原因:默认分组group 不同的 ,组流水号不同,被认为不同组,可以消费

自定义分组 再自定义配置分为同一个组,解决重复消费 问题

分组(队列)

测试分组

8802 8803 设置不同组

修改8802的yml

修改8803的yml

测试:

设置相同分组

修改8803的yml中group为angeninA,然后重启8003。



结论:同一个组的微服务实例,每次只会有一个拿到

持久化

  1. 停掉8802和8803,去掉8802的group: atguiguA
  2. 然后8801发送4条消息。
  3. 启动8802,8802并没有去拿取消息。(因为8802去掉了group: atguiguA,所以启动后会再新建一个队列)
  4. 启动8803,启动后获取到8801的消息。(因为8803没删除group: atguiguA,atguiguA队列是在8801发送消息前存在的,所以当8803停机后再启动,就可以获取到停机时8801发送的信息(如果此时同组(队列)里有别的消费者,那么消息会被别的消费者消费掉))

没有设置分组属性的情况下,stream会自动帮你生成一个临时队列,服务器下线就会删除队列,而带有分组属性的,就是一个持久化队列,服务器下线也不会被删除,重新上线就会获取到消息

posted @   flypiggg  阅读(40)  评论(0编辑  收藏  举报
编辑推荐:
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示