一:消费者确认

消费者确认或者说消费者应答指的是RabbitMQ需要确认消息到底有没有被收到 
- 自动应答

boolean autoAck = true;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);

在订阅消息的时候可以指定应答模式,当自动应答等于true的时候,表示当消费者一收到消息就表示消费者收到了消息,消费者收到了消息就会立即从队列中删除。

生产者

public class Producer {
@Test
public void testBasicPublish() throws IOException, TimeoutException, InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
factory.setVirtualHost("/");
factory.setHost("127.0.0.1");
factory.setPort(AMQP.PROTOCOL.PORT);
factory.setUsername("mengday");
factory.setPassword("mengday"); Connection connection = factory.newConnection();
Channel channel = connection.createChannel(); String EXCHANGE_NAME = "exchange.direct";
String QUEUE_NAME = "queue_name";
String ROUTING_KEY = "key";
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
channel.queueDeclare(QUEUE_NAME, true, false, false, null);
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, ROUTING_KEY); String message = "Hello RabbitMQ:";
for (int i = 0; i < 5; i++) {
channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, null, (message + i).getBytes("UTF-8"));
} channel.close();
connection.close();
}
}

消费者

public class Consumer1 {
@Test
public void testBasicConsumer1() throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setVirtualHost("/");
factory.setHost("127.0.0.1");
factory.setPort(AMQP.PROTOCOL.PORT); // 5672
factory.setUsername("mengday");
factory.setPassword("mengday"); Connection connection = factory.newConnection();
final Channel channel = connection.createChannel();
String EXCHANGE_NAME = "exchange.direct";
String QUEUE_NAME = "queue_name";
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "key"); // GetResponse response = channel.basicGet(QUEUE_NAME, false);
// byte[] body = response.getBody();
// System.out.println(new String(body).toString()); Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println(message);
}
}; channel.basicConsume(QUEUE_NAME, true, consumer); Thread.sleep(100000);
}
}

运行结果:

运行生产者可以看到Ready=5, Unacked=0, Total=5, Total代表队列中的消息总条数,Ready代表消费者还可以读到的条数,Unacked:代表还有多少条没有被应答 

在消费者端的获取消息的第一行打个断点,可以看到,第一次进入到handleDelivery()方法时,队列瞬间被清空。Ready=0, Unacked=0, Total=0 

当消费者连接上队列了,因为没有指定消费者一次获取消息的条数,所以队列把队列中的所有消息一下子推送到消费者端,当消费者订阅的该队列,消息就会从队列推到客户端,当消息从队列被推出的时的那一刻就表示已经对消息进行自动确认了,消息就会从队列中删除。

  • 手动应答

手动应答和自动应答不一样,需要将autoAck设置为false,当消费者收到消息在合适的时候来显示的进行确认,说我已经接收到了该消息了,RabbitMQ可以从队列中删除该消息了,可以通过显示调用channel.basicAck(envelope.getDeliveryTag(), false);来告诉消息服务器来删除消息

boolean autoAck = false;
channel.basicConsume(QUEUE_NAME, autoAck, consumer);

消费者

Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println(message);
channel.basicAck(envelope.getDeliveryTag(), false);
}
}; channel.basicConsume(QUEUE_NAME, false, consumer);
Thread.sleep(100000);

当代码执行完channel.basicConsume(QUEUE_NAME, false, consumer);还没有进入到handleDelivery()方法时可以看到Ready=0, Unacked=5, Total=5 

当代码进入handleDelivery()方法没执行一次channel.basicAck(envelope.getDeliveryTag(), false);Unacked和Total就会减去1,直到两个值都为0 

特殊情况:手动应答如果忘记写channel.basicAck(envelope.getDeliveryTag(), false)这行代码,现象是消费者仍然能获取所有消息,不过此时Unacked和Total一直都是5,Ready=0, Unacked=5, Total=5,直到消费者运行结束,Ready=5, Unacked=0, Total=5 

特殊情况2:如果设置消费者每次从队列中获取指定的条数channel.basicQos(1);,此时如果没有应答的话,消费者将不再继续获取

// 因设置了一次获取一条,所以可读的为4,未应答的是1 

// 继续运行,因为一次只获取一条,而这一条还没有应答,就没有办法继续获取下一条 

// 消费者运行结束的时候又回到原来的状态Ready=5, Unacked=0, Total=5 

注意:如果都没有手动应答,在没有指定获取消息的条数时,消费者可以获取所有消息,当指定时,只能获取指定条,下次就只能等待了,没法继续获取下一条了

  • 手动拒绝 
    手动应答是除了确认应答,也可以拒绝应答。
requeue=true,表示将消息重新放入到队列中,false:表示直接从队列中删除,此时和basicAck(long deliveryTag, false)的效果一样
void basicReject(long deliveryTag, boolean requeue);

