在进行本篇文章的学习之前,你需要先阅读 https://www.cnblogs.com/duanjt/p/10057330.html。以便对Java访问RabbitMQ的基础用法有所了解。

一、失败通知

基于前面的讲解,如果消息通过交换器发送到指定的路由键,而这个路由键却没有被队列绑定,那么这条消息就会被丢弃。从这个角度来说消息的可靠性就比较低。为了增强可靠性,于是引入了失败通知的机制。在生产者发送消息到RabbitMQ的时候,如果路由键没有被队列绑定就将回调一个函数,让消费者能够知晓数据发送失败,从而做一些处理(注意:发送成功不会回调)。
具体代码如下:

public class Productor {
public static String EXCHANGE_NAME = "zd_exchange";
public static String ROUTEKEY = "zd_data_list1"; public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("172.23.88.116");
factory.setPort(5672);
factory.setUsername("zhangxueliang");
factory.setPassword("zhangxueliang");
factory.setVirtualHost("/"); Connection conn = factory.newConnection();
Channel channel = conn.createChannel(); // 创建交换器
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT); // 创建失败通知
channel.addReturnListener(new ReturnListener() { @Override
public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException { System.out.println("ERROR DATA:" + new String(body));
System.out.println("replyText:" + replyText);
System.out.println("replyCode:" + replyCode);
System.out.println("================");
}
}); // 发送数据
for (int i = 0; i < 3; i++) {
String msg = "Hello world " + i;
channel.basicPublish(EXCHANGE_NAME, ROUTEKEY, true, null, msg.getBytes());
System.out.println("send:" + msg);
} Thread.sleep(2000);//暂停2秒,以便接收回调。因为通道关闭后就不能接收到回调消息了 channel.close();
conn.close();
}
}

注意:

1.我们需要在发送消息的时候指定mandatory=true(channel.basicPublish的第三个参数)
2.我们需要通过channel增加一个返回监听,replyText表示错误原因,body表示发送的消息内容。

二、事务

1.channel.txSelect()声明启动事务模式;
2.channel.txComment()提交事务;
3.channel.txRollback()回滚事务;
说明:由于事务会严重影响效率,所以我们一般不使用,而是用发送方确认模式来保证数据的可靠性。

三、发送方确认模式

发送方确认模式和事务还是有一定的区别
比如发送10条数据--
事务能保证在第5条发送错误的时候就回滚,RabbitMQ里面没有一条数据。
而发送者确认模式在第5条发送错误的时候会抛出一个异常,生产者就能抓住这个异常从而得知没有全部成功,但是已经发送的数据却已经存在于RabbitMQ了。
发送方确认有3种方式。2种同步,1种异步
方式一:channel.waitForConfirms()普通发送方确认模式;消息到达交换器,就会返回true。
方式二:channel.waitForConfirmsOrDie()批量确认模式;使用同步方式等所有的消息发送之后才会执行后面代码,只要有一个消息未到达交换器就会抛出IOException异常。
方式三:channel.addConfirmListener()异步监听发送方确认模式
【注意】必须要先启动发送者确认模式channel.confirmSelect();
具体代码:

