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 ...
随机推荐
- JVM调优命令-jinfo
jinfo JVM Configuration info这个命令作用是实时查看和调整虚拟机运行参数. 之前的jps -v口令只能查看到显示指定的参数,如果想要查看未被显示指定的参数的值就要使用jinf ...
- linq总结系列(二)---Expression
一.linq中的表达式和表达式树 Linq中的表达式(Expression<TDel>)是强类型的lambda表达式,对Func和Action形式的委托做了一层封装. lambda表达式的 ...
- BIO | NIO | AIO (Java版)
几篇解释的不错的文章: BIO NIO AIO NIO.2 入门,第 1 部分: 异步通道 API 使用异步 I/O 大大提高应用程序的性能
- html5 实时监听输入框值变化的完美方案:oninput & onpropertychange
结合 HTML5 标准事件 oninput 和 IE 专属事件 onpropertychange 事件来监听输入框值变化. H5手机端: <input type="text" ...
- 无线DOS攻击
1.无线连接状态 IEEE 802.11定义了一种客户端状态机制,用于跟踪工作站身份验证和关联状态.无线客户端和AP基于IEEE标准实现这种状态机制.成功关联的客户站停留在状态3,才能进行无线通信.处 ...
- 绘图QPainter-画笔
绘图要在paintEvent()方法中实现.在QPainter对象的begin()与end()方法间编写绘图代码.它会在控件或其他图形设备上进行低级的图形绘制 画笔样式Penstyle Qt.Nope ...
- Linux之包管理工具总结[RPM/DPKG]-[YUM/APT]
0.关键词解释 RPM:Red Hat Package Manager(原名),RPM Package Manager(现名,递归缩写,类似于GNU的命名); 解释:RPM软件包管理器 YUM:Yel ...
- HDU4738 Caocao's Bridges【强连通】
题意: 曹操有N个岛,这些岛用M座桥连接起来,每座桥有士兵把守(也可能没有),周瑜想让这N个岛不连通,但只能炸掉一座桥,并且炸掉一座桥需要派出不小于守桥士兵数的人去,桥的守兵数为0时,也需要派出一个人 ...
- sonarqube6.7安装
1.下载sonarhttps://www.sonarqube.org/#downloads 2.配置JDK1.8 3.centOS默认mysql版本为mariaDB直接安装yum install my ...
- proxysql 系列 ~ 总揽概括
一 简介: proxysql相关知识汇总 二 proxysql 相关报错 1 proxysql 报错 too many connections 分析 proxysql关于连接池的参数 ...