微服务SpringCloud(Stream 消息驱动)整合二十

什么是SpringCloud Stream ?

SpringCloud Stream 是一个构建消息驱动微服务的框架,应用程序通过inputs或者outputs来与Spring Cloud Stream 中binder对象交互,通过我们配置bingding(绑定),而SpringCloud Stream 的binder对象负责与消息中间件交互。通过使用Spring Integration来连接消息代理中间件以实现消息事件驱动。
SpringCloud Stream 为一些供应商的消息中间件产品提供了个性化的自动化配置实现,引用了发布-订阅、消费组、分区的三个核心概念,目前仅支持RabbitMQ、Kafka。

SpringCloud Stream 官网地址:
https://cloud.spring.io/spring-cloud-static/spring-cloud-stream/3.0.1.RELEASE/reference/html/

为什么使用SpringCloud Stream

比方说我们用到了RabbitMQ和Kafka,由于这两个消息中间件的架构上的不同,像RabbitMQ有exchange,kafka有Topic和Partitions分区。这些中间件的差异性导致我们实际项目开发给我们造成了一定的困扰,我们如果用了两个消息队列的其中一种,后面的业务需求,我想往另外一种消息队列进行迁移,这时候无疑就是一个灾难性的,一大堆东西都要重新推倒重新做,因为它跟我们的系统耦合了,这时候springcloud Stream给我们提供了一种解耦合的方式。

SpringCloud Stream 的重要组件Binder

在没有绑定器这个概念的情况下,我们的SpringBoot应用要直接与消息中间件进行信息交互的时候,由于各消息中间件构建的初衷不同,它们的实现细节上会有较大的差异性,通过定义绑定器作为中间层,完美地实现了应用程序与消息中间件细节之间的隔离。Stream对消息中间件的进一步封装,可以做到代码层面对中间件的无感知,甚至于动态的切换中间件(rabbitmq切换为kafka),使得微服务开发的高度解耦,服务可以关注更多自己的业务流程。

在这里插入图片描述
通过定义绑定器Binder作为中间层,实现了应用程序与消息中间件细节之间的隔离。Binder可以生成Binding,Binding用来绑定消息容器的生产者和消费者,它有两种类型,INPUT和OUTPUT,INPUT对应于消费者,OUTPUT对应于生产者。

SpringCloud Stream 处理流程

在这里插入图片描述

SpringCloud Stream API和注解说明
  • Middleware 中间件,目前只支持RabbitMQ和Kafka
  • Binder:Binder是应用与消息中间之间的封装,目前实现了Kafka和RabbitMQ的Binder,通过Binder可以很方便的连接中间件,可以动态的改变消息类型。
  • @Input: 注解标识输入通道,通过该输入通道接收到的消息进入应用程序
  • @Output:注解标识输出通道,发布的消息将通过该通道离开应用程序
  • @StreamListener: 监听队列,用于消费的队列的消息接收
  • @EnableBinding: 值信道channel和Exchange绑定在一起
SpringCloud Stream 项目整合

我们之前已经搭建了RabbitMQ,所以RabbitMQ环境已经没问题了。现在我们在工程中新增三个子模块
pcloud-stream-rabbitmq-provider9010 作为生产者进行发消息模块
pcloud-stream-rabbitmq-consumer9011 作为消息接收模块
pcloud-stream-rabbitmq-consumer9012 作为消息接收模块
新建Module pcloud-stream-rabbitmq-provider9010
在这里插入图片描述
在这里插入图片描述
修改pom文件

  <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>

新增application.yml文件:

server:
  port: 9010

spring:
  application:
    name: pcloud-stream-provider
  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,文本则设置“text/plain”
          binder: defaultRabbit # 设置要绑定的消息服务的具体设置

# 客户端进行Eureka注册的配置
eureka:
  client:
    service-url:
      defaultZone: http://eureka6001:6001/eureka/

新增启动类: StreamMain9010

