RabbitMQ:消费端限流&&消费端ACK与重回队列
一.消费端限流场景
如果RabbitMQ服务上堆积了成千上万条未处理的消息,然后随便打开一个消费者客户端,巨量的消息瞬间被推送过来,但是单个客户端无法同时处理这么多消息,可能会导致服务器宕机,产生线上故障。
所以RabbitMQ提供了一种qos功能(服务质量保证),即在非自动确认消息的前提下,如果一定数目的消息(通过基于consume或者channel设置Qos的值)未被确认前,不进行消费新的消息。
二.BasicQos方法
void BasicQos(int prefetchSize,int prefetchCount,boolean global)
prefetchSize:消费端一般设置为0
prefetchCount:消费者同时接收消息的个数
global:true/false 是否将上面的设置应用于channel级别(是channel级别还是consumer级别)
prefetchCount和global这两项,rabbitmq没有实现,即在自动应答情况下这两个值是不生效的。
消费端
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public class Consumer { public static void main(String[] args) throws IOException, TimeoutException, InterruptedException { //创建一个连接工厂 ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.setHost( "192.168.10.132" ); connectionFactory.setPort( 5672 ); connectionFactory.setVirtualHost( "/" ); //创建连接 Connection connection = connectionFactory.newConnection(); //通过连接创建一个Channel Channel channel = connection.createChannel(); //创建一个队列 String queueName = "qos" ; channel.queueDeclare(queueName, true , false , false , null ); //限流策略,第一件事要设置autoAck为false,即下面basicConsume方法的第二个参数 channel.basicQos( 0 , 1 , false ); //设置Channel channel.basicConsume(queueName, false , new MyConsumer(channel)); } }自定义consumer: |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public class MyConsumer extends DefaultConsumer { private Channel channel; public MyConsumer(Channel channel) { super (channel); this .channel = channel; } @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte [] body) throws IOException { System.out.println(consumerTag); System.out.println(envelope); System.out.println(properties); System.out.println( new String(body)); channel.basicAck(envelope.getDeliveryTag(), false ); } } |
二.消费端ACK与重回队列
消费端ACK使用场景:
1.消费端进行消费的时候,如果由于业务异常我们可以进行日志记录,然后进行补偿。
2.由于服务器宕机等严重问题,那我们就需要手工进行ACK保障消费端消费成功。
生产端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | public static void main(String[] args) throws IOException, TimeoutException { //创建一个连接工厂 ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.setHost( "192.168.10.132" ); connectionFactory.setPort( 5672 ); connectionFactory.setVirtualHost( "/" ); //创建连接 Connection connection = connectionFactory.newConnection(); //通过连接创建一个Channel Channel channel = connection.createChannel(); //通过Channel发送数据 // 在这里要设置Mandatory(第三个参数)为true,否则broker会自动删除消息 for ( int i= 0 ;i< 10 ;i++){ Map<String ,Object> hearders = new HashMap<>(); hearders.put( "ack" , "ok" +i); AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder() .deliveryMode( 2 ) .contentEncoding( "UTF-8" ) .headers(hearders) .build(); channel.basicPublish( "" , "ack" ,properties, "hello world" .getBytes()); } channel.close(); connection.close(); } |
消费端:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | public static void main(String[] args) throws IOException, TimeoutException, InterruptedException { //创建一个连接工厂 ConnectionFactory connectionFactory = new ConnectionFactory(); connectionFactory.setHost( "192.168.10.132" ); connectionFactory.setPort( 5672 ); connectionFactory.setVirtualHost( "/" ); //创建连接 Connection connection = connectionFactory.newConnection(); //通过连接创建一个Channel Channel channel = connection.createChannel(); //创建一个队列 String queueName = "ack" ; channel.queueDeclare(queueName, true , false , false , null ); //设置Channel DefaultConsumer consumer = new DefaultConsumer(channel){ @Override public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte [] body) throws IOException { System.out.println( new String(body)); Object ack = properties.getHeaders().get( "ack" ); System.out.println(ack.toString()); try { Thread.sleep( 2000 ); } catch (InterruptedException e) { e.printStackTrace(); } if ( "ok0" .equals(ack.toString())){ //表示消息处理失败了,设置重回队列,Broker端就会将没有成功处理的消息重新发送,并且位于队列底端。 //参数3:requeue 是否重回队列(实际生产会设置false) channel.basicNack(envelope.getDeliveryTag(), false , true ); } else { channel.basicAck(envelope.getDeliveryTag(), false ); } } }; //手工签收,必须关闭autoAck(false) channel.basicConsume(queueName, false ,consumer); } |
打印结果:
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix