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 ...
随机推荐
- 学习Git笔记
一.名词解释 1.仓库(Repository) 仓库用来存放项目代码,每个项目对应一个仓库,多个开源项目则有多个仓库. 2.收藏(Star) 收藏项目,方便下次查看 3.复制克隆项目(Fork) 该f ...
- scala面向对象.高阶函数,柯里化,Actor编程简介
1.定义一个类 class Person{ //用val修饰的变量是只读属性,有getter但是没有setter val id ="111" //用var修饰的变量既有getter ...
- UVALive - 7637 E - Balanced String(构造)
原题链接 题意:给出一个打乱顺序的序列,问是否能构造出一个括号匹配的字符串.每个数字为此前读取到的左括号数减去右括号数. 分析:有左括号开始构造,不够的话就找右括号.注意特殊情况待处理.详情看代码 # ...
- bzoj千题计划297:bzoj3629: [JLOI2014]聪明的燕姿
http://www.lydsy.com/JudgeOnline/problem.php?id=3629 约数和定理: 若n的标准分解式为 p1^k1 * p2^k2 …… 那么n的约数和= π (Σ ...
- DOM-Element对象
一. 整体介绍 这里介绍DOM对象中Element对象. 那么何为Element对象呢?Element对象就是HTML元素,Element对象包括:元素节点.文本节点.属性节点. 下面利用一张图来总 ...
- WEB 服务器 加速缓存比较
Nginx 相对 Apache httpd 的优点: - 轻量级,同样起web 服务,比apache 占用更少的内存及资源 - 抗并发,nginx 处理请求是异步非阻塞的,而apache 则是阻塞型的 ...
- python的安装和pycharm的安装
下载地址 官网:https://www.python.org/downloads/release/python-372/ Window 平台安装 Python: Add python xx to ...
- Kafka0.10.0安装配置
1 解压文件 tar -zvxf kafka_2.11-0.10.0.0.tgz 2 修改配置server.properties vim server.properties broker.id=1 z ...
- Adjoint of SE(3)
以前看的书都提到 SE(3) 和 se(3) 的 Adjoint,但是并没有讲这个东西是干什么用的,只是给了一堆性质.这东西来自群论. 参考 Lie Groups for 2D and 3D Tran ...
- spring-framework-x.x.x.RELEASE-dist下载教程
1.打开Spring官网:https://spring.io,点击PROJECTS 2.点击SPRING FRAMEWORK 3.点击GitHub图标 4.找到Access to Binaries,点 ...