rabbitmq如何保证消息可靠性不丢失
之前我们简单介绍了rabbitmq的功能。他的作用就是方便我们的消息解耦。紧接着问题就会暴露出来。解耦就设计到双方系统不稳定问题。在mq中有生产者、mq、消费者三个角色。其中一个角色down机或者重启后。就设计到消息的丢失问题。
因为MQ整个消息周期设计到上述的三个角色,所以我们从这个三个角色开始讨论丢失数据的情况。并如何解决
生产者丢失消息
- 在生产数据程序中,消息已经处理好还未发送给MQ这个阶段,生产者因为意外情况中断了。这个时候生产者这条消息就会丢失。因为程序重启好之后可能不会再次生产该消息。
实际案列1:
- 购物商城中已经选购了商品提交到支付界面。在支付成功后我们的程序需要发送消息给商家。这个时候程序中断了。待重启后客户界面订单状态是付款成功的。但是这个订单就没有及时通知给商家。这会造成商家延迟发货。
实际案例2:
- 同样是购物支付,A客户先付款order1订单,支付成功后发送MQ前直线异常但并未导致程序中断。这个时候order1商家收不到通知,然后B客户对order2订单进行支付且整个过程正常。order2订单就会通知到对应的商家。整个周期order1订单就属于丢失
总结:
- 两种情况都是在发送消息是出现问题。第一种是程序中断,第二种是订单异常,第一种异常级别高会影响整个程序使用反而是好排查。第二种程序不异常。这种情况很难发现,只会是个别情况。
解决方案:
- 针对上述情况mq也提供了两种方法解决。
- 1、开启rabbitmq事务(同步)
- 2、开启confirm模式(异步)
代码模拟
Map<String, Object> resultMap = new HashMap<String, Object>(){
{
put("code", 200);
}
};
String msg = "";
Integer index = 0;
if (params.containsKey("msg")) {
msg = params.get("msg").toString();
}
if (params.containsKey("index")) {
index = Integer.valueOf(params.get("index").toString());
}
if (index != 0) {
//这里开始模拟异常出现。消息将会丢失
int i = 1 / 0;
}
rabbitTemplate.convertAndSend(RabbitConfig.TOPICEXCHANGE, "zxh", msg);
return resultMap;
- 上述代码
http://localhost:8282/rabbitmq/sendTopic?msg=test&index=1
就会发生异常,这个时候数据丢失 http://localhost:8282/rabbitmq/sendTopic?msg=test
可以正常发送。读者可以自行测试- 其实通过rabbitmq的事务并不能解决上面的丢失情况。但是加上事务会保证消息发送的可靠性。上面发送消息后出异常这时候我们就没法回退消息了。但是事务可以帮我们实现
事务
String msg = "trantest";
Connection connection = rabbitTemplate.getConnectionFactory().createConnection();
Channel channel = connection.createChannel(true);
try {
channel.basicPublish(RabbitConfig.TOPICEXCHANGE, "zxh", null, msg.getBytes());
int i = 1 / 0;
} catch (IOException e) {
channel.txRollback();
e.printStackTrace();
}
channel.txCommit();
connection.close();
- 最终测试效果是mq没有收到消息的。
confirm模式确实
Connection connection = rabbitTemplate.getConnectionFactory().createConnection();
Channel channel = connection.createChannel(false);
channel.confirmSelect();
try {
channel.basicPublish(RabbitConfig.TOPICEXCHANGE, "zxh", null, msg.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
boolean b = channel.waitForConfirms();
if (b) {
System.out.println("mq接收消息成功");
Thread.sleep(1000*5);
}
System.out.println("end1");
channel.confirmSelect();
channel.basicPublish(RabbitConfig.TOPICEXCHANGE, "zxh", null, msg.getBytes());
channel.addConfirmListener(new ConfirmListener() {
@SneakyThrows
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
System.out.println("~~~~~消息成功发送到交换机");
Thread.sleep(1000 * 5);
}
@Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
System.out.println("~~~~~消息发送到交换机失败");
}
});
System.out.println("end2");
channel.close();
connection.close();
- 上面使用了两种确认方式,前者是同步确认,后者是异步确认。因为在同一个方法里。msg都是能获取到的。所以在ConfimListener中就没有返回消息。
数据退回监听
- 上面两种一个增加安全可靠性。一个增加确认机制。还有一种情况是数据回退。当交换机没有队列绑定是这个时候发送数据后如果设置了回退属性,那么消息会回退到监听器汇中的。channel中的mandatory表示是否检测分发到队列中。
String msg = "Hello World!";
Connection connection = rabbitTemplate.getConnectionFactory().createConnection();
Channel channel = connection.createChannel(false);
channel.confirmSelect();
//return机制:监控交换机是否将消息分发到队列
channel.addReturnListener(new ReturnListener() {
@Override
public void handleReturn(int i, String s, String s1, String s2, AMQP.BasicProperties basicProperties, byte[] bytes) throws IOException {
//如果交换机分发消息到队列失败,则会执行此方法(用来处理交换机分发消息到队列失败的情况)
System.out.println("*****"+i); //标识
System.out.println("*****"+s); //
System.out.println("*****"+s1); //交换机名
System.out.println("*****"+s2); //交换机对应的队列的key
System.out.println("*****"+new String(bytes)); //发送的消息
}
});
//发送消息
//channel.basicPublish("ex2", "c", null, msg.getBytes());
channel.basicPublish(RabbitConfig.DIRECTEXCHANGE, "c", true, null, msg.getBytes());
channel.addConfirmListener(new ConfirmListener() {
@SneakyThrows
@Override
public void handleAck(long deliveryTag, boolean multiple) throws IOException {
System.out.println("~~~~~消息成功发送到交换机");
Thread.sleep(1000 * 5);
}
@Override
public void handleNack(long deliveryTag, boolean multiple) throws IOException {
System.out.println("~~~~~消息发送到交换机失败");
}
});
- 上面ReturnListener就会被触发,这个时候confirm监听器也被触发认为成功接收的只不过被退回。
MQ事务相关软文推荐
MQ丢失信息
- 在发送消息到MQ时我们可以设置消息属性是否为可持久化。如果设置了直接就会存储在磁盘上。在内存可用时也会同步到内存中提高效率。如果消息属性中设置的是非持久化的话,就会直接存储在内存里,当内存不足是会将数据备份至磁盘上。
消费者丢失信息
消费端如果没有单独设置的话默认就是MQ不管理。换句话说MQ只负责发送消息。mq为我们提供了三种模式
NONE,
MANUAL,
AUTO; 默认的我们需要手动将连接工厂设置MANUAL后再接收到消息后我们需要手动确认,mq才会删除消息。否则会一直等待到消费端重启才会进行重新分发数据
channel.basicAck(long,boolean); 确认收到消息,消息将被队列移除,false只确认当前consumer一个消息收到,true确认所有consumer获得的消息。
channel.basicNack(long,boolean,boolean); 确认否定消息,第一个boolean表示一个consumer还是所有,第二个boolean表示requeue是否重新回到队列,true重新入队。
channel.basicReject(long,boolean); 拒绝消息,requeue=false 表示不再重新入队,如果配置了死信队列则进入死信队列。
当消息回滚到消息队列时,这条消息不会回到队列尾部,而是仍是在队列头部,这时消费者会又接收到这条消息,如果想消息进入队尾,须确认消息后再次发送消息。
rabbitmq如何保证消息可靠性不丢失的更多相关文章
- RabbitMQ消息丢失问题和保证消息可靠性-消费端不丢消息和HA(二)
继续上篇文章解决RabbitMQ消息丢失问题和保证消息可靠性(一) 未完成部分,我们聊聊MQ Server端的高可用和消费端如何保证消息不丢的问题? 回归上篇的内容,我们知道消息从生产端到服务端,为了 ...
- 解决RabbitMQ消息丢失问题和保证消息可靠性(一)
原文链接(作者一个人):https://juejin.im/post/5d468591f265da03b810427e 工作中经常用到消息中间件来解决系统间的解耦问题或者高并发消峰问题,但是消息的可靠 ...
- RabbitMQ高级之如何保证消息可靠性?
人生终将是场单人旅途,孤独之前是迷茫,孤独过后是成长. 楔子 本篇是消息队列RabbitMQ的第四弹. RabbitMQ我已经写了三篇了,基础的收发消息和基础的概念我都已经写了,学任何东西都是这样,先 ...
- springboot + rabbitmq发送邮件(保证消息100%投递成功并被消费)
前言: RabbitMQ相关知识请参考: https://www.jianshu.com/p/cc3d2017e7b3 Linux安装RabbitMQ请参考: https://www.jianshu. ...
- (转载)springboot + rabbitmq发送邮件(保证消息100%投递成功并被消费)
转载自https://www.jianshu.com/p/dca01aad6bc8 一.先扔一张图 image.png 说明: 本文涵盖了关于RabbitMQ很多方面的知识点, 如: 消息发送确认 ...
- RabbitMQ 如何保证消息不丢失?
RabbitMQ一般情况很少丢失,但是不能排除意外,为了保证我们自己系统高可用,我们必须作出更好完善措施,保证系统的稳定性. 下面来介绍下,如何保证消息的绝对不丢失的问题,下面分享的绝对干货,都是在知 ...
- (转)RabbitMQ学习之消息可靠性及特性
http://blog.csdn.net/zhu_tianwei/article/details/53971296 下面主要从队列.消息发送.消息接收方面了解消息传递过的一些可靠性处理. 1.队列 消 ...
- RabbitMQ如何保证消息99.99%被发送成功?
1. 本篇概要 RabbitMQ针对这个问题,提供了以下几个机制来解决: 生产者确认 持久化 手动Ack 本篇博客我们先讲解下生产者确认机制,剩余的机制后续单独写博客进行讲解. 2. 生产者确认 要想 ...
- RabbitMQ 和 Kafka 的消息可靠性对比
RabbitMQ和Kafka都提供持久的消息保证.两者都提供至少一次和至多一次的保证,另外,Kafka在某些限定情况下可以提供精确的一次(exactly-once)保证. 让我们首先理解一下上述术语的 ...
随机推荐
- Codeforces Round #643 (Div. 2) C. Count Triangles (数学公式)
题意:给你四个正整数\(A,B,C,D\),且\(A\le B\le C \le D\),有\(A\le x\le B\le y\le C \le z\le D\),求最多有多少组\((x,y,z)\ ...
- ElasticSearch 搜索引擎概念简介
公号:码农充电站pro 主页:https://codeshellme.github.io 1,倒排索引 倒排索引是一种数据结构,经常用在搜索引擎的实现中,用于快速找到某个单词所在的文档. 倒排索引会记 ...
- MiniSMB 网络性能测试 免费版本安装指南
1) 烧录Image至USB 在Linux环境下可以运行以下命令(假设usb设备符号为/dev/sdb): root# tar -Jxf minismb-free-edition.img.tar.xz ...
- windows安装
1.windows系统版本分类a. 个人版windows98.XP.win7,win8,win10b. 企业版/服务器版windows server NT/2000/2003/2008/2012[广泛 ...
- 实战交付一套dubbo微服务到k8s集群(7)之交付dubbo服务的消费者集群到K8S
构建dubbo-demo-consumer,可以使用和dubbo-demo-service的流水线来构建 1.登录jenkins构建dubbo-demo-consumer 2.填写构建dubbo-de ...
- P类问题,NP,NPC,HPHard,coNP,NPI问题 的简单认识
参考<算法设计技巧与分析>--沙特 问题可以分为判定类问题和最优化问题,判定类问题可以转化为最优化问题,所以下面讨论的是判定类的问题. P类问题是可以在多项式时间 采用确定性算法给出解 ...
- mysql 索引类型以及创建
明天就去面浦发了,感觉对数据库有些忘了,时间紧迫,就直接把链接贴这了,有空再整理. 参考: 1. https://www.cnblogs.com/crazylqy/p/7615388.html
- bash variables plus operator All In One
bash variables plus operator All In One Errors missing pass params #!/usr/bin/env bash # echo emoji ...
- Microsoft Solitaire Collection
Microsoft Solitaire Collection game https://zone.msn.com/gameplayer/gameplayerHTML.aspx?game=mssolit ...
- js & bitwise-operators
js & bitwise-operators 不用加减乘除运算符, 求整数的7倍 "use strict"; /** * * @author xgqfrms * @lice ...