消费者代码示例一:

public class Consumer1 {
@Test
public void testBasicConsumer1() throws Exception{
ConnectionFactory factory = new ConnectionFactory();
factory.setVirtualHost("/");
factory.setHost("127.0.0.1");
factory.setPort(AMQP.PROTOCOL.PORT); // 5672
factory.setUsername("mengday");
factory.setPassword("mengday"); Connection connection = factory.newConnection();
final Channel channel = connection.createChannel();
String EXCHANGE_NAME = "exchange.direct";
String QUEUE_NAME = "queue_name";
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT);
channel.queueBind(QUEUE_NAME, EXCHANGE_NAME, "key"); Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println(message); if (message.contains(":3")){
// requeue:重新入队列,false:直接丢弃,相当于告诉队列可以直接删除掉
channel.basicReject(envelope.getDeliveryTag(), false);
} else {
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
}; channel.basicConsume(QUEUE_NAME, false, consumer);
Thread.sleep(100000);
}
}

消费者代码示例二:

Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println(message); if (message.contains(":3")){
// requeue:重新入队列,true: 重新放入队列
channel.basicReject(envelope.getDeliveryTag(), true);
} else {
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
}; channel.basicConsume(QUEUE_NAME, false, consumer);
Thread.sleep(100000);

结果解释:代码中没有指定设定消费者一次从队列中获取消息的条数,所以消费者一下子拿到了5条消息,消费了0、1、2当消费第i=3时执行channel.basicReject(envelope.getDeliveryTag(), true);会将消息放入到队列中,然后将消息推送给消费者, 然后消费4,接着再消费3,还会再次放入到队列,整个过程死循环,Ready=0, Unacked=1, Total=1, 当消费者运行结束了,Ready=1, Unacked=0, Total=1, 这个1就是消息3

  • 重新投递 
    basicRecover(): 重新投递并没有所谓的像basicReject中的basicReject的deliveryTag参数,所以重新投递好像是将消费者还没有处理的所有的消息都重新放入到队列中,而不是将某一条消息放入到队列中,与basicReject不同的是,重新投递可以指定投递的消息是否允许当前消费者消费。
// If true, messages will be requeued and possibly delivered to a different consumer. If false, messages will be  redelivered to the same consumer.

Basic.RecoverOk basicRecover(boolean requeue);

示例代码一:

Consumer consumer = new DefaultConsumer(channel) {
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
String message = new String(body, "UTF-8");
System.out.println(message); if (message.contains(":3")){
channel.basicRecover(true);
} else {
channel.basicAck(envelope.getDeliveryTag(), false);
}
}
}; channel.basicConsume(QUEUE_NAME, false, consumer);
Thread.sleep(100000);

这里不太明白,true的话表示会被其他消费者消费,不知道3、4又被接收了一次???


false:表示重新递送的消息还会被当前消费者消费 

二:生产者确认

当生产者发布消息到RabbitMQ中,生产者需要知道是否真的已经发送到RabbitMQ中,需要RabbitMQ告诉生产者。

  • Confirm机制 
    channel.confirmSelect(); 
    channel.waitForConfirms();

  • 事务机制 
    channel.txSelect(); 
    channel.txRollback();

注意:事务机制是非常非常非常消耗性能的,最好使用Confirm机制,Confirm机制相比事务机制性能上要好很多。

channel.confirmSelect();
String message = "Hello RabbitMQ:";
for (int i = 0; i < 5; i++) {
channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, null, (message + i).getBytes("UTF-8"));
}
boolean isAllPublished = channel.waitForConfirms();

********

String message = "Hello RabbitMQ:";
try {
channel.txSelect();
for (int i = 0; i < 5; i++) {
channel.basicPublish(EXCHANGE_NAME, ROUTING_KEY, null, (message + i).getBytes("UTF-8"));
}
channel.txCommit();
} catch (Exception e) {
channel.txRollback();
}

