目录
Spring Cloud Stream是一个用来为微服务应用构建消息驱动能力的框架,它为一些供应商的消息中间件产品提供了个性化的自动化配置实现,并且引入了发布-订阅、消费组以及分区这三个核心概念。通过使用Spring Cloud Stream,可以有效简化开发人员对消息中间件的使用复杂度,让系统开发人员可以有更多的精力关注于核心业务逻辑的处理。
在Spring Cloud Stream中,我们的微服务通过inputs或者outputs来与Spring Cloud Stream中的Binder进行交互,而这里的 Binder相当于微服务和消息中间件之间的一个粘合剂,这个Binder可以负责与消息中间件如RabbitMQ或者Kafka进行交互。拿RabbitMQ和Kafka来说,RabbitMQ支持优先级队列、延时队列、死信队列;Kafka支持消息分区...Spring Cloud Stream能够屏蔽掉这些消息中间件的具体实现功能细节,提供抽象的、统一的上层功能实现。这个时候对于开发者而言,我们只需要关注微服务和Spring Cloud Stream之间的通信方式,即消息要怎么样发送、怎么样订阅,做好这些工作之后,剩下的事情就交给Spring Cloud Stream来做,它会帮助我们完成和消息中间件之间的交互。
使用Binder,我们可以在程序运行时,动态修改消息的destination,具体到RabbitMQ中就是exchange,具体到Kafka中就是topic,这些我们都可以通过外部属性或者其它Spring Boot支持的配置方式(如application.properties或application.yml)来实现,甚至不需要改变一行代码(从下面的代码实现上来看,Spring Cloud Stream RabbitMQ和Spring Cloud Stream Kafka只有pom依赖和配置文件上的区别而已,除此之外的Java代码都是一样的)。
笔者使用的Java版本是jdk-8u201,IDE使用的是IntelliJ IDEA 2019.3 x64,Spring Boot的版本是2.1.9.RELEASE(RabbitMQ)、2.2.4.RELEASE(Kafka),Spring Cloud的版本是Greenwich.SR3(RabbitMQ)、Hoxton.SR1(Kafka)。
1 RabbitMQ
1.1 pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.hys</groupId>
<artifactId>spring-cloud-stream-rabbitmq</artifactId>
<version>1.0-SNAPSHOT</version>
<name>spring-cloud-stream-rabbitmq</name>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Greenwich.SR3</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-rabbit</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.amqp</groupId>
<artifactId>spring-rabbit-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-test-support</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
1.2 application.properties
#connect
spring.rabbitmq.password=root
spring.rabbitmq.username=root
spring.rabbitmq.host=127.0.0.1
spring.rabbitmq.port=5672
#channel
spring.cloud.stream.bindings.mychannel-input.destination=mytopic
spring.cloud.stream.bindings.mychannel-output.destination=mytopic
#消息分组
spring.cloud.stream.bindings.mychannel-input.group=g1
spring.cloud.stream.bindings.mychannel-output.group=g1
1.3 自定义Channel
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;
public interface MyChannel {
String INPUT = "mychannel-input";
String OUTPUT = "mychannel-output";
@Output(OUTPUT)
MessageChannel output();
@Input(INPUT)
SubscribableChannel input();
}
1.4 生产者代码
import com.hys.springcloudstreamrabbitmq.service.MyChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.support.MessageBuilder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Autowired
private MyChannel myChannel;
@GetMapping("test")
public void hello() {
myChannel.output().send(MessageBuilder.withPayload("hello stream!").build());
}
}
1.5 消费者代码
import com.hys.springcloudstreamrabbitmq.service.MyChannel;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
@EnableBinding(MyChannel.class)
public class MsgReceiver {
@StreamListener(MyChannel.INPUT)
public void receive(Object payload) {
System.out.println("Received:" + payload);
}
}
2 Kafka
2.1 pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.4.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.hys</groupId>
<artifactId>spring-cloud-stream-kafka</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>spring-cloud-stream-kafka</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<spring-cloud.version>Hoxton.SR1</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-streams</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-stream-binder-kafka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.2 application.properties
server.port=8080
spring.application.name=kafka-stream
spring.cloud.stream.kafka.binder.brokers=localhost:9092
spring.cloud.stream.bindings.mychannel-input.destination=mytopic
spring.cloud.stream.bindings.mychannel-output.destination=mytopic
spring.cloud.stream.bindings.mychannel-output.content-type=text/plain
#消息分组
spring.cloud.stream.bindings.mychannel-input.group=g1
spring.cloud.stream.bindings.mychannel-output.group=g1
2.3 自定义Channel
import org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;
public interface MyChannel {
String INPUT = "mychannel-input";
String OUTPUT = "mychannel-output";
@Output(OUTPUT)
MessageChannel output();
@Input(INPUT)
SubscribableChannel input();
}
2.4 生产者代码
import com.hys.kafka.service.MyChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@Autowired
private MyChannel myChannel;
@GetMapping("test")
public void hello() {
myChannel.output().send(MessageBuilder.withPayload("hello stream!").build());
}
}
2.5 消费者代码
import com.hys.kafka.service.MyChannel;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
@EnableBinding(MyChannel.class)
public class MsgReceiver {
@StreamListener(MyChannel.INPUT)
public void receive(Object payload) {
System.out.println("Received:" + payload);
}
}