每日一句

军人天生就舍弃了战斗的意义!

概述

RabitMQ 发布确认,保证消息在磁盘上。

前提条件

1。队列必须持久化 队列持久化

2。队列中的消息必须持久化 消息持久化

使用

三种发布确认的方式:

1。单个发布确认

2。批量发布确认

3。异步批量发布确认

开启发布确认的方法

 //创建一个连接工厂
ConnectionFactory factory = new ConnectionFactory();
factory.setHost("127.0.0.1");
factory.setUsername("guest");
factory.setPassword("guest");
Connection connection = factory.newConnection();
Channel channel = connection.createChannel();
**
//开启发布确认
channel.confirmSelect();**

单个确认

最简单的确认方式,它是一种同步发布确认的方式,也就是说发送一个消息后只有它被确认,后续的消息才能继续发布。

最大缺点是:发布速度特别的满。

吞吐量:每秒不超过数百条发布的消息

/**
* 单个确认
*/
public static void publishSingleMessage() throws Exception {
Channel channel = RabbitMqUtils.getChannel();
//生命队列
String queueName = UUID.randomUUID().toString(); channel.queueDeclare(queueName, true, false, false, null);
**//开启发布确认
channel.confirmSelect();**
//开始时间
long begin = System.currentTimeMillis(); for (int i = 0; i < 1000; i++) {
String message = i + "";
channel.basicPublish("", queueName, null, message.getBytes());
//单个消息马上进行确认
** boolean b = channel.waitForConfirms();**
if (b) {
System.out.println("消息发送成功!!!");
}
} //结束时间
long end = System.currentTimeMillis(); System.out.println("发送消息1000,单个发布确认用时: " + (end - begin) + " ms");
}

批量确认

与单个等待确认消息相比,先发布一批消息然后一起确认可以极大地提高吞吐量。

当然这种方式的缺点就是:当发生故障导致发布出现问题时,不知道是哪个消息出现问题了,我们必须将整个批处理保存在内存中,以记录重要的信息而后重新发布消息。

当然这种方案仍然是同步的,也一样阻塞消息的发布

/**
* 批量确认
*/
public static void publishBatchMessage() throws Exception {
Channel channel = RabbitMqUtils.getChannel();
//生命队列
String queueName = UUID.randomUUID().toString(); channel.queueDeclare(queueName, true, false, false, null);
**//开启发布确认
channel.confirmSelect();
//批量确认消息大小
int batchSize = 100;
//未确认消息个数
int outstandingMessageCount = 0;** //开始时间
long begin = System.currentTimeMillis(); for (int i = 0; i < 1000; i++) {
String message = i + "";
channel.basicPublish("", queueName, null, message.getBytes());
**outstandingMessageCount++;
//发送的消息 == 确认消息的大小后才批量确认
if (outstandingMessageCount == batchSize) {
channel.waitForConfirms();
outstandingMessageCount = 0;
}**
}
**//为了确保还有剩余没有确认消息 再次确认
if (outstandingMessageCount > 0) {
channel.waitForConfirms();
}**
//结束时间
long end = System.currentTimeMillis(); System.out.println("发送消息1000,批量发布确认100个用时: " + (end - begin) + " ms");
}

异步确认

它是利用回调函数来达到消息可靠性传递的,这个中间件也是通过函数回调来保证是否投递成功

/**
* 异步批量确认
*
* @throws Exception
*/
public static void publishAsyncMessage() throws Exception {
try (Channel channel = RabbitMqUtils.getChannel()) {
String queueName = UUID.randomUUID().toString();
channel.queueDeclare(queueName, false, false, false, null);
** //开启发布确认
channel.confirmSelect();
**
//线程安全有序的一个哈希表,适用于高并发的情况
//1.轻松的将序号与消息进行关联 2.轻松批量删除条目 只要给到序列号 3.支持并发访问
ConcurrentSkipListMap<Long, String> outstandingConfirms = new ConcurrentSkipListMap<>(); **//确认收到消息的一个回调**
//1.消息序列号
//2.multiple 是否是批量确认
//false 确认当前序列号消息
ConfirmCallback ackCallback = (sequenceNumber, multiple) -> {
if (multiple) {
//返回的是小于等于当前序列号的未确认消息 是一个 map
ConcurrentNavigableMap<Long, String> confirmed =
outstandingConfirms.headMap(sequenceNumber, true);
//清除该部分未确认消息
confirmed.clear();
} else {
//只清除当前序列号的消息
outstandingConfirms.remove(sequenceNumber);
}
};
//未确认消息的回调
ConfirmCallback nackCallback = (sequenceNumber, multiple) -> {
String message = outstandingConfirms.get(sequenceNumber);
System.out.println("发布的消息" + message + "未被确认,序列号" + sequenceNumber);
}; **//添加一个异步确认的监听器
//1.确认收到消息的回调
//2.未收到消息的回调
channel.addConfirmListener(ackCallback, nackCallback);** long begin = System.currentTimeMillis(); for (int i = 0; i < 1000; i++) {
String message = "消息" + i;
**//channel.getNextPublishSeqNo()获取下一个消息的序列号
//通过序列号与消息体进行一个关联,全部都是未确认的消息体
//将发布的序号和发布消息保存到map中
outstandingConfirms.put(channel.getNextPublishSeqNo(), message);**
channel.basicPublish("", queueName, null, message.getBytes());
}
long end = System.currentTimeMillis();
System.out.println("发布" + 1000 + "个异步确认消息,耗时" + (end - begin) + "ms");
} }

