RabbitMQ(四): rabbitmq 的消息确认机制(事务+confirm)
在 rabbitmq 中我们可以通过持久化数据解决 rabbitmq 服务器异常的数据丢失问题。
问题:生产者将消息发送出去之后,消息到底有没有到达 rabbitmq 服务器。默认情况下是不知道的。
两种方式:
- AMQP 实现了事务机制
- Confirm 模式
事务机制
- txSelect:用户将当前的 channel 设置成 transaction 模式
- txCommit:用于提交事务
- txRollback:回滚事务
缺点:降低了 rabbitmq 的吞吐量。
生产者
public class TxSend {
private static final String QUEUE_NAME = "test_queue_tx";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
String msg = "hello tx message!";
try {
channel.txSelect();
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
// 出错测试
int xx = 1 / 0;
System.out.println("send: " + msg);
channel.txCommit();
} catch (Exception e) {
channel.txRollback();
System.out.println("send message rollback.");
}
channel.close();
connection.close();
}
}
消费者
public class TxRecv {
private static final String QUEUE_NAME = "test_queue_tx";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
channel.basicConsume(QUEUE_NAME, true, new DefaultConsumer(channel){
@Override
public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
System.out.println("recv[tx] msg: " + new String(body, "utf-8"));
}
});
}
}
Confirm 模式
生产者端 confirm 模式的实现原理
生产者将信道设置成 confirm 模式,一旦信道进入 confirm 模式,所有在该信道上面发布的消息都会被指派一个唯一的 ID(从1开始),一旦消息被投递到所有的匹配队列之后,broker 就会发送一个确认给生产者(包含消息的唯一ID),这就使得生产者知道消息已经正确到达目的队列了,如果消息和队列是可持久化的,那么确认消息会将消息写入磁盘之后发出,broker 回传给生产者的确认消息中 deliver-tag 域包含了确认消息的序列号,此外 broker 也可以设置 basic.ack 的 multiple 域,表示这个序列号之前的所有消息已经得到了处理。
confirm 模式最大的好处在于它是异步的。
开启 confirm 模式:
channel.confirmSelect();
编程模式:
- 普通 发一条 waitForConfirms()
- 批量 发一批 waitForConfirms()
- 异步 confirm 模式:提供一个回调方法
confirm 单条
public class Send1 {
private static final String QUEUE_NAME = "test_queue_confirm1";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 生产者调用 confirmSelect 将 channel 设置成为 confirm 模式 (注意)
channel.confirmSelect();
String msg = "hello confirm message!";
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
if (!channel.waitForConfirms()) {
System.out.println("message send failed.");
} else {
System.out.println("message send ok.");
}
channel.close();
connection.close();
}
}
confirm 批量
public class Send2 {
private static final String QUEUE_NAME = "test_queue_confirm1";
public static void main(String[] args) throws IOException, TimeoutException, InterruptedException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 生产者调用 confirmSelect 将 channel 设置成为 confirm 模式 (注意)
channel.confirmSelect();
String msg = "hello confirm message batch!";
// 批量模式
for (int i = 0; i< 10; i++) {
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
}
// 确认
if (!channel.waitForConfirms()) {
System.out.println("message send failed.");
} else {
System.out.println("message send ok.");
}
channel.close();
connection.close();
}
}
confirm 异步
Channel 对象提供的 ConfirmListener() 回调方法只包含 deliveryTag (当前 Channel 发出的消息序号),我们需要自己为每一个 Channel 维护一个 unconfirm 的消息序号集合,每 publish 一条数据,集合中元素加 1,每回调一次 handleAck 方法,unconfirm 集合删掉相应的一条(multiple=false)或多条(multiple=true)记录。从程序运行效率上看,这个 unconfirm 集合最好采用有序集合 SortedSet 存储结构。
public class Send3 {
private static final String QUEUE_NAME = "test_queue_confirm3";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 生产者调用 confirmSelect 将 channel 设置为 confirm 模式
channel.confirmSelect();
// 未确认的消息标识
final SortedSet<Long> confirmSet = Collections.synchronizedSortedSet(new TreeSet<Long>());
// 通道添加监听
channel.addConfirmListener(new ConfirmListener() {
// 没有问题的 handleAck
public void handleAck(long l, boolean b) throws IOException {
if (b) {
System.out.println("---handleAck---multiple");
confirmSet.headSet(l + 1).clear();
} else {
System.out.println("---handleAck---multiple false");
confirmSet.remove(l);
}
}
// handleNack 1s 3s 10s xxx...
public void handleNack(long l, boolean b) throws IOException {
if (b) {
System.out.println("---handleNack---multiple");
confirmSet.headSet(l + 1).clear();
} else {
System.out.println("---handleNack---multiple false");
confirmSet.remove(l);
}
}
});
String msg = "sssss";
while (true) {
long seqNo = channel.getNextPublishSeqNo();
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
confirmSet.add(seqNo);
}
}
}
消费者
(只需修改 QUEUE_NAME)
public class Send3 {
private static final String QUEUE_NAME = "test_queue_confirm3";
public static void main(String[] args) throws IOException, TimeoutException {
Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare(QUEUE_NAME, false, false, false, null);
// 生产者调用 confirmSelect 将 channel 设置为 confirm 模式
channel.confirmSelect();
// 未确认的消息标识
final SortedSet<Long> confirmSet = Collections.synchronizedSortedSet(new TreeSet<Long>());
// 通道添加监听
channel.addConfirmListener(new ConfirmListener() {
// 没有问题的 handleAck
public void handleAck(long l, boolean b) throws IOException {
if (b) {
System.out.println("---handleAck---multiple");
confirmSet.headSet(l + 1).clear();
} else {
System.out.println("---handleAck---multiple false");
confirmSet.remove(l);
}
}
// handleNack 1s 3s 10s xxx...
public void handleNack(long l, boolean b) throws IOException {
if (b) {
System.out.println("---handleNack---multiple");
confirmSet.headSet(l + 1).clear();
} else {
System.out.println("---handleNack---multiple false");
confirmSet.remove(l);
}
}
});
String msg = "sssss";
while (true) {
long seqNo = channel.getNextPublishSeqNo();
channel.basicPublish("", QUEUE_NAME, null, msg.getBytes());
confirmSet.add(seqNo);
}
}
}
RabbitMQ(四): rabbitmq 的消息确认机制(事务+confirm)的更多相关文章
- RabbitMQ学习笔记之五种模式及消息确认机制
本文详细介绍简单模式Simple.工作模式Work.发布订阅模式Publish/Subscribe.Topic.Routing. Maven依赖引用 <dependencies> < ...
- RabbitMQ (十二) 消息确认机制 - 发布者确认
消费者确认解决的问题是确认消息是否被消费者"成功消费". 它有个前提条件,那就是生产者发布的消息已经"成功"发送出去了. 因此还需要一个机制来告诉生产者,你发送 ...
- SpringBoot(九)RabbitMQ安装及配置和使用,消息确认机制
Windows下RabbitMQ安装及配置地址: https://blog.csdn.net/zhm3023/article/details/82217222RabbitMQ(四)订阅模式:https ...
- RabbitMQ 消息确认机制
消息确认机制 在之前异常处理部分就已经写了,对于consumer的异常退出导致消息丢失,可以时候consumer的消息确认机制.重复的就不说了,这里说一些不一样的. consumer的消息确认机制 当 ...
- (转)RabbitMQ消息队列(九):Publisher的消息确认机制
在前面的文章中提到了queue和consumer之间的消息确认机制:通过设置ack.那么Publisher能不到知道他post的Message有没有到达queue,甚至更近一步,是否被某个Consum ...
- RabbitMQ消息队列(九):Publisher的消息确认机制
在前面的文章中提到了queue和consumer之间的消息确认机制:通过设置ack.那么Publisher能不到知道他post的Message有没有到达queue,甚至更近一步,是否被某个Consum ...
- SpringBoot 整合 RabbitMQ(包含三种消息确认机制以及消费端限流)
目录 说明 生产端 消费端 说明 本文 SpringBoot 与 RabbitMQ 进行整合的时候,包含了三种消息的确认模式,如果查询详细的确认模式设置,请阅读:RabbitMQ的三种消息确认模式 同 ...
- RabbitMQ消息确认机制
文章目录 1. 事务机制2. Confirm模式2.1 生产者2.1.1 普通Confirm模式2.1.2 批量Confirm模式2.1.3 异步Confirm模式2.2 消费者3. 其他 消费者如何 ...
- springboot + rabbitmq 用了消息确认机制,感觉掉坑里了
本文收录在个人博客:www.chengxy-nds.top,技术资源共享,一起进步 最近部门号召大伙多组织一些技术分享会,说是要活跃公司的技术氛围,但早就看穿一切的我知道,这 T M 就是为了刷KPI ...
随机推荐
- Spring触发器触发2次问题【转】
触发2遍任务对大多数程序而言,都会造成 数据重复 和 资源浪费. 我们在写spring触发器的timetrigger.xml配置文件的时候, 千万不要在Spring的总配置文件applicationC ...
- xml总结(一 )
一.了解 XML(eXtensive Markup Language)可扩展标记语言. XML是被用来传输和存储数据,焦点是内容,是对html的补充. HTML是将数据进行格式化显示.xml需要自定义 ...
- Redis(二)冰叔带你了解Redis-哨兵模式和高可用集群解析
前言 Redis 的 主从复制 模式下,一旦 主节点 由于故障不能提供服务,需要手动将 从节点 晋升为 主节点,同时还要通知 客户端 更新 主节点地址,这种故障处理方式从一定程度上是无法接受的. ...
- scapy的安装
我是安装了sulley,这里安装了pcapy的模块. https://github.com/zlorb/scapy ----按照此链接的步骤安装 但是在安装pycrypto模块出现了错误. 这里通 ...
- js 分页问题
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...
- Linux - 进程服务资源
1.进程查看操作管理 ps -eaf # 查看所有进程 kill - PID # 强制终止某个PID进程 kill - PID # 安全退出 需程序内部处理信号 cmd & # 命令后台运行 ...
- Linux - 文件操作
touch file # 创建空白文件 rm -rf 目录名 # 不提示删除非空目录(-r:递归删除 -f强制) dos2unix # windows文本转linux文本 unix2dos # lin ...
- luogu P3565 [POI2014]HOT-Hotels
传送门 无脑暴力+O2=AC 题目要统计距离两两相等的三个点的组数,这三个点之间显然有一个点,并且这三个点到这个点的距离都相同.所以枚举中间这个点作为根,然后bfs整棵树,对于每一层,把以根的某个儿子 ...
- python技巧 计算字符串中字母出现的次数并取出最大
有一个字符串 “aaddfdfdercfghfyttefsfsfewretr123trefg5624sdfcgvfdgte6435234532”,现在需要取出里面出现次数最多的字符 第一种方法-装饰器 ...
- Eclipse通用设置
分类 Eclipse分为64位.32位,安装版.免安装版 查看Eclipse版本信息 Help - About Eclipse - Installation Details