public class Productor {
public static String EXCHANGE_NAME = "zd_exchange";
public static String ROUTEKEY = "zd_data_list"; public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("172.23.88.116");
factory.setPort(5672);
factory.setUsername("zhangxueliang");
factory.setPassword("zhangxueliang");
factory.setVirtualHost("/"); Connection conn = factory.newConnection();
Channel channel = conn.createChannel(); // 创建交换器
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT); // 创建失败通知
channel.addReturnListener(new ReturnListener() {
@Override
public void handleReturn(int replyCode, String replyText, String exchange, String routingKey, AMQP.BasicProperties properties, byte[] body) throws IOException { System.out.println("handleReturn==>" + replyText);
}
}); // 异步确认
channel.addConfirmListener(new ConfirmListener() {
@Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
System.out.println("handleNack==>" + deliveryTag + "----" + multiple);
} @Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
System.out.println("handleAck==>" + deliveryTag + "----" + multiple);
}
}); // 启用发送确认模式
channel.confirmSelect(); // 发送数据
String[] routekeys = new String[] { "zd_data_list", "zd_data_list2", "zd_data_list3" };
for (int i = 0; i < 3; i++) {
String msg = "Hello world " + i;
channel.basicPublish(EXCHANGE_NAME, routekeys[i % 3], true, null, msg.getBytes());
System.out.println("send:" + msg); // 同步单条确认
//channel.waitForConfirms();
}
// 同步批量确认
// channel.waitForConfirmsOrDie(); Thread.sleep(2000);// 暂停2秒,以便接收回调。因为通道关闭后就不能接收到回调消息了 channel.close();
conn.close();
}
}

注意:

1.发送者确认模式只是告诉生产者是否发送成功,至于成功和失败后的具体处理还是代码自己实现。
2.同步单条确认channel.waitForConfirms()会返回是否成功。同步批量确认channel.waitForConfirmsOrDie()可通过异常来判断是否成功。
3.异步确认channel.addConfirmListener()。有两个参数(long deliveryTag, boolean multiple)。第一个参数表示消息的Id,第二个参数表示是否为批量。如果第二个参数为true,那么编号<=deliveryTag的所有消息都确认了。
4.发送者确认模式和失败通知可以一起使用。无论消息的路由键是否有队列绑定,都会返回Ack表示成功。而如果没有队列绑定路由键,同时又启用了失败通知,那么还会在调用channel.addConfirmListener()之前去调用channel.addReturnListener()

四、备用交换器

备用交换器从本质上说也就是一个普通交换器。当消息发送到普通交换器而没有队列绑定路由键的时候。该消息就会发送到备用交换器上面。如果备用交换器上面也没得队列绑定,那么这条消息也将被丢弃(如果配置了失败通知,那么将回调失败通知方法channel.addReturnListener())
核心代码:

// 创建主交换器
Map<String, Object> argsMap = new HashMap<String, Object>();
argsMap.put("alternate-exchange", BACK_EXCHANGE_NAME);// 定义主交换器的备用交换器名称
channel.exchangeDeclare(EXCHANGE_NAME, BuiltinExchangeType.DIRECT, false, false, argsMap);
// 创建备用交换器
channel.exchangeDeclare(BACK_EXCHANGE_NAME, BuiltinExchangeType.FANOUT);

RabbitMQ消息发布时的权衡的更多相关文章

  1. RabbitMQ消息发布和消费的确认机制

    前言 新公司项目使用的消息队列是RabbitMQ,之前其实没有在实际项目上用过RabbitMQ,所以对它的了解都谈不上入门.趁着周末休息的时间也猛补习了一波,写了两个窗体应用,一个消息发布端和消息消费 ...

  2. RabbitMQ消息的交换

    消息的交换 目录 RabbitMQ-从基础到实战(1)— Hello RabbitMQ RabbitMQ-从基础到实战(2)— 防止消息丢失 1.简介 在前面的例子中,每个消息都只对应一个消费者,即使 ...

  3. (五)RabbitMQ消息队列-安装amqp扩展并订阅/发布Demo(PHP版)

    原文:(五)RabbitMQ消息队列-安装amqp扩展并订阅/发布Demo(PHP版) 本文将介绍在PHP中如何使用RabbitMQ来实现消息的订阅和发布.我使用的系统依然是Centos7,为了方便, ...

  4. 9. RabbitMQ系列之消息发布确认

    Publisher Confirms发布确认是用于实现可靠发布的RabbitMQ扩展. 我们将使用发布确认来确保已发布的消息已安全到达代理.我们将介绍几种使用publisher确认的策略,并解释其优缺 ...

  5. Rabbitmq消息队列(四) 发布订阅

    1.简介 在上篇教程中,我们搭建了一个工作队列,每个任务只分发给一个工作者,在本篇教程中,我们要做的跟之前完全不一样 —— 分发一个消息给多个消费者(consumers).这种模式被称为“发布/订阅” ...

  6. 【python】-- RabbitMQ Publish\Subscribe(消息发布\订阅)

    RabbitMQ RabbitMQ Publish\Subscribe(消息发布\订阅) 1对1的消息发送和接收,即消息只能发送到指定的queue里,但这样使用有些局限性,有些时候你想让你的消息被所有 ...

  7. RabbitMQ消息队列在PHP下的应用

    消息队列的实现中,RabbitMQ以其健壮和可靠见长.公司的项目中选择了它作为消息队列的实现.关于MQ的机制和原理网上有很多文章可以看,这里就不再赘述,只讲几个比较容易混淆的问题 1,binding ...

  8. 【译】RabbitMQ:发布-订阅(Publish/Subscribe)

    在前一篇教程中,我们创建了一个工作队列,我们假设在工作队列后的每一个任务都只被调度给一个消费者.在这一部分,我们将做一些完全不一样的事情,调度同一条消息给多个消费者,也就是有名的“发布-订阅”模式.为 ...

  9. [转]RabbitMQ消息队列在PHP下的应用

    FROM : http://www.cnblogs.com/phpinfo/p/4104551.html 参考资料: http://www.yuansir-web.com/tag/rabbitmq/ ...

