简介
构建消息驱动微服务的框架,应用通过inputs或者outputs来和stream的binder对象交互,通过配置来binding绑定,binder对象负责和消息中间件交互。总之,stream是一种屏蔽底层消息中间件的差异,降低切换版本,统一消息的编程模型。
设计思想
- 标准MQ:
- Message:生产者/消费者之间靠消息媒介传递信息内容;
- MessageChannel消息通道:消息必须走特定的通道;
- 消息通道的子接口SubscribableChannel,由MessageHandler消息处理器订阅。
通过定义绑定器Binder作为中间层,实现了应用程序与消息中间件之间的隔离。遵循了发布-订阅模式,用Topic主题进行广播,在RabbitMQ就是Exchange,在kafka中就是Topic。
- 组件
- Binder:连接中间件,屏蔽差异
- Channel:通道,是Queue的一种抽象,在消息通讯系统中就是实现存储和转发的媒介,由它对队列进行配置
- Source和Sink:从stream发布消息就是输出,接受消息就是输入。
常用API和注解
-
Middleware:中间件
-
Binder:应用和消息中间件之间的封装,可以方便连接中间件,动态地改变消息类型,通过配置文件实现。
-
@Input:标识输入通道,接受消息进入应用程序
-
@Output:标识输出通道,消息由此离开应用程序
-
@StreamListener:监听队列,用于消费者的队列的消息接收
-
@EnableBinding:指信道Channel和Exchange绑定在一起
生产者配置
- yml:
spring:
application:
name: cloud-stream-provider
cloud:
stream:
binders: # 在此处配置要绑定的rabbitmq的服务信息;
defaultRabbit: # 表示定义的名称,用于于binding整合
type: rabbit # 消息组件类型
environment: # 设置rabbitmq的相关的环境配置
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
bindings: # 服务的整合处理
output: # 这个名字是一个通道的名称
destination: studyExchange # 表示要使用的Exchange名称定义
content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
binder: defaultRabbit # 设置要绑定的消息服务的具体设置
- 业务类实现IMessageProvider接口,注入MessageChannel对象,加**@EnableBinding(Source.class)**
public interface IMessageProvider
{
public String send();
}
@Override
public String send() {
String serial = UUID.randomUUID().toString(); output.send(MessageBuilder.withPayload(serial).build()); System.out.println("*****serial: "+serial);
return null;
}
- controller注入重写的业务类
@GetMapping(value = "/sendMessage")
public String sendMessage()
{
return messageProvider.send();
}
消费者配置
- yml:
spring:
application:
name: cloud-stream-consumer
cloud:
stream:
binders: # 在此处配置要绑定的rabbitmq的服务信息;
defaultRabbit: # 表示定义的名称,用于于binding整合
type: rabbit # 消息组件类型
environment: # 设置rabbitmq的相关的环境配置
spring:
rabbitmq:
host: localhost
port: 5672
username: guest
password: guest
bindings: # 服务的整合处理
input: # 这个名字是一个通道的名称
destination: studyExchange # 表示要使用的Exchange名称定义
content-type: application/json # 设置消息类型,本次为json,文本则设置“text/plain”
binder: defaultRabbit # 设置要绑定的消息服务的具体设置
- controller加**@Enablebinding(Sink.class)和@Component**,具体的方法上加**@StreamListener(Sink.INPUT)**
@StreamListener(Sink.INPUT)
public void input(Message<String> message) {
System.out.println("消费者1号,接受:"+message.getPayload()+"\t port:"+serverPort);
}
分组消费
默认会将不同消费者放入不同分组,而将微服务放入同一个group才会保证消息只会被其中一个应用消费一次。不同的组是可以消费的,同一个组内会发生竞争关系,只有其中一个可以消费。
*****在yml的bindings的input下新加group:XXX即可指定分组为XXX
持久化
分组同样解决了持久化问题
指定了分组的微服务可以读取持久化的数据。