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
}
}