随机推荐

  1. ERROR 1526 (HY000): Table has no partition for value xxx

    最近,我们有些功能需要使用到基于多个字段的分区,需要同时支持oracle/mysql,但是开发人员又希望尽可能少的改动业务代码,也不愿意使用多列分区,在oracle 11g之前,不支持多列分区(12. ...

  2. MySQL更改relay-bin名称导致同步停止的解决办法

    今天在优化io的时候,移动了从库relay-bin的位置,并将hostname部分去掉了,启动后,从库slave状态如下: mysql> show slave status\G; ******* ...

  3. 【题解】Luogu P2605 [ZJOI2010]基站选址

    原题传送门:P2604 [ZJOI2010]基站选址 看一眼题目,变知道这题一定是dp 设f[i][j]表示在第i个村庄修建第j个基站且不考虑i+1~n个村庄的最小费用 可以得出f[i][j] = M ...

  4. 通过注册表regedit对Windows回收站进行恢复

    误删资料恢复 一不小心,删错了,还把回收站清空了,咋办啊? 只要三步,你就能找回你删掉并清空回收站的东西 步骤: 1.单击"开始——运行,然后输入regedit(打开注册表) 2.依次展开: ...

  5. 07: mysql锁和事物隔离

    MySQL其他篇 目录: 1.1 MySQL中的事物 1.2 mysql中锁 1.1 MySQL中的事物返回顶部 1.InnoDB事务原理 1. 事务(Transaction)是数据库区别于文件系统的 ...

  6. Codeforces 822D My pretty girl Noora - 线性筛 - 动态规划

    In Pavlopolis University where Noora studies it was decided to hold beauty contest "Miss Pavlop ...

  7. Codeforces 772A Voltage Keepsake - 二分答案

    You have n devices that you want to use simultaneously. The i-th device uses ai units of power per s ...

  8. 经典算法分析:n^2与nlgn

    冒泡.插入.选择排序的时间复杂度为O(n2) Arrays.sort()时间复杂度为nlgn 具体算法实现代码: package recursion; import java.util.Arrays; ...

  9. ScheduledTheadPool线程池的使用

    ScheduledTheadPool线程池的特点在于可以延迟执行任务,也可以周期性执行任务. 创建线程池 ScheduledExecutorService scheduled = Executors. ...

  10. topcoder srm 415 div1

    problem1 link 每次贪心地从crans由大到小地找到一个能搬得动地尽量大地box即可. problem2 link 首先,$hava$可以全部换成钱,然后就是找到一个最小的钱减去自己已有的 ...