RabbitMQ高级应用

 

高级应用一: 手动模式和自动应答模式

 1. 了确保消息不会丢失,RabbitMQ支持消息应答。消费者发送一个消息应答,告诉RabbitMQ这个消息已经接收并且处理完毕了。RabbitMQ就可以删除它了。
   2. 如果一个消费者挂掉却没有发送应答,RabbitMQ会理解为这个消息没有处理完全,然后交给另一个消费者去重新处理。这样,你就可以确认即使消费者偶尔挂掉也不会丢失任何消息了。
   3. 没有任何消息超时限制;只有当消费者挂掉时,RabbitMQ才会重新投递。即使处理一条消息会花费很长的时间。
   4. 消息应答是默认打开的。我们通过显示的设置autoAsk=true关闭这种机制。现即自动应答开,一旦我们完成任务,消费者会自动发送应答。通知RabbitMQ消息已被处理,可以从内存删除。如果消费者因宕机或链接失败等原因没有发送ACK(不同于ActiveMQ,在RabbitMQ里,消息没有过期的概  念),则RabbitMQ会将消息重新发送给其他监听在队列的下一个消费者。

 

案例:
生产者端代码不变,消费者端代码这部分就是用于开启手动应答模式的。
channel.basicConsume(QUEUE_NAME, false, defaultConsumer);
注:第二个参数值为false代表关闭RabbitMQ的自动应答机制,改为手动应答。
在处理完消息时,返回应答状态,true表示为自动应答模式。
channel.basicAck(envelope.getDeliveryTag(), false);

 

应答模式: 自动签收~~~

自动应答: 不在乎消费者对消息处理是否成功,都会告诉队列删除消息。如果处理消息失败,实现自动补偿(队列投递过去 重新处理)。

 

手动应答: 消费者处理完业务逻辑,手动返回ack(通知)告诉队列处理完了,队列进而删除消息。

 

生产者代码不变,消费者:

package com.toov5.Consumer;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.toov5.utils.MQConnectionUtils;

public class Consumer {
  
     //队列名称
        private static final String QUEUE_NAME = "test_queue";
        
        public static void main(String[] args) throws IOException, TimeoutException {
            System.out.println("消费者启动..........");
            //创建新的连接
        Connection connection = MQConnectionUtils.newConnection();
           //创建Channel
            Channel channel = connection.createChannel();
            // 消费者关联队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            
              DefaultConsumer defaultConsumerr = new DefaultConsumer(channel) {
                  //监听获取消息
                    @Override
                    public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
                            byte[] body) throws IOException {
                        String msg =new String(body,"UTF-8");
                        System.out.println("消费者获取生产者消息:"+msg);
                    }
              };
            //牵手模式设置  默认自动应答模式  true:自动应答模式  
              channel.basicConsume(QUEUE_NAME, false, defaultConsumerr);//    fanse手动应答          
              
//            //关闭通道和连接
//             channel.close();
//             connection.close();
        }
}

手动应答。此时 消息队列的消息 一直没有被清除掉

如下修改:

package com.toov5.Consumer;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP.BasicProperties;
import com.toov5.utils.MQConnectionUtils;

public class Consumer {
  
     //队列名称
        private static final String QUEUE_NAME = "test_queue";
        
        public static void main(String[] args) throws IOException, TimeoutException {
            System.out.println("消费者启动..........");
            //创建新的连接
        Connection connection = MQConnectionUtils.newConnection();
           //创建Channel
            final Channel channel = connection.createChannel();
            // 消费者关联队列
            channel.queueDeclare(QUEUE_NAME, false, false, false, null);
            
              DefaultConsumer defaultConsumerr = new DefaultConsumer(channel) {
                  //监听获取消息
                    @Override
                    public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties,
                            byte[] body) throws IOException {
                        String msg =new String(body,"UTF-8");
                        System.out.println("消费者获取生产者消息:"+msg);
                        channel.basicAck(envelope.getDeliveryTag(), false);  //手动应答 告诉消息队列服务器 消费成功
                    }
              };
            //牵手模式设置  默认自动应答模式  true:自动应答模式  
              channel.basicConsume(QUEUE_NAME, false, defaultConsumerr);//    fanse手动应答          
              
//            //关闭通道和连接
//             channel.close();
//             connection.close();
        }
}

 

 这样就消费完毕了

 

高级应用二: 公平队列

目前消息转发机制是平均分配,这样就会出现俩个消费者,奇数的任务很耗时,偶数的任何工作量很小,造成的原因就是近当消息到达队列进行转发消息。并不在乎有多少任务消费者并未传递一个应答给RabbitMQ。仅仅盲目转发所有的奇数给一个消费者,偶数给另一个消费者。