RabbitMQ---4、消息确认Ack的更多相关文章

  1. RabbitMQ的消息确认ACK机制

    1.什么是消息确认ACK. 答:如果在处理消息的过程中,消费者的服务器在处理消息的时候出现异常,那么可能这条正在处理的消息就没有完成消息消费,数据就会丢失.为了确保数据不会丢失,RabbitMQ支持消 ...

  2. rabbitmq++:RabbitMQ的消息确认ACK机制介绍

    1):什么是消息确认ACK. 答:如果在处理消息的过程中,消费者的服务器在处理消息的时候出现异常,那么可能这条正在处理的消息就没有完成消息消费,数据就会丢失.为了确保数据不会丢失,RabbitMQ支持 ...

  3. rabbitmq监控之消息确认ack

    rabbitmq springboot ack 监控 一.测试环境 二.启动测试 一直以来,学习rabbitmq都是跟着各种各样的教程.博客.视频和文档,撸起袖子就是干!!!最后,也成功了. 当然,成 ...

  4. RabbitMQ入门教程(十二):消息确认Ack

    原文:RabbitMQ入门教程(十二):消息确认Ack 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接:https://blog.csd ...

  5. RabbitMQ的消息确认机制

    一:确认种类 RabbitMQ的消息确认有两种. 一种是消息发送确认.这种是用来确认生产者将消息发送给交换器,交换器传递给队列的过程中,消息是否成功投递.发送确认分为两步,一是确认是否到达交换器,二是 ...

  6. RabbitMq初探——消息确认

    消息确认机制 前言 消息队列的下游,业务逻辑可能复杂,处理任务可能花费很长时间.若在一条消息到达它的下游,任务刚处理了一半,由于不确定因素,下游的任务处理进程 被kill掉啦,导致任务无法执行完成.而 ...

  7. RabbitMq之消息确认

    最近阅读了rabbitmq的官方文档,然后结合之前面试时被问到关于消息队列的问题来探索一下关于消息队列的消息确认机制. 其实消息确认就是消费者确认消息被消费了, 生产者确认消息已经发送到了消息队列中了 ...

  8. RabbitMQ学习笔记六:RabbitMQ之消息确认

    使用消息队列,必须要考虑的问题就是生产者消息发送失败和消费者消息处理失败,这两种情况怎么处理. 生产者发送消息,成功,则确认消息发送成功;失败,则返回消息发送失败信息,再做处理. 消费者处理消息,成功 ...

  9. Java使用RabbitMQ之消息确认(confirm模板)

    RabbitMQ生产者消息确认Confirm模式,分为普通模式.批量模式和异步模式,本次举例为普通模式. 源码: package org.study.confirm4; import com.rabb ...

  10. RabbitMQ 之消息确认机制(事务+Confirm)

    概述 在 Rabbitmq 中我们可以通过持久化来解决因为服务器异常而导致丢失的问题,除此之外我们还会遇到一个问题:生产者将消息发送出去之后,消息到底有没有正确到达 Rabbit 服务器呢?如果不错得 ...

随机推荐

  1. sqlite批量处理数据性能优化

    最近设计到sqlite数据库批量操作的,性能很是问题.于是一番研究(站在巨人肩膀)从网上整理出来相关性能优化方向.大体分三个级别,一般第一个阶段已足够. 1.sqlite每次插入数据(每调用一次sql ...

  2. ClamAV学习【5】—— cli_scanpe函数浏览

    这近2000行的代码,要是没有Source Insight,都不知道怎么看下去.跟着跟着来到了PE文件查杀的地方,发现前面都中规中矩地进行PE属性检查,中间一段开始扫描每个区块,然后和特征库的size ...

  3. 问题 E: YK的书架

    点击打开链接 问题 E: YK的书架 时间限制: 1 秒  内存限制: 128 MB 提交: 596  解决: 138 提交 状态 题目描述     YK新买了2n+1本相同的书,准备放在家里的3层书 ...

  4. C - Oil Deposits(dfs)

    点击打开链接 Oil Deposits Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Othe ...

  5. pymongo 学习

    查看一条记录,返回一条 dict 记录 db.Account.find_one({"UserName":"keyword"}) 查看某一列的一条记录(此时的1, ...

  6. “全栈2019”Java异常第十六章:Throwable详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java异 ...

  7. 白帽hacker酷炫小技能大盘点!

    白帽子是谁? 他们与一行行代码打交道,在“0”和“1”的世界中寻找风险,在IT产业软硬件核心技术和代码等自主研发能力不足.安全防护手段滞后.地下黑客业务已形成产业链的背景下,我国网络安全正面临日益严峻 ...

  8. Saiku 系列

    1.Saiku2.6 Saiku315 链接SQL的JDBC字符串 2.Saiku2.6 配置数据源 3.Saiku2.6 保存查询后,重新打开报 Error Loading Query错误. 4.C ...

  9. BurpSuite系列----Extender模块(扩展器)

    一.简介 Burp在软件中提供了支持第三方拓展插件的功能,方便使用者编写自己的自定义插件或从插件商店中安装拓展插件.Burp扩展程序可以以多种方式支持自定义Burp的行为,例如:修改HTTP请求和响应 ...

  10. JS实现表格列宽拖动

    在数据表格中,有时候需要拖动表格宽度,查看完整的数据,是很常用的功能. 1 效果 可以用纯JS就可以实现,如下,是正常情况下的表格: 拖动表格标题中间线,拖动后效果如下: 查看DEMO 2 代码 HT ...