package com.younger.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class StreamMain9010 {

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

在这里插入图片描述
新增 MessageProvider 类

package com.younger.springcloud.service;

public interface MessageProvider {

    String send();
}

新增 MessageProviderImpl类

package com.younger.springcloud.service.impl;

import com.younger.springcloud.service.MessageProvider;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;

import org.springframework.messaging.MessageChannel;

import javax.annotation.Resource;
import java.util.Date;

@EnableBinding(Source.class)
public class MessageProviderImpl implements MessageProvider {

    // 消息的发送管道 Output
    @Resource
    private MessageChannel messageChannel;

    @Override
    public String send() {
        String message = new Date() + "@ younger 你好";
        messageChannel.send(MessageBuilder.withPayload(message).build());
        System.out.println("发送消息:" + message);
        return message;
    }
}

新增 SendMessageController类

package com.younger.springcloud.controller;

import com.younger.springcloud.service.MessageProvider;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
public class SendMessageController {

    @Resource
    private MessageProvider messageProvider;

    /**
     * 发送消息
     * @return
     */
    @GetMapping("/sendMessage")
    public String sendMessage() {
        return messageProvider.send();
    }
}

启动项目 测试:
在这里插入图片描述

访问地址:http://localhost:9010/sendMessage
在这里插入图片描述
新建Module pcloud-stream-rabbitmq-consumer9011

在这里插入图片描述
在这里插入图片描述
新增pom文件:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>


  <parent>
    <artifactId>pcloud</artifactId>
    <groupId>com.younger.springcloud</groupId>
    <version>1.0-SNAPSHOT</version>
  </parent>

  <groupId>com.younger.springcloud</groupId>
  <artifactId>pcloud-stream-rabbitmq-consumer9011</artifactId>
  <version>1.0-SNAPSHOT</version>

  <name>pcloud-stream-rabbitmq-consumer9011</name>

  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</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-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>
</project>

新增application.yml文件:

server:
  port: 9011

spring:
  application:
    name: pcloud-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: myExchange # 表示要使用的Exchange名称定义
          content-type: application/json # 设置消息类型,本次为对象json,如果是文本则设置“text/plain”
          binder: defaultRabbit # 设置要绑定的消息服务的具体设置

eureka:
  client: # 客户端进行Eureka注册的配置
    service-url:
      defaultZone: http://eureka6001:6001/eureka/

新增启动类:StreamMain9011

package com.younger.springcloud;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

在这里插入图片描述
新增 ReceiveMessageListener类:

package com.younger.springcloud.service;

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 ReceiveMessageListener {

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

    @StreamListener(Sink.INPUT)
    public void input(Message<String> message) {
        System.out.println("收到消息:"+ message.getPayload() +" 端口号为:\t " + serverPort);
    }
}

启动项目测试:
在这里插入图片描述
发送消息: http://localhost:9010/sendMessage
在这里插入图片描述
在这里插入图片描述
可以看到 我们在9010 上收到消息了。

我们新建Module pcloud-stream-rabbitmq-consumer9012
在这里插入图片描述
在这里插入图片描述
新增pom文件:

<dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</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-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>

新增application.yml文件:

server:
  port: 9012

spring:
  application:
    name: pcloud-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: myExchange # 表示要使用的Exchange名称定义
          content-type: application/json # 设置消息类型,本次为对象json,如果是文本则设置“text/plain”
          binder: defaultRabbit # 设置要绑定的消息服务的具体设置

eureka:
  client: # 客户端进行Eureka注册的配置
    service-url:
      defaultZone: http://eureka6001:6001/eureka/

新增启动类: StreamMain9012

package com.younger.springcloud;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class StreamMain9012 {

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

新增 ReceiveMessageListener 类:在这里插入图片描述

package com.younger.springcloud.service;

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 ReceiveMessageListener {

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

    @StreamListener(Sink.INPUT)
    public void input(Message<String> message) {
        System.out.println("收到消息:"+ message.getPayload() +" 端口号为:\t "+ serverPort);
    }
}

启动项目测试:
在这里插入图片描述
发送消息: http://localhost:9010/sendMessage
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

9011和9012 都收到了消息,这个就产生了重复消费的问题了。为了避免重复消费的问题,Stream是通过消息分组来解决。微服务应用放置于同一个group中,就能够保证消息只会被其中一个应用消费一次。
不同的组是可以消费的,同一个组内会发生竞争关系,只有其中一个可以消费。

我们修改9011和9012的yml文件:
在这里插入图片描述
设置9011和9012 为同一个组。

重新启动9011和9012测试:
在这里插入图片描述
发送消息: http://localhost:9010/sendMessage
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
可以看到,只有9011收到了消息。

我们现在设想一下,我们关闭9011和9012,然后在通过9010发送消息。我们的消息会丢失吗?我们测试一下:
关闭9011 和 9012
在这里插入图片描述
发送消息: http://localhost:9010/sendMessage
在这里插入图片描述
我们在重新启动 9011 和 9012看下:
在这里插入图片描述
在这里插入图片描述
可以看到,我们是可以收到消息的,这个就说明SpringCloud Stream是可以对消息进行持久化的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

youngerone123

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值