Spring Cloud Stream 是 Netflix 提供的数据流操作开发包,是为了提供微服务间消息通信而产生的一种框架,封装了与 RabbitMQ、Kafka 等中间件交互的操作。Spring Cloud Stream 基于 Spring Boot 构建,开发者可以很方便地实现自己的消息通信实例。Spring Cloud Stream 为不同的消息中间件提供了个性化的自动化配置实现,引用了发布 - 订阅、消费组、分区的三个核心概念。

1. Spring Cloud Stream 说明

Spring Cloud Stream 应用由第三方的中间件组成。应用间的通信通过输入通道(input channel)和输出通道(output channel)完成。这些通道是有 Spring Cloud Stream 注入的。而通道与外部的消息队列的连接又是通过 Binder 实现的。

Spring Cloud Stream 还在维护吗 spring cloud stream function_xhtml

如上图所示,在实际的生产环境下,开发者只需要定义 input 和 output 输出流即可,Middleware 中间件目前支持 Kafka 和 RabbitMQ。

体验 Spring Cloud Stream 需要你的主机有以下环境依赖 (以 RabbitMQ 为例):

软件环境

版本信息

jdk

1.8+

RabbitMQ

3.0+

2. 生产者实现

本文介绍的实例主要实现了三种 message 类型,分别是 String、JSON、Object 对象等。首先,pom.xml 文件中加入依赖:

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

增加消息通道接口:

public interface StreamChannelConfig {
    /**
     * 消息生产者
     */
    String ENTITY_PRODUCER = "testStreamEntityProducer";
    @Output(ENTITY_PRODUCER)
    MessageChannel producerEntity();
    /**
     * 消息生产者
     */
    String JSON_PRODUCER = "testStreamJSONProducer";
    @Output(JSON_PRODUCER)
    MessageChannel producerJSON();
    /**
     * 消息生产者
     */
    String STRING_PRODUCER = "testStreamStringProducer";
    @Output(STRING_PRODUCER)
    MessageChannel producerString();
}

实现消息发送:

@Component
@EnableBinding(StreamChannelConfig.class)
public class TestProducer {
    private static final Logger LOG = LoggerFactory.getLogger(TestProducer.class);
    @Resource
    private StreamChannelConfig testStreamChannel;
    /**
     * 发送消息
     * @param object
     */
    public void sendEntityMessage(Object object) {
        LOG.info("发送消息:{}", object.toString());
        testStreamChannel.producerEntity().send(MessageBuilder.withPayload(object).build());
    }
    /**
     * 发送消息
     * @param json
     */
    public void sendJSONMessage(JSONObject json) {
        LOG.info("发送消息:{}", json.toJSONString());
        testStreamChannel.producerJSON().send(MessageBuilder.withPayload(json).build());
    }
    /**
     * 发送消息
     * @param message
     */
    public void sendStringMessage(String message) {
        LOG.info("发送消息:{}", message);
        testStreamChannel.producerString().send(MessageBuilder.withPayload(message).build());
    }
}

配置文件中加入相关配置:

#rabbitmq
spring.rabbitmq.addresses=RabbitMQ所在IP:Port
spring.rabbitmq.username=XXX
spring.rabbitmq.password=XXX
#stream
spring.cloud.stream.default.contentType=application/json
spring.cloud.stream.bindings.testStreamEntityProducer.destination=testStreamEntityExchange
spring.cloud.stream.rabbit.bindings.testStreamEntityProducer.producer.routing-key-expression='testStreamEntityRoutingKey'
spring.cloud.stream.bindings.testStreamJSONProducer.destination=testStreamJSONExchange
spring.cloud.stream.rabbit.bindings.testStreamJSONProducer.producer.routing-key-expression='testStreamJSONRoutingKey'
spring.cloud.stream.bindings.testStreamStringProducer.destination=testStreamStringExchange
spring.cloud.stream.rabbit.bindings.testStreamStringProducer.producer.routing-key-expression='testStreamStringRoutingKey'

至此,已经实现了消息的生产者端,这就能看到 Spring Cloud Stream 的优点了,通过不到 10 行的代码,就能实现了对 RabbitMQ 的消息发送。

3. 消费者实现

添加依赖:

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

定义消费者通道接口:

public interface StreamChannelConfig {
    /**
     * 消费者
     */
    String ENTITY_CONSUMER = "testStreamEntityConsumer";
    @Input(ENTITY_CONSUMER)
    SubscribableChannel consumerEntity();
    /**
     * 消息消费者
     */
    String JSON_CONSUMER = "testStreamJSONConsumer";
    @Input(JSON_CONSUMER)
    SubscribableChannel consumerJSON();
    /**
     * 消息消费者
     */
    String STRING_CONSUMER = "testStreamStringConsumer";
    @Input(STRING_CONSUMER)
    SubscribableChannel consumerString();
}

实现消息接收:

@Component
@EnableBinding(StreamChannelConfig.class)
public class TestConsumer {
    private static final Logger LOG = LoggerFactory.getLogger(TestConsumer.class);
    /**
     * 消息消费
     * @param object
     */
    @StreamListener(StreamChannelConfig.ENTITY_CONSUMER)
    public void consumerEntity(Object object) {
        LOG.info("接收消息:{}", object.toString());
    }
    /**
     * 消息消费
     * @param json
     */
    @StreamListener(StreamChannelConfig.JSON_CONSUMER)
    public void consumerJSON(JSONObject json) {
        LOG.info("接收消息:{}", json.toJSONString());
    }
    /**
     * 消息消费
     * @param message
     */
    @StreamListener(StreamChannelConfig.STRING_CONSUMER)
    public void consumerString(String message) {
        LOG.info("接收消息:{}", message);
    }
}

配置文件增加:

#rabbitmq
spring.rabbitmq.addresses=RabbitMQ所在IP:Port
spring.rabbitmq.username=XXX
spring.rabbitmq.password=XXXX
#stream
spring.cloud.stream.default.contentType=application/json
spring.cloud.stream.bindings.testStreamEntityConsumer.destination=testStreamEntityExchange
spring.cloud.stream.bindings.testStreamEntityConsumer.group=testStreamEntityQueueName
spring.cloud.stream.rabbit.bindings.testStreamEntityConsumer.consumer.bindingRoutingKey=testStreamEntityRoutingKey
spring.cloud.stream.bindings.testStreamJSONConsumer.destination=testStreamJSONExchange
spring.cloud.stream.bindings.testStreamJSONConsumer.group=testStreamJSONQueueName
spring.cloud.stream.rabbit.bindings.testStreamJSONConsumer.consumer.bindingRoutingKey=testStreamJSONRoutingKey
spring.cloud.stream.bindings.testStreamStringConsumer.destination=testStreamStringExchange
spring.cloud.stream.bindings.testStreamStringConsumer.group=testStreamStringQueueName
spring.cloud.stream.rabbit.bindings.testStreamStringConsumer.consumer.bindingRoutingKey=testStreamStringRoutingKey

注意配置文件中的 destination 为消息的目的地(类似于 Kafka 的 topic 与 RabbitMQ 的 exchange),需要生产者和消费者保持相同,exchangeType 表示采用的是 topic 模式。group 为一个组(Spring Cloud Stream 中每个 group 都会接收都消息,且只接收一次)。

4. 总结

Spring Cloud Stream 最大的好处在于,抽象了微服务开发中事件驱动的一些概念,对消息中间件的使用做了进一步封装,可以做到代码层面对中间件的无感知,甚至于动态的切换中间件(从 Kafka 切换到 RabbitMQ 而不需要改代码)。使得微服务的消息通信开发做到了高度解耦。


作者:zhaoyh