为了解决这样的问题,我们可以使用basicQos方法,传递参数为prefetchCount= 1。这样告诉RabbitMQ不要在同一时间给一个消费者超过一条消息。
换句话说,只有在 消费者空闲的时候会发送下一条信息。调度分发消息的方式,也就是告诉RabbitMQ每次只给消费者处理一条消息,也就是等待消费者处理完毕并自己对刚刚处理的消息进行确认之后,才发送下一条消息,防止消费者太过于忙碌,也防止它太过去清闲。
通过 设置channel.basicQos(1);

 

 

服务器能力不同,能者多劳。 均摊模式的话,都处理相同数量的

 消息队列 发出去的消息被消费完了 然后收到 ack包 才可以继续发给他

 公平队列原理:队列服务器向消费者发送消息的时候,消费者采用手动应答模式,队列服务器必须要收到消费者发送ack结果通知,才会发送下一个消息。(快的处理的多,消费的多)

producer:

package com.toov5.Producer;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.toov5.utils.MQConnectionUtils; public class Producer { // 队列名称 private static final String UEUE_NAME = "test_queue"; public static void main(String[] args) throws IOException, TimeoutException { // 创建新的连接 Connection connection = MQConnectionUtils.newConnection(); // 创建Channel Channel channel = connection.createChannel(); // 创建队列 channel.queueDeclare(UEUE_NAME, false, false, false, null);  channel.basicQos(1); // 保证 取一个消费 队列给消费者发送消息时候 一个消息 for (int i = 0; i < 10; i++) { // 创建message String msg = "toov5_message"; System.out.println("生产者投递消息" + msg + i); // 生产者发送消息 channel.basicPublish("", UEUE_NAME, null, msg.getBytes()); } // 关闭通道和连接  channel.close(); connection.close(); } }

 

Consumer1

package com.toov5.Consumer;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection; import com.rabbitmq.client.DefaultConsumer; import com.rabbitmq.client.Envelope; import com.toov5.utils.MQConnectionUtils; public class Consumer1 { //队列名称 private static final String QUEUE_NAME = "test_queue"; public static void main(String[] args) throws IOException, TimeoutException { System.out.println("消费者启动..........1"); //创建新的连接 Connection connection = MQConnectionUtils.newConnection(); //创建Channel final Channel channel = connection.createChannel(); // 消费者关联队列 channel.queueDeclare(QUEUE_NAME, false, false, false, null); channel.basicQos(1); DefaultConsumer defaultConsumerr = new DefaultConsumer(channel) { //监听获取消息  @Override public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException { String msg =new String(body,"UTF-8"); System.out.println("消费者获取生产者消息:"+msg); try { //模拟应答等待时间 Thread.sleep(1000); } catch (Exception e) { }finally { channel.basicAck(envelope.getDeliveryTag(), false); //手动应答 告诉消息队列服务器 消费成功  } } }; //牵手模式设置 默认自动应答模式 true:自动应答模式 channel.basicConsume(QUEUE_NAME, false, defaultConsumerr);// fanse手动应答  } }

 

Consumer2

package com.toov5.Consumer;

import java.io.IOException;
import java.util.concurrent.TimeoutException;

import com.rabbitmq.client.AMQP.BasicProperties;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection; import com.rabbitmq.client.DefaultConsumer; import com.rabbitmq.client.Envelope; import com.toov5.utils.MQConnectionUtils; public class Consumer2 { //队列名称 private static final String QUEUE_NAME = "test_queue"; public static void main(String[] args) throws IOException, TimeoutException { System.out.println("消费者启动..........2"); //创建新的连接 Connection connection = MQConnectionUtils.newConnection(); //创建Channel final Channel channel = connection.createChannel(); // 消费者关联队列 channel.queueDeclare(QUEUE_NAME, false, false, false, null); channel.basicQos(1); DefaultConsumer defaultConsumerr = new DefaultConsumer(channel) { //监听获取消息  @Override public void handleDelivery(String consumerTag, Envelope envelope, BasicProperties properties, byte[] body) throws IOException { String msg =new String(body,"UTF-8"); System.out.println("消费者获取生产者消息:"+msg); try { //模拟应答等待时间 Thread.sleep(300); } catch (Exception e) { }finally { channel.basicAck(envelope.getDeliveryTag(), false); //手动应答 告诉消息队列服务器 消费成功  } } }; //牵手模式设置 默认自动应答模式 true:自动应答模式 channel.basicConsume(QUEUE_NAME, false, defaultConsumerr);// fanse手动应答  } }

运行结果:

睡眠少的(执行快的) 指定的多

 注意 每个消费者 必须要应答 一下! 队列服务器没有收到应答 就不会发送下一个给消费者~

 

转载于:https://www.cnblogs.com/toov5/p/9940566.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值