RabbitMQ 限流 流量削峰

以下是基于spring cloud 2.1.4+rabbitmq-server-3.8.22的实现代码


设置:

spring:
  rabbitmq:
    port: 5672
    host: localhost
    username: guest
    password: guest
    virtual-host: /
    #开启confirms这个模式
    #springboot2.2.0.RELEASE支持这个
    #publisher-confirm-type: correlated
    publisher-confirms: true
    #开启return模式
    publisher-returns: true
    listener:
      simple:
        #开启手动签收
        acknowledge-mode: manual
        #削峰
        #设置每一个消费端能够处理未确认消息的最大数,默认是250,一般用默认的就行了,这里是测试我们用1
        prefetch: 1

 消费端

package com.example.listener;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Map;

@Component
@RabbitListener(queues = "queue_test6")
public class MyRabbitListener {
    /**
     *
     * @param message 消息封装的对象,(包括了消息的序号,消息本身,消费者名称等)
     * @param channel  链接的通道
     * @param msg  消息本身
     */
    @RabbitHandler //用于处理具体类型的消息,会自动把消息转换成对应的对象
    public void receiveMessage(Message message, Channel channel,String msg){
        //接收消息
        System.out.println(msg);
        MessageProperties messageProperties = message.getMessageProperties();
        try {

            //模拟业务-100
            System.out.println("消费后减100元");
            //模拟出问题
            //int i = 10/0;
            //我们测试流量削峰,所以上面出错我就注释了
            Thread.sleep(2000);

            //如果正常就签收消息
            //参数1,消息的序号
            //参数二,是否批量签收 true是批量签收
            channel.basicAck(messageProperties.getDeliveryTag(),true);
        } catch (Exception e) {
            e.printStackTrace();
            //不正常就拒收消息(丢弃了)

            try {
                //如果该消息重回过队列就不投递了,避免死循环
                if(messageProperties.getRedelivered()){
                    System.out.println("已经重新投递过一次了");
                }else{
                    //参数1 消息序号
                    //参数2 是否批量拒绝消息
                    //参数3 是否把消息重新回到队列中
                    channel.basicNack(messageProperties.getDeliveryTag(),true,true);
                }

                //不能批量处理拒绝消息。第二个参数,true会重新放回队列,所以需要自己根据业务逻辑判断什么时候使用拒绝
                //channel.basicReject();
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }

        }



    }

    //消费者监听队列
    /*@RabbitHandler //用于处理具体类型的消息,会自动把消息转换成对应的对象
    public void receiveMessage(String msg){
        //接收消息
        System.out.println(msg);
        //模拟业务-100
    }*/

    //消费者监听队列
    @RabbitHandler //用于处理具体类型的消息,会自动把消息转换成对应的对象
    public void receiveMessage(Map msg){
        //接收消息
        System.out.println(msg);
        //模拟业务-100
    }
}

生产者

package com.example.controller;

import com.example.confirm.MyConfirmCallBack;
import com.example.confirm.MyReturnCallBack;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/testsend")
public class TestSendController {

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    //或者用这个
    //private MyConfirmCallBack myConfirmCallBack;
    private RabbitTemplate.ConfirmCallback confirmCallback;



    @Autowired
    //private MyReturnCallBack myReturnCallBack;
    //或者用这个
    private RabbitTemplate.ReturnCallback returnsCallback;

    @GetMapping ("/send1")
    public String send1(){
        //设置confirm回调函数
        rabbitTemplate.setConfirmCallback(confirmCallback);
        //设置return回调函数
        rabbitTemplate.setReturnCallback(returnsCallback);
        //发送消息
        //rabbitTemplate.convertAndSend("exchange_test6","test6.insert","消息本身");

        //测试一个错误,把交换机改正确,routingKey写错
        //rabbitTemplate.convertAndSend("exchange_test6","test6.insertxxx","消息本身");
        //测试削峰配置是否生效,把所有东西都改正确
        rabbitTemplate.convertAndSend("exchange_test6","test6.insert","消息本身");
        return "ok";
    }

}

开启服务测试,发现是每隔两秒执行一个。

这个时候我们把配置改为最大处理消息数为3,你在测试发现还是一个一个消费。原因是这是单一线程的,我们需要在消费者注解加上concurrency属性并且设置3@RabbitListener(queues = "queue_test6",concurrency = "3")

 再次测试你就会发现是3个3个的消费的了。concurrency单词的意思:并发性,在程序中相当于开了多个线程。

package com.example.listener;

import com.rabbitmq.client.Channel;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.Map;

@Component
@RabbitListener(queues = "queue_test6",concurrency = "3")
public class MyRabbitListener {
    /**
     *
     * @param message 消息封装的对象,(包括了消息的序号,消息本身,消费者名称等)
     * @param channel  链接的通道
     * @param msg  消息本身
     */
    @RabbitHandler //用于处理具体类型的消息,会自动把消息转换成对应的对象
    public void receiveMessage(Message message, Channel channel,String msg){
        //接收消息
        System.out.println(msg);
        MessageProperties messageProperties = message.getMessageProperties();
        try {

            //模拟业务-100
            System.out.println("消费后减100元");
            //模拟出问题
            //int i = 10/0;
            //我们测试流量削峰,所以上面出错我就注释了
            Thread.sleep(2000);

            //如果正常就签收消息
            //参数1,消息的序号
            //参数二,是否批量签收 true是批量签收
            channel.basicAck(messageProperties.getDeliveryTag(),true);
        } catch (Exception e) {
            e.printStackTrace();
            //不正常就拒收消息(丢弃了)

            try {
                //如果该消息重回过队列就不投递了,避免死循环
                if(messageProperties.getRedelivered()){
                    System.out.println("已经重新投递过一次了");
                }else{
                    //参数1 消息序号
                    //参数2 是否批量拒绝消息
                    //参数3 是否把消息重新回到队列中
                    channel.basicNack(messageProperties.getDeliveryTag(),true,true);
                }

                //不能批量处理拒绝消息。第二个参数,true会重新放回队列,所以需要自己根据业务逻辑判断什么时候使用拒绝
                //channel.basicReject();
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }

        }



    }

    //消费者监听队列
    /*@RabbitHandler //用于处理具体类型的消息,会自动把消息转换成对应的对象
    public void receiveMessage(String msg){
        //接收消息
        System.out.println(msg);
        //模拟业务-100
    }*/

    //消费者监听队列
    @RabbitHandler //用于处理具体类型的消息,会自动把消息转换成对应的对象
    public void receiveMessage(Map msg){
        //接收消息
        System.out.println(msg);
        //模拟业务-100
    }
}

posted @ 2021-09-07 17:06  在线电影制作人  阅读(4)  评论(0)    收藏  举报  来源