如何处理异步未确认消息

最好的解决的解决方案就是把未确认的消息放到一个基于内存的能被发布线程访问,适用于高并发的的队列。

比如说用 ConcurrentLinkedQueue 、这个队列在 confirm callbacks 与发布线程之间进行消息的传递。

ConcurrentSkipListMap

等等都可。

面试题

如何保证消息不丢失?

就市面上常见的消息队列而言,只要配置得当,我们的消息就不会丢失。

消息队列主要有三个阶段:

1。生产消息

2。存储消息

3。消费消息

1。生产消息

生产者发送消息至 Broker ,需要处理 Broker 的响应,不论是同步还是异步发送消息,同步和异步回调都需要做好 try-catch ,妥善的处理响应。

如果 Broker 返回写入失败等错误消息,需要重试发送。

当多次发送失败需要作报警,日志记录等。这样就能保证在生产消息阶段消息不会丢失。

2。存储消息

存储消息阶段需要在消息刷盘之后再给生产者响应,假设消息写入缓存中就返回响应,那么机器突然断电这消息就没了,而生产者以为已经发送成功了。

如果 Broker 是集群部署,有多副本机制,即消息不仅仅要写入当前 Broker ,还需要写入副本机中。

那配置成至少写入两台机子后再给生产者响应。这样基本上就能保证存储的可靠了。一台挂了还有一台还

在呢(假如怕两台都挂了..那就再多些)。

3。消费消息

我们应该在消费者真正执行完业务逻辑之后,再发送给 Broker 消费成功,这才是真正的消费了。

所以只要我们在消息业务逻辑处理完成之后再给 Broker 响应,那么消费阶段消息就不会丢失

总结:

1。生产者 需要处理好 Broker 的响应,出错情况下利用重试、报警等手段

2。Broker 需要控制响应的时机,单机情况下是消息刷盘后返回响应,集群多副本情况下,即发送至两个副本及以上的情况下再返回响应。

3。消费者 需要在执行完真正的业务逻辑之后再返回响应给 Broker

volatile 关键字的作用?

1。保证内存可见性

1.1 基本概念

可见性 是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结果,另一个线程马上就能够看到。

1.2 实现原理

当对非volatile变量进行读写的时候,每个线程先从主内存拷贝变量到CPU缓存中,如果计算机有多个CPU,每个线程可能在不同的CPU上被处理,这意味着每个线程可以拷贝到不同的CPU cache中。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,保证了每次读写变量都从主内存中读,跳过CPU cache这一步。当一个线程修改了这个变量的值,新值对于其他线程是立即得知的。

2。禁止指令重排序

2.1 基本概念

指令重排序是JVM为了优化指令、提高程序运行效率,在不影响单线程程序执行结果的前提下,尽可能地提高并行度。指令重排序包括编译器重排序和运行时重排序。在JDK1.5之后,可以使用volatile变量禁止指令重排序。针对volatile修饰的变量,在读写操作指令前后会插入内存屏障,指令重排序时不能把后面的指令重排序到内存屏

示例说明:
double r = 2.1; //(1)
double pi = 3.14;//(2)
double area = pi*r*r;//(3)
虽然代码语句的定义顺序为1->2->3,但是计算顺序1->2->3与2->1->3对结果并无影响,所以编译时和运行时可以根据需要对1、2语句进行重排序。

2.2 指令重排带来的问题

线程A中
{
context = loadContext();
inited = true;
} 线程B中
{
if (inited)
fun(context);
}
如果线程A中的指令发生了重排序,那么B中很可能就会拿到一个尚未初始化或尚未初始化完成的context,从而引发程序错误。

2.3 禁止指令重排的原理

olatile关键字提供内存屏障的方式来防止指令被重排,编译器在生成字节码文件时,会在指令序列中插入内存屏障来禁止特定类型的处理器重排序。

JVM内存屏障插入策略:

  • 每个volatile写操作的前面插入一个StoreStore屏障;
  • 在每个volatile写操作的后面插入一个StoreLoad屏障;
  • 在每个volatile读操作的后面插入一个LoadLoad屏障;
  • 在每个volatile读操作的后面插入一个LoadStore屏障。

3。适用场景

(1)volatile关键字无法同时保证内存可见性和原子性。加锁机制既可以确保可见性也可以确保原子性。

(2)volatile屏蔽掉了JVM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。

介绍一下Netty?

  1. Netty是一个高性能、异步事件驱动的NIO框架。

  2. 简化并优化了TCP和UDP套接字等网络编程,性能和安全等很多方面都做了优化。

