1、首先添加引用
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> <version>2.2.1.RELEASE</version> </dependency>
2、先写一个简单的消息接收的例子,定义消息的输入输出通道
public interface StreamClient { //定义输入和输出通道 String INPUT = "myInput"; String OUTPUT = "myOutput"; //如果Input注解无参数,通道的名称就是注解的方法的名称 @Input(StreamClient.INPUT) SubscribableChannel input(); @Output(StreamClient.OUTPUT) MessageChannel output(); }
3、定义消息的侦听方法
//@EnableBinding 注解用来指定一个或多个定义了 @Input 或 @Output 注解的接口,以此实现对消息通道(Channel)的绑定 //@EnableBinding(value={Sink.class,Source.class}) @EnableBinding(StreamClient.class) public class StreamReceiver { @StreamListener(StreamClient.INPUT) public void receive(String message) { System.out.println("接受到消息:" + message); } }
4、配置文件配置信息
server: port: 8090 spring: application: name: spring-cloud-stream rabbitmq: host: 192.168.1.1 port: 5672 username: sa password: 123456 cloud: stream: bindings: #指定输入输出通道对应的主题名 myInput: destination: hello myOutput: destination: hello
5、向这个通道里面添加消息测试看看效果
@RestController public class HomeController { @Autowired private StreamClient streamClient; @GetMapping("/index") public void index() { //向被监听的消息通道发送消息 streamClient.output().send(MessageBuilder.withPayload("发送一条测试消息").build()); } }
运行起来就看以看到消息了。但是你运行两个程序实例的时候会发现,两个接收者都接受到这条消息了。
我运行两个实例就启动两个队列,都属于交换机“hello”的。AD表示自动删除,Excl表示排他队列(排他队列,只有创建它的连接有权访问,连接断开后,排他队列将自动删除。这种队列适用于一个队列仅对应一个客户端收发消息的场景),ML好像是设置队列master的策略用的(https://blog.bossma.cn/rabbitmq/highly-available-mirrored-queues/),
可以看到Routing key是“#”,表示任意位数符。
为了不让消息重复消费就又一个“消费组”的概念,我们就来设置下消费组。配置文件添加group: world设置即可,运行两个实例,就会发现队列只有一个,两个程序监听这一个队列,发送消息,也确实是每次只有一个程序能输出信息。
server: port: 8091 spring: application: name: spring-cloud-stream rabbitmq: host: 192.168.1.1 port: 5672 username: sa password: 123456 cloud: stream: bindings: #指定输入输出通道对应的主题名 myInput: destination: hello #指定该应用实例属于world消费组 group: world myOutput: destination: hello
还有一个“消息分区”的概念,重点是partitionKeyExpression配置信息,这个配置信息配置后,会进行计算,具体怎么计算我暂时也不知,如果是消息1就会路由到一个分区,如果是2路由到另一个分区。感觉像是取模的样子,发送消息a和消息b也不在一个分区,还没找到具体的计算方式。。。。找到再来补充。。。
注意,配置的缩进千万别搞错了。。。
server: port: 8090 spring: application: name: spring-cloud-stream rabbitmq: host: 192.168.1.1 port: 5672 username: sa password: 123456 cloud: stream: bindings: myInput: #指定输入通道对应的主题名 destination: hello #指定该应用实例属于world消费组 group: world consumer: #通过该参数开启消费者分区功能 partitioned: true myOutput: destination: hello producer: #通过该参数指定了分区键的表达式规则,可以根据实际的输出消息规则配置 SpEL 来生成合适的分区键 partitionKeyExpression: payload partitionCount: 2 #该参数指定了当前消费者的总实例数量 instance-count: 2 #该参数设置了当前实例的索引号,从 0 开始,最大值为 spring.cloud.stream.instance-count 参数 - 1 instance-index: 1
异常重试,默认情况下,代码出现异常,消费者会进行3次重试,3次后依旧失败则抛出异常。可以设置重试次数max-attempts。
bindings: myInput: #指定输入通道对应的主题名 destination: hello #指定该应用实例属于world消费组 group: world consumer: #设置重试次数,默认3次,重试时间貌似指数增长 max-attempts: 3 myOutput: destination: hello
异常可以自定义一个异常方法来接收信息参考:http://blog.didispace.com/spring-cloud-starter-finchley-7-3/
推荐使用异常情况将队列数据传入死信队列中,注意每次实验前将之前队列以及交换机删除掉,否则会出现异常。配置信息如下
cloud: stream: bindings: myInput: #指定输入通道对应的主题名 destination: hello #指定该应用实例属于world消费组 group: world consumer: #设置重试次数,默认3次,重试时间貌似指数增长 max-attempts: 3 myOutput: destination: hello rabbit: bindings: myInput: consumer: #开启死信队列 auto-bind-dlq: true #超过时间自动从DLQ中移除,单位毫秒 dlq-ttl: 10000 #会在headers中加入错误信息 republish-to-dlq: true
提示:如果死信队列中设置headers加入错误信息,就可以获取headers获取指定错误做出指定数据的处理。
此外还有一种配置,指明出现异常,将队列数据重新入队列,而不是将数据丢弃。
consumer: requeue-rejected: true
死信队列:http://blog.didispace.com/spring-cloud-starter-finchley-7-4/