1. 前言

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

2. SpringCloudStream介绍

Spring Cloud Stream是一个用来为微服务应用构建消息驱动能力的框架。它可以基于Spring Boot来创建独立的、可用于生产的Spring应用程序。它通过使用Spring Integration来连接消息代理中间件以实现消息事件驱动的微服务应用。

Spring Cloud Stream为一些供应商的消息中间件产品提供了个性化的自动化配置实现,并且引入了发布-订阅、消费组以及消息分区这三个核心概念。简单的说,Spring Cloud Stream本质上就是整合了Spring Boot和Spring Integration,实现了一套轻量级的消息驱动的微服务框架。

通过使用Spring Cloud Stream,可以有效地简化开发人员对消息中间件的使用复杂度,让系统开发人员可以有更多的精力关注于核心业务逻辑的处理。目前Spring Cloud Stream官方的实现有RabbitMQ Binder和Kafka Binder,而Spring Cloud Alibaba内部已经实现了RocketMQ Binder,处理模型图如下:
微服务轮子项目(32) -SpringCloudStream消息框架_# 微服务轮子项目
从图中可以看出,Binding 是连接应用程序跟消息中间件的桥梁,用于消息的消费和生产。

关键概念:

  • Inputs:接收消息的通道
  • Output:发送消息的通道
  • Binder:可理解为一个抽象的中间件,应用通过在spring cloud stream中所注入的inputs,outputs通道来跟外界消息通信,而这些通道又是通过具体中间件的Binder实现来连接到消息队列的服务器上。有了Binder,甚至可以不改一行代码,就切换中间件的类型。
  • Group:消费组,一个消息到达一个消费组后,只能被这个消费组的其中一个实例消费掉;
  • Partion:消息分区,一个非常大的topic可以分布到多个broker(即服务器)上。

3. 项目集成

以下以RocketMQ为具体中间件作为示例。

3.1 添加依赖

在pom.xml中添加依赖

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

3.2 生产者

1.自定义通道的创建

@EnableBinding({MySource.class})
public class RocketMqConfig {
    public interface MySource {
        @Output(Source.OUTPUT)
        MessageChannel output();
    }
}

要加上@EnableBinding绑定通道才能够发出消息到mq的服务器。

2.发送消息的函数

@Service
public class SenderService {
   @Autowired
   private MySource source;

   public void send(String msg) {
      source.output().send(MessageBuilder.withPayload(msg).build());
   }
}

3.对应的配置application.yml:

spring:
  cloud:
    stream:
      rocketmq:
        binder:
          namesrv-addr: 127.0.0.1:9876
        bindings:
          output:
            producer:
              group: produce-group
      bindings:
        output:
          destination: test-topic
          content-type: application/json

3.3 消费者

1.自定义通道的创建:

@EnableBinding({MySink.class})
public class RocketMqConfig {
    public interface MySink {
        @Input(Sink.INPUT)
        SubscribableChannel input();
    }
}

2.接收消息的函数:

@Service
public class ReceiveService {
   @StreamListener(Sink.INPUT)
   public void receiveInput(String receiveMsg) {
      System.out.println("input receive: " + receiveMsg);
   }
}

3.对应的配置application.yml:

spring:
  cloud:
    stream:
      rocketmq:
        binder:
          namesrv-addr: 127.0.0.1:9876
      bindings:
        input:
          destination: test-topic
          content-type: text/plain
          group: consume-group1