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 实现的。
如上图所示,在实际的生产环境下,开发者只需要定义 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