1.Stream简介
Spring Cloud Steam是一个用来为微服务应用构建消息驱动能力的框架,它可以基于Spring Boot创建独立的可用于生产的Spring应用程序。它通过使用Spring Integration来连接消息代理中间件实现消息事件驱动。
上图为官方提供的Spring Cloud Stream运行图,图中定义了inputs、outputs分别表示消息的输入通道和输出通道。
Binder:为绑定器用于消息交换,Binder起承上启下作用,通过Binder我们的应用程序就不需要直接与下层消息中间件打交通,所有的封装空全交给Binder,我们只需关心业务逻辑即可
Middleware:表示具体的消息中间件,可以是Rabbitmq或Kafka(目前只支持这两种)
2.入门案例
服务 | 端口 | 作用 |
---|---|---|
consul | 8500 | 注册中心 |
spring-cloud-stream-provider | 8888 | 消息发送端 |
spring-cloud-stream-consumer | 8889 | 消息接收端 |
-
创建
spring-cloud-stream-provider
服务 -
引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency>
-
书写发送端binder
public interface MySource { String OUT_PUT = "send_exchange"; /** * @return output channel */ @Output(OUT_PUT) MessageChannel sendMsg(); }
-
书写发送消息逻辑
@Service @EnableBinding(MySource.class) public class SenderServiceImpl implements SenderService { @Resource private MySource sendChannel; @Override public void sendMsg(String msg) { sendChannel.sendMsg().send(MessageBuilder.withPayload(msg).build()); } }
(1)通过
@EnableBinding
绑定消息通道,即上述所说的定义绑定器(2)通过
MessageBuilder
定义发送消息内容(3)调用通道的
send()
方法发送消息 -
书写配置文件
application.yml
server: port: 8888 spring: rabbitmq: host: 192.168.1.100 port: 5672 username: admin password: admin virtual-host: /study-rabbitmq cloud: stream: bindings: send_exchange: destination: test-exchange
(1)spring.rabbitmq:表示配置rabbitmq基本信息,如果不配置,则使用默认配置。默认配置如下:
spring.rabbitmq.host:localhost spring.rabbitmq.port:5672 spring.rabbitmq.username:guest spring.rabbitmq.password:guest
(2)spring.cloud.stream:配置具体的消息通道信息
send_exchange
:表示我们的绑定器中设置的输出通道destination
:表示消息具体发送到的交换机,对应rabbitmq中的exchange完成上述步骤,则我们的消息发送端已经书写完毕,下边继续完成消息接收端服务的书写。
-
创建服务消费者
-
引入依赖
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency>
-
书写接收端binder
public interface MySkin { String INPUT = "send_exchange"; /** * @return output channel */ @Input(INPUT) SubscribableChannel receiveMsg(); }
-
书写接收端
application.yml
server: port: 8889 spring: rabbitmq: host: 192.168.1.100 port: 5672 username: admin password: adm过in virtual-host: /study-rabbitmq cloud: stream: bindings: send_exchange: destination: test-exchange
配置与发送端相同,完成上述配置,启动两个服务,发送端发送消息,接收端能接收到消息,表示通过Spring Cloud Stream实现消息发送与接收案例已经完成。
3.基本概念
通过上述入门案例,我们对Spring Cloud Stream有了初步的认识,下边具体讲解一下Stream其它基本概念。
-
消费组
简单理解消费者,见名知意就是将服务进行分组。分组后,在同一组的服务实例只会有一个服务实例可以接收到消息,通过在消费端配置如下配置进行消息分组。
spring.cloud.bindings.send_exchange.group=group-a
通过该配置,则所有起动的
group-a
分组的服务实例中只有一个服务实例可以获取到消息。当启动多个服务分组时,每个服务分组中只会有一个服务实例可以获取到消息,通过此即完成了消息分组。 -
消息分区
可以通过设置分区,让每一次服务消费都为指定分区服务消费,配置如下
(1)服务提供方
spring.cloud.bindings.send_exchange.producer.partitionCount:2 spring.cloud.bindings.send_exchange.producer.partitionKeyExpression:headers['partitionKey']
partitionCount:表示分区总数
partitionKeyExpression:表示设置分区表达式,通过表达式进行设置值,在此通过headers设置值,则在发送消息时,对headers进行设置
@GetMapping("/sendMessage") public void sendMessage(@RequestParam("msg") String msg) { System.out.println("发送消息:"+msg); mySource.sendMsg().send(MessageBuilder.withPayload(msg).setHeader("partitionKey",1).build()); }
通过上述设置,则只有设置分区为1的服务实例能收到消息。
(2)服务消费方
spring.cloud.bindings.send_exchange.consumer.partitioned:true spring.cloud.bindings.send_exchange.consumer.instanceCount:2 spring.cloud.bindings.send_exchange.consumer.instanceIndex:1
partitioned=true:表示开启分区
instanceCount:表示服务分区总数
instanceIndex:表示当前服务分区值,从0开始,-1表示所有分区
-
开启绑定功能
通过
@EnableBinding
注解来绑定消息通道,查看其注解详情如下:@Target({ ElementType.TYPE, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Configuration @Import({ BindingBeansRegistrar.class, BinderFactoryConfiguration.class }) @EnableIntegration public @interface EnableBinding { Class<?>[] value() default {}; }
通过上边可以看出该注解就是一个Spring boot的配置,
BindingBeansRegistrar
作用即扫描@EnableBinding
配置的value值,然后将其加入Spring容器的beanMap中。因此我们在自定义channel时,未将自定义的接口加入spring中,但是依然能使用。 -
绑定消息通道
Stream默认定义了三个消息通道,分别时
Sink
、Source
、Processor
,三个消息通道源码如下:public interface Sink { /** * Input channel name. */ String INPUT = "input"; /** * @return input channel. */ @Input(Sink.INPUT) SubscribableChannel input(); } public interface Source { /** * Name of the output channel. */ String OUTPUT = "output"; /** * @return output channel */ @Output(Source.OUTPUT) MessageChannel output(); } public interface Processor extends Source, Sink { }
三个消息通道通过
@Input
、@Output
来定义是消息输入还是输出,只需在配置中定义好输入、输出消息目的地即可实现消息的传递。 -
注入消息
在定义消息通道后,我们可以将消息通道中的消息注入到需使用的地方,有两种方式,可以直接接口,通过接口调用。也可以通过别名方式注入,分别用法如下。
(1)定义通道信息
public interface MySource { String OUT_PUT = "send_exchange"; @Output(OUT_PUT) MessageChannel sendMsg(); String OUT_PUT1 = "send_1"; @Output(OUT_PUT1) MessageChannel sendMsg1(); }
(2)使用方式一
@Service @EnableBinding({MySource.class, Processor.class}) public class SenderServiceImpl implements SenderService { @Resource private MySource sendChannel; @Override public void sendMsg1(String msg) { sendChannel.sendMsg().send(MessageBuilder.withPayload(msg).setHeader("partitionKey",1).build()); } @Override public void sendMsg2(String msg) { sendChannel.sendMsg1().send(MessageBuilder.withPayload(msg).setHeader("partitionKey",1).build()); }
(3)使用方式二
@Service @EnableBinding({MySource.class, Processor.class}) public class SenderServiceImpl implements SenderService { @Qualifier("send_exchange") private MessageChannel output1; @Qualifier("send_1") private MessageChannel output2; @Override public void sendMsg(String msg) { output1.send(MessageBuilder.withPayload(msg).setHeader("partitionKey",1).build()); } @Override public void sendMsg2(String msg) { output2.send(MessageBuilder.withPayload(msg).setHeader("partitionKey",1).build()); }
7.参考资料
- spring cloud stream文档链接
- 《Springcloud微服务实战》