3.支持多种协议,如FTP、SMTP、HTTP以及各种二进制和基于文本的传统协议。

在网络编程中,Netty是绝对的王者。

有很多开源项目都用到了Netty。

1。市面上很多消息推送系统都是基于Netty来做的。

2。我们常用的框架:Dubbo、RocketMQ、ES等等都用到了Netty。

使用Netty的项目统计:https://netty.io/wiki/related-projects.html

你好,我是yltrcc,日常分享技术点滴,欢迎关注我:ylcoder

RabitMQ 发布确认的更多相关文章

  1. rabbitmq的发布确认和事务 - 2207872494的个人空间

    rabbitmq的发布确认和事务 - 2207872494的个人空间   https://my.oschina.net/lzhaoqiang/blog/670749

  2. 【转载】rabbitmq的发布确认和事务

    地址:https://my.oschina.net/lzhaoqiang/blog/670749 摘要: 介绍confirm的工作机制.使用spring-amqp介绍事务以及发布确认的使用方式.因为事 ...

  3. rabbitmq的发布确认和事务

    摘要: 介绍confirm的工作机制.使用spring-amqp介绍事务以及发布确认的使用方式.因为事务以及发布确认是针对channel来讲,所以在一个连接中两个channel,一个channel可以 ...

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

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

  5. 【EasyNetQ】- 发布确认

    默认的AMQP发布不是事务性的,并不保证您的消息实际到达代理.AMQP确实指定了事务发布,但是使用RabbitMQ它非常慢,我们还没有通过EasyNetQ API支持它.对于高性能保证交付,建议您使用 ...

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

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

  7. redis、rabitmq对比

    redis.rabitmq对比 原文地址 简要介绍 RabbitMQ RabbitMQ是实现AMQP(高级消息队列协议)的消息中间件的一种,最初起源于金融系统,用于在分布式系统中存储转发消息,在易用性 ...

  8. MQTT——发布报文

    发布报文的知识点并不难,只是多.看过前面几章的读者们应该或多或少都认识服务质量QOS.发布报文跟他的联系最紧的.我们也清楚订阅报文里面虽然也有用到QOS,但是他却没有更进一步的联系.往下看就知道是什么 ...

  9. netcore开发windows普通服务(非Web)并一键发布到服务器

    如何开发并一键发布WindowsService项目(netcore普通项目) netcore下开发windows服务如果是web项目的话,由于aspnetcore本身是支持的,把默认的host.Run ...

随机推荐

  1. validator API文档

    如何使用 引入 <script src="../node_modules/jquery/dist/jquery.js"></script> <scri ...

  2. spring总览

    Spring 概述 1. 什么是spring? Spring 是个java企业级应用的开源开发框架.Spring主要用来开发Java应用,但是有些扩展是针对构建J2EE平台的web应用.Spring  ...

  3. Exchange日志清理

    1.清理日志--完整备份 Exchange Server 2013被部署在Windows Server 2012 及以上版本的操作系统中,使用操作系统内的"Windows Server Ba ...

  4. (ICONIP2021)On the Unreasonable Effectiveness of Centroids in Image

    目录 摘要 1.引言 2.提出的方法 2.1 CentroidTripletloss 2.2 聚合表示 3.实验 3.1 数据集 3.2 应用细节 3.3 Fashion检索结果 3.4 行人再识别结 ...

  5. js知识梳理3:创建对象的模式探究

    写在前面 注:这个系列是本人对js知识的一些梳理,其中不少内容来自书籍:Javascript高级程序设计第三版和JavaScript权威指南第六版,感谢它们的作者和译者.有发现什么问题的,欢迎留言指出 ...

  6. OpenHarmony标准设备应用开发(一)——HelloWorld

    (以下内容来自开发者分享,不代表 OpenHarmony 项目群工作委员会观点) 邢碌 本文是 OpenAtom OpenHarmony(以下简称"OpenHarmony")标准设 ...

  7. ES Bridge跨链桥服务升级,新增BSC跨链网络

    3月15日,Equal Sign Bridge(ES Bridge)跨链桥宣布新增BSC跨链网络,方便更多用户参与到ES Bridge的建设与发展,未来还将持续拓展更多的主流跨链币种,提升各链间的互操 ...

  8. 从小白到侠客的 Windows 快捷键宝典

    "天下 武功,唯快不破."你是否羡慕过那些电脑键盘侠客,他们操作起电脑行云流水,任务完成的又快又准.这到底是怎么做到的呢?我们是否也能向他们一样达到把键盘操作熟记于心呢?那就跟着笔 ...

  9. IDEA SpringBoot-Mybatis-plus 实现增删改查(CRUD)

    上一篇: IDEA SpringBoot-Mybatis实现增删改查(CRUD) 下一篇:Intellij IDEA 高效使用教程 (插件,实用技巧) 最好用的idea插件大全 一.前言 Mybati ...

  10. Git批量下载MODIS数据

    1.download.sh获取 EarthData(需注册账号)中获取MODIS的产品类型.地理范围.时间年份等,进入下载页面Download Status 下载点击得到_download.sh 文件 ...