Spring Cloud Stream 的 binder 负责与消息中间件交互(和消息中间件解耦,不需要关注具体的消息中间件)应用通过Spring Cloud Stream插入的input(相当于消费者consumer,它是从队列中接收消息的)和output(相当于生产者producer,它是从队列中发送消息的。)通道与外界交流。当需要升级消息中间件,或者是更换其他消息中间件产品时,我们需要做的就是更换对应的Binder绑定器而不需要修改任何应用逻辑。
Stream内置接口
Stream内置了三个接口,分别是Source,Sink,Processor
Source是输出通道,通道名是output
public interface Source {
String OUTPUT = "output";
@Output("output")
MessageChannel output();
}
Sink是输入通道,通道名是input
public interface Sink {
String INPUT = "input";
@Input("input")
SubscribableChannel input();
}
Processor即是输入通道,也是输出通道,通道名是output,input
public interface Processor extends Source, Sink {
}
下面我们使用stream提供的三个通道来做一个demo
- 生产者发送消息到中转者
- 中转者接收消息并对消息做出修改,接着中转者将消息发送给消费者
- 消费者消费消息
下面三个模块的启动类都不变,使用默认的就好,就不贴出来了
创建消息发送者
新建模块stream-producer-8013,添加依赖,为了方便就不将服务注册到注册中心了,等会测试只需要启动这三个模块就可以了
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
添加配置
server:
port: 8013
spring:
application:
name: stream-producer
cloud:
stream:
binders: #在此处配置要绑定的rabbitmq的服务信息
myrabbit: #自定义的名称
type: rabbit #消息组件类型
environment: #设置rabbitmq的相关的环境配置
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
bindings:
output: #绑定的通道,这里使用的是stream提供的输出通道
destination: msg #发送的目的地
content-type: application/json #消息类型
创建一个user类,等会发送消息就发送这个user
public class User implements Serializable {
private int id;
private String name;
public User() {
}
public User(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
创建消息发送者来发送消息
//绑定输出通道
@EnableBinding(Source.class)
public class Producer {
@Autowired
private Source source;
public void sendMsg(User user){
//发送消息
source.output().send(MessageBuilder.withPayload(user).build());
}
}
提供一个访问接口
@RestController
public class ProducerController {
@Autowired
private Producer producer;
@RequestMapping("/sendMsg")
public void sendMsg(){
User user = new User(1,"小王");
producer.sendMsg(user);
}
}
至此,消息发送者创建完毕
创建消息中转者
创建stream-transfer-8014模块,添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
添加配置
server:
port: 8014
spring:
application:
name: stream-producer
cloud:
stream:
binders: #在此处配置要绑定的rabbitmq的服务信息
myrabbit: #自定义的名称
type: rabbit #消息组件类型
environment: #设置rabbitmq的相关的环境配置
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
bindings:
input: #绑定的通道,这里使用的是stream提供的输入通道
destination: msg #从该目的地接受消息,上面消息发送者是向这个目的地发送消息
content-type: application/json #消息类型
output: #绑定的通道,这里使用的是stream提供的输出通道
destination: transfer-msg #消息中转者等会向这个目的地发送消息
content-type: application/json #消息类型
创建消息中转者
//绑定Processor通道
@EnableBinding(Processor.class)
public class Transfer {
/**
* 监控input通道,接收到的消息就是obj,最后发送加工过的消息到output通道
* @param obj 接收到的消息
* @return 加工后的消息
*/
@ServiceActivator(inputChannel = Processor.INPUT, outputChannel = Processor.OUTPUT)
public Object getMsg(Object obj){
//这里先将消息输出,再返回加工后的消息
System.out.println(obj);
return "经过中转者加工过的消息:"+obj;
}
}
至此,消息中转者创建完毕
创建消息消费者
创建模块stream-consumer-8015,添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-stream-rabbit</artifactId>
</dependency>
添加配置
server:
port: 8015
spring:
application:
name: stream-producer
cloud:
stream:
binders: #在此处配置要绑定的rabbitmq的服务信息
myrabbit: #自定义的名称
type: rabbit #消息组件类型
environment: #设置rabbitmq的相关的环境配置
spring:
rabbitmq:
host: 127.0.0.1
port: 5672
username: guest
password: guest
bindings:
input: #绑定的通道,这里使用的是stream提供的输入通道
destination: transfer-msg #从该目的地接受消息,上面消息中转者是向这个目的地发送消息
content-type: application/json #消息类型
创建消息消费者
//绑定输入通道
@EnableBinding(Sink.class)
public class Consumer {
/**
* 监控input通道,接收到的消息就是obj
* @param obj 接收到的消息
*/
@StreamListener(Sink.INPUT)
public void getMsg(Object obj){
System.out.println(obj);
}
}
至此,消息消费者创建完毕
下面我们分别启动这三个模块,访问http://localhost:8013/sendMsg接口,向消息发送者发送消息,可以看到中转者和消费者分别打印出消息
如果我们有多个消息消费者,而消息每次只需要被消费一次该怎么办呢?
我们可以将多个消息消费者放到同一个组中,这样他们就会互相竞争,最终只有一个消费者会接收到消息。
bindings:
input:
destination: transfer-msg
#多个消费者分到同一个组中会相互竞争
group: group
自定义消息通道
在实际生产中,我们不可能仅仅使用stream提供的三个通道,这还远远不够,因此我们要自定义消息通道,其实很简单,就是比着葫芦画瓢。
首先我们来改stream-producer-8013,这是消息发送者,我们模拟一个myOutput通道
public interface MyOutput {
String MYOUTPUT = "myOutput";
//方法名是什么无所谓
@Output("myOutput")
MessageChannel output();
}
修改消息发送者,使用我们自定义的发送通道
//绑定输出通道
@EnableBinding(MyOutput.class)
public class Producer {
@Autowired
private MyOutput myOutput;
public void sendMsg(User user){
myOutput.output().send(MessageBuilder.withPayload(user).build());
}
}
修改配置文件
spring:
cloud:
stream:
bindings:
myOutput: #绑定的通道,这里使用的是自定义的输出通道
destination: msg #发送的目的地
content-type: application/json #消息类型
接着我们修改stream-transfer-8014,这是消息中转者,我们来模拟一个MyTransfer通道
public interface MyTransfer {
String MYOUTPUT = "myOutput";
String MYINPUT = "myInput";
@Output("myOutput")
MessageChannel output();
@Input("myInput")
MessageChannel input();
}
修改消息中转者,使用我们自定义的中转通道
//绑定我们自定义的中转通道
@EnableBinding(MyTransfer.class)
public class Transfer {
/**
* 监控myInput通道,接收到的消息就是obj,最后发送加工过的消息到myOutput通道
* @param obj 接收到的消息
* @return 加工后的消息
*/
@ServiceActivator(inputChannel = MyTransfer.MYINPUT, outputChannel = MyTransfer.MYOUTPUT)
public Object getMsg(Object obj){
//这里先将消息输出,再返回加工后的消息
System.out.println(obj);
return "经过中转者加工过的消息:"+obj;
}
}
修改配置文件
spring:
cloud:
stream:
bindings:
myInput: #绑定的通道,这里使用的是我们自定义的输入通道
destination: msg #从该目的地接受消息,上面消息发送者是向这个目的地发送消息
content-type: application/json #消息类型
myOutput: #绑定的通道,这里使用的是我们自定义的输出通道
destination: transfer-msg #消息中转者等会向这个目的地发送消息
content-type: application/json #消息类型
最后我们修改stream-consumer-8015,这是消息消费者,我们模拟一个MyInput通道
public interface MyInput {
String MYINPUT = "myInput";
@Input("myInput")
MessageChannel input();
}
修改消息消费者,换成我们自定义的输入通道
//绑定我们自定义的输入通道
@EnableBinding(MyInput.class)
public class Consumer {
/**
* 监控myInput通道,接收到的消息就是obj
* @param obj 接收到的消息
*/
@StreamListener(MyInput.MYINPUT)
public void getMsg(Object obj){
System.out.println(obj);
}
}
修改配置文件
spring:
cloud:
stream:
bindings:
myInput: #绑定的通道,这里使用的是自定义的输入通道
destination: transfer-msg #从该目的地接受消息,上面消息中转者是向这个目的地发送消息
content-type: application/json #消息类型
最后启动程序,访问http://localhost:8013/sendMsg接口,向消息发送者发送消息,可以看到中转者和消费者分别打印出消息
相关阅读
项目代码
SpringCloud 汇总【Greenwich 版】
SpringCloud(一):Eureka注册中心【Greenwich 版】
SpringCloud(二):Ribbon负载均衡【Greenwich 版】
SpringCloud(三):Feign声明式服务调用【Greenwich 版】
SpringCloud(四):Hystrix熔断器介绍【Greenwich 版】
SpringCloud(五):Hystrix的请求熔断与服务降级【Greenwich 版】
SpringCloud(六):Hystrix的请求合并【Greenwich 版】
SpringCloud(七):Hystrix仪表盘与Turbine集群监控【Greenwich 版】
SpringCloud(八):Zuul网关【Greenwich 版】
SpringCloud(九):Config配置中心【Greenwich 版】
SpringCloud(十):Bus消息总线【Greenwich 版】
SpringCloud(十一):Stream消息驱动 + RabbitMQ【Greenwich 版】
SpringCloud(十二):Sleuth链路跟踪【Greenwich 版】