每日一句

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

概述

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. template7入门教程及对它的一些看法

    template7是framework7的内置模板引擎,在此之前使用过jquery-tmpl,不过刚刚打开github看了下,已经停止更新,并且将要被JsRender所替代.妹的,JsRender又是 ...

  2. Kurento安装与入门08——Group Call

    Group Call 本示例展示了一个视频聊天室的功能,不同的聊天室之间互相隔离. 官网文档 Group Call 首先从github上获取代码(如果已经获取可以跳过,获取的代码已经包括后面的示例代码 ...

  3. 探讨:微信小程序应该如何设计

    微信小程序公测后,开发者非常热情,都有很高的期待,都想抓住这一波红利.但是热情背后需要冷静,我们需要搞清楚两个问题: 微信想要我们做什么?微信小程序可以做什么? 微信想要我们做什么? 首先来弄清楚微信 ...

  4. 使用electron制作满屏心特效

    图片被压缩了 看起来很难看 主进程代码 import {BrowserWindow, app, ipcMain} from 'electron' createWindow(); ipcMain.on( ...

  5. 如何跨线程访问Winform中的UI元素

    如何跨线程访问Winform中的UI元素 假如制作一个Socket聊天应用程序,很可能会用到多线程: 例如为Receive方法开辟单独一个线程,例如为Receive方法开辟单独一个线程(后台运行的线程 ...

  6. 【java】密码检查

    [问题描述] 开发一个密码检查软件,密码要求: 长度超过8位 包括大小写字母.数字.其它符号,以上四种至少三种 不能有相同长度超2的子串重复 [输入形式] 一组或多组长度超过2的子符串.每组占一行 [ ...

  7. AcWing 1222. 密码脱落

    题目链接 题目描述: X星球的考古学家发现了一批古代留下来的密码. 这些密码是由A.B.C.D 四种植物的种子串成的序列. 仔细分析发现,这些密码串当初应该是前后对称的(也就是我们说的镜像串). 由于 ...

  8. 数据库篇:mysql事务原理之MVCC视图+锁

    前言 数据库的事务特性 数据并发读写时遇到的一致性问题 mysql事务的隔离级别 MVCC的实现原理 锁和隔离级别 关注公众号,一起交流,微信搜一搜: 潜行前行 1 数据库的事务特性 原子性:同一个事 ...

  9. Visual Studio 打包和安装 exe

    # Visual Studio 打包和安装 exe > **小型项目(无复杂的库)** //VS2022 作为演示平台   > 1.解决方案配置 = Release   > 2.解决 ...

  10. Ubuntu 安装cmake

    Ubuntu 安装 cmake 官网 https://cmake.org 下载地址 https://cmake.org/download/ 参考文档 -<ubuntu下更新cmake版本> ...