Spring Cloud Stream官方的说法是一个构建消息驱动微服务的框架。我们可以这么理解,这个Spring Cloud Stream封装了mq的玩法,统一了模型,然后屏蔽各个mq产品中间件不同,降低了我们的学习成本,不过目前只支持kafka与rabbitmq。我们的应用程序,也就是spring Application ,通过这个input 与output 这两种channel 与binder 进行交互,binder(绑定器对象)屏蔽了咱们的消息中间件产品的差异, 封装了消息中间件,可以很方便的链接中间件
我们可以看下这个Spring Cloud Stream的架构图
创建Stream-Service
引入依赖
org.springframework.cloud
spring-cloud-stream
org.springframework.cloud
spring-cloud-starter-stream-rabbit
org.springframework.cloud
spring-cloud-stream-binder-rabbit
server:
port: 7001 #服务端口
spring:
application:
name: stream_service #指定服务名
rabbitmq:
addresses: localhost
username: guest
password: guest
port: 5672
cloud:
stream:
bindings:
output:
destination: yuan11-topic #指定消息发送的目的地,在rabbitmq中,发送到一个yuan11-topic的exchange中
binders: #配置绑定器
defaultRabbit:
type: rabbit # 绑定的是rabbit
/**
* @title: HelloController
* @Author yuan11
* @Date: 2022/4/27 1:09
* @Version 1.0
*/
@RestController
// 绑定controller 为消息发送者 output
@EnableBinding(Source.class)
public class HelloController {
// 注入stream 内置的消息通道
@Autowired
private MessageChannel output;
@GetMapping("/hello")
public void hello(){
// 通道发送消息
output.send(MessageBuilder.withPayload("hello stream message").build());
}
}
public interface Source {
String OUTPUT = "output";
@Output("output")
MessageChannel output();
}
org.springframework.boot
spring-boot-starter-web
org.springframework.cloud
spring-cloud-stream
org.springframework.cloud
spring-cloud-stream-binder-rabbit
org.springframework.cloud
spring-cloud-starter-stream-rabbit
server:
port: 7002 #服务端口
spring:
application:
name: stream-client #指定服务名
rabbitmq:
addresses: localhost
username: guest
password: guest
cloud:
stream:
bindings:
input: #内置的获取消息的通道 , 从yuan11-topic中获取消息 ,注意消费者这里是input
destination: yuan11-topic
binders:
defaultRabbit:
type: rabbit
/**
* @title: MessageReceiver
* @Author yuan11
* @Date: 2022/4/28 0:06
* @Version 1.0
*/
// 绑定消费者
@EnableBinding(Sink.class)
public class MessageReceiver {
// 消费通道监听器
@StreamListener(Sink.INPUT)
public void input(String message){
System.out.println("获取到的消息是:"+message);
}
}
public interface Sink {
String INPUT = "input";
@Input("input")
SubscribableChannel input();
}
上面是默认用的stream 内置的消息通道,下面我们使用自己定义的消费通道
/**
* @title: MyChannel
* @Author yuan11
* @Date: 2022/4/28 0:14
* @Version 1.0
*/
public interface MyChannel {
/**
* 消息生产者的配置
*/
String MYOUTPUT="myoutput";
@Output(MYOUTPUT)
MessageChannel myoutput();
}
/**
* @title: HelloController
* @Author yuan11
* @Date: 2022/4/27 1:09
* @Version 1.0
*/
@RestController
// 绑定controller 为消息发送者 output
//@EnableBinding(Source.class)
@EnableBinding(MyChannel.class)
public class HelloController {
// 注入stream 内置的消息通道
// @Autowired
// private MessageChannel output;
// 这里注入自定义的channel
@Autowired
private MyChannel myChannel;
@GetMapping("/hello")
public void hello(){
// 通道发送消息
//output.send(MessageBuilder.withPayload("hello stream message").build());
// 自定义的通道发送消息
myChannel.myoutput().send(MessageBuilder.withPayload("hello stream myouput message").build());
}
}
server:
port: 7001 #服务端口
spring:
application:
name: stream_service #指定服务名
rabbitmq:
addresses: localhost
username: guest
password: guest
port: 5672
cloud:
stream:
bindings:
output:
destination: yuan11-topic #指定消息发送的目的地,在rabbitmq中,发送到一个yuan11-topic的exchange中
myoutput: # 自定义的通道也就是发送者
destination: yuan11-custom-output #规定发送给哪个通道 即接收者
binders: #配置绑定器
defaultRabbit:
type: rabbit # 绑定的是rabbit
// 绑定消费者
//@EnableBinding(Sink.class)
@EnableBinding(MyChannel.class)
public class MessageReceiver {
// 消费通道监听器
// @StreamListener(Sink.INPUT)
@StreamListener(MyChannel.MYINPUT)
public void input(String message){
System.out.println("获取到的消息是:"+message);
}
}
// 绑定消费者
//@EnableBinding(Sink.class)
@EnableBinding(MyChannel.class)
public class MessageReceiver {
// 消费通道监听器
// @StreamListener(Sink.INPUT)
@StreamListener(MyChannel.MYINPUT)
public void input(String message){
System.out.println("获取到的消息是:"+message);
}
}
server:
port: 7002 #服务端口
spring:
application:
name: stream-client #指定服务名
rabbitmq:
addresses: localhost
username: guest
password: guest
cloud:
stream:
bindings:
input: #内置的获取消息的通道 , 从yuan11-topic中获取消息 ,注意消费者这里是input
destination: yuan11-topic
myinput: # 自定义消费者通道
destination: yuan11-custom-output
binders:
defaultRabbit:
type: rabbit
通常在生产环境,我们的每个服务都不会以单节点的方式运行在生产环境,当同一个服务启动多个实例的时候,这些实例都会绑定到同一个消息通道的目标主题(Topic)上。默认情况下,当生产者发出一条消息到绑定通道上,这条消息会产生多个副本被每个消费者实例接收和处理,但是有些业务场景之下,我们希望生产者产生的消息只被其中一个实例消费,这个时候我们需要为这些消费者设置消费组来实现这样的功能
cloud:
stream:
bindings:
input: #内置的获取消息的通道 , 从yuan11-topic中获取消息 ,注意消费者这里是input
destination: yuan11-topic
myinput: # 自定义消费者通道
destination: yuan11-custom-output
group: group1 # #设置消.息的组名称(同名组中的多个消费者,只会有一个去消费消息.)
stream:
bindings:
output:
destination: yuan11-topic #指定消息发送的目的地,在rabbitmq中,发送到一个yuan11-topic的exchange中
myoutput: # 自定义的通道也就是发送者
destination: yuan11-custom-output #规定发送给哪个通道 即接收者
group: group1 #设置消.息的组名称(同名组中的多个消费者,只会有一个去消费消息.)
有一些场景需要满足, 同一个特征的数据被同一个实例消费, 比如同一个id的传感器监测数据必须被同一个实例统计计算分析, 否则可能无法获取全部的数据。又比如部分异步任务,首次请求启动task,二次请求取消task,此场景就必须保证两次请求至同一实例
消息生产者
myoutput: # 自定义的通道也就是发送者
destination: yuan11-custom-output #规定发送给哪个通道 即接收者
group: group1 #设置消.息的组名称(同名组中的多个消费者,只会有一个去消费消息.)
producer:
partition-key-expression: payload #分区关键字 对象中的id,对象
partition-count: 2 #分区大小
cloud:
stream:
bindings:
input: #内置的获取消息的通道 , 从yuan11-topic中获取消息 ,注意消费者这里是input
destination: yuan11-topic
myinput: # 自定义消费者通道
destination: yuan11-custom-output
group: group1 # #设置消.息的组名称(同名组中的多个消费者,只会有一个去消费消息.)
consumer:
partitioned: true #开启分区支持
binders:
defaultRabbit:
type: rabbit
instance-count: 2 # 实例个数
instance-index: 0 # 当前实例的编号