【开发技巧/经验分享】使用SpringCloudStream操作RabbitMQ(内附重复消费的情况,并有相关解决方案)

1. 什么是SpringCloudStream?

SpringCloudStream是SpringCloud中对于消息中间件操作的封装包,目前支持的消息中间件有RabbitMQ、Kafka,以下是SpringCloudStream的模型图。

【开发技巧/经验分享】使用SpringCloudStream操作RabbitMQ(内附重复消费的情况,并有相关解决方案)_第1张图片
从上图可以发现消息的发送由output接口提供,消息的接收由input输入。应用程序与消息中间件通过Binder进行链接。

2. 为什么要使用SpringCloudStream?

SpringCloudStream是对消息中间件操作的再次封装,简化了消息中间件的细节,它可以使得我们对消息中间间无感知,使我们能够更加专注业务代码的开发。

3. 如何使用SpringCloudStream?

单个实例
3.1 在工程中引入相关Maven依赖

<dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-stream-rabbitartifactId>
dependency>
3.2 编写相关配置信息
spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
  cloud:
    stream:
      bindings:
          input:
            destination: myMsg
          output:  
            destination: myMsg
3.2 编写一个StreamClient接口,并在里边声明消息输入、输出方法。
package com.qingyun.order.server.message;


import org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;

public interface StreamClient {
    
    static String INPUT="input";
    
    static String OUTPUT="output";

    @Input(StreamClient.INPUT)
    SubscribableChannel input();

    @Output(StreamClient.OUTPUT)
    MessageChannel output();
}
3.3 编写一个消息接收类,用于接收信息
package com.qingyun.order.server.message;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.stereotype.Component;

/**
 * Created with IntelliJ IDEA.
 * User: 李敷斌.
 * Date: 2020-02-16
 * Time: 18:16
 * Explain:
 */
@EnableBinding(StreamClient.class)
@Component
@Slf4j
public class StreamReceiver {

    @StreamListener(StreamClient.INPUT)
    public void process(Object msg){
      log.info("StreamReceiver msg={}",msg);
    }
}
3.4 编写一个请求处理器方便测试代码
package com.qingyun.order.server.controller;

import com.qingyun.order.server.message.StreamClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Date;

/**
 * Created with IntelliJ IDEA.
 * User: 李敷斌.
 * Date: 2020-02-16
 * Time: 18:17
 * Explain:
 */
@RestController
public class SendMsgController {

    @Autowired
    private StreamClient streamClient;

    @GetMapping(value = "sendMsg")
    public void sendStreamMsg(){

        String msg="now time="+new Date();

        streamClient.output().send(MessageBuilder.withPayload(msg).build());
    }
}
3.5 运行一个应用实例,并查看RabbitMQ中的队列情况,并且还要发起一次测试

【开发技巧/经验分享】使用SpringCloudStream操作RabbitMQ(内附重复消费的情况,并有相关解决方案)_第2张图片
可以发现消息中间件中的队列创建成功,这时我们发起一次测试

【开发技巧/经验分享】使用SpringCloudStream操作RabbitMQ(内附重复消费的情况,并有相关解决方案)_第3张图片
消息正常传达,那么这个时候我们就可以开启多个实例进行测试了。

多实例
3.6 运行多个实例进行测试

【开发技巧/经验分享】使用SpringCloudStream操作RabbitMQ(内附重复消费的情况,并有相关解决方案)_第4张图片
查看RabbitMQ的控制面板可以发现此时有两个myMsg开头的队列,这个时候我们发起一次测试

  • 实例一
    【开发技巧/经验分享】使用SpringCloudStream操作RabbitMQ(内附重复消费的情况,并有相关解决方案)_第5张图片
  • 实例二
    在这里插入图片描述
    从上两图中我们可以发现两个实例都接收到了我们的消息,这根本不符合我们的要求,消息竟然被重复消费了,原因就是在有两个myMsg开头的实例。要解决这个问题我们可以添加一个属性spring.cloud.stream.bindings.名称.group属性。
3.7 修改application.yml配置文件
spring:
  cloud:
    stream:
      bindings:
          input:
            destination: myMsg
            group: order-service
          output:  
            destination: myMsg
            group: order-service
3.8 重启两个实例,并查看RabbitMQ控制面板

【开发技巧/经验分享】使用SpringCloudStream操作RabbitMQ(内附重复消费的情况,并有相关解决方案)_第6张图片
从上图可以很清楚的发现,之前的随机字符串被替换成了我们添加的group属性值,并且两个实例都与它有关,这个时候我们发起一次测试,看看消息是否还会被重复消费。

  • 实例一
    【开发技巧/经验分享】使用SpringCloudStream操作RabbitMQ(内附重复消费的情况,并有相关解决方案)_第7张图片
  • 实例二
    【开发技巧/经验分享】使用SpringCloudStream操作RabbitMQ(内附重复消费的情况,并有相关解决方案)_第8张图片
    可以发现此时没有重复消费的现象

4. 消息接收完毕后发送一条签收信息

4.1 在工程中新建一个SteamClient以及消息接收处理类

StreamClient2

package com.qingyun.order.server.message;


import org.springframework.cloud.stream.annotation.Input;
import org.springframework.cloud.stream.annotation.Output;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.SubscribableChannel;

public interface StreamClient2 {

    static String INPUT="input2";

    static String OUTPUT="output2";

    @Input(StreamClient2.INPUT)
    SubscribableChannel input();

    @Output(StreamClient2.OUTPUT)
    MessageChannel output();
}

消息接收器

@EnableBinding(StreamClient2.class)
@Component
@Slf4j
public class StreamReceiver2 {

    @StreamListener(StreamClient2.INPUT)
    public void process(Object msg){
      log.info("StreamReceiver2 msg={}",msg);
    }
}
4.2 修改配置文件application.yml
spring:
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
  cloud:
    stream:
      bindings:
          input:
            destination: myMsg
            group: order-service
          output:  
            destination: myMsg
            group: order-service
          input2:
            destination: myMsg2
            group: order-service
          output2:  
            destination: myMsg2
            group: order-service
4.3 修改第一个消息接收器
package com.qingyun.order.server.message;

import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Component;

/**
 * Created with IntelliJ IDEA.
 * User: 李敷斌.
 * Date: 2020-02-16
 * Time: 18:16
 * Explain:
 */
@EnableBinding(StreamClient.class)
@Component
@Slf4j
public class StreamReceiver {

    @StreamListener(StreamClient.INPUT)
    @SendTo(StreamClient2.INPUT)
    public boolean process(Object msg){
      log.info("StreamReceiver msg={}",msg);

      return true;
    }
}

这里需要注意的是返回值只能是 true、false、null三者,否则将会引发程序运行时异常。

4.4 重启应用程序,并发起测试请求

【开发技巧/经验分享】使用SpringCloudStream操作RabbitMQ(内附重复消费的情况,并有相关解决方案)_第9张图片
从上图可以看出消息正常发出,并正确返回了签收信息

你可能感兴趣的:(开发技巧,SpringCloud,SpringBoot)