每日一句

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

概述

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. 【EasyNetQ】- 发布确认

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

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

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

  6. redis、rabitmq对比

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

  7. MQTT——发布报文

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

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

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

  9. RabbitMQ 消息确认机制以及lazy queue+ disk消息持久化

    一:Basic的一些属性,一些方法 1. 消费端的确认 自动确认: message出队列的时候就自动确认[broke] basicget... 手工确认: message出队列之后,要应用程序自己去确 ...

  10. 10,EasyNetQ-发布确认

    默认的AMQP发布不是事务性的,并且不能保证您的消息实际上会到达代理. AMQP指定了一个事务性发布,但是对于RabbitMQ来说,它非常慢,我们还没有通过EasyNetQ API支持. 对于高性能保 ...

随机推荐

  1. Node.js之sails框架

    先开一坑,有空更新,记录最近钉钉项目上对node及sails框架的学习记录和理解

  2. LINUX下编译源码时所需提前安装的常用依赖包列表

    yum -y install gcc gcc-c++ autoconf libjpeg libjpeg-devel libpng libpng-devel freetype freetype-deve ...

  3. 从为什么String=String谈到StringBuilder和StringBuffer

    前言 有这么一段代码: public class TestMain { public static void main(String[] args) { String str0 = "123 ...

  4. [p2p]UDP用打洞技术穿透NAT的原理与实现

    首先先介绍一些基本概念:            NAT(Network Address             Translators),网络地址转换:网络地址转换是在IP地址日益缺乏的情况下产生的, ...

  5. Hibernate征途(七)之复合主键映射和集合映射

    把这两种映射放到一起说,是因为这两种映射不像前面的复用型映射.数量和方向型映射那么分类鲜明,所以放到了这个“其他”里面. 复合主键映射 在关系模型中,复合主键和其他的主键方式没有很大区别,但是反映到对 ...

  6. VC自动与Internet时间服务器同步更新

    在VCKBASE.CSDN里挖了许久的坟,才找到一些有点用的资料,最后自己整理出这样的个函数,方面VC实现时间同步,多的不说,自己看源码,根据自己的需要可以适当修改源码: #include <W ...

  7. NVelocity模板引擎学习笔记

    NVelocity模板引擎学习笔记 学习模板引擎有一段时间现在做一些总结

  8. Maven学习(五)

    使用Maven构建多模块项目 一般的web项目构成: 建立解决方案目录parent 首先使用命令进入到我们需要建立maven项目的目录: mvn archetype:generate -DgroupI ...

  9. Android的Context Manager(服务管理器)源码剖析-android学习之旅(99)

    Context Manager介绍 Context Manager对应的进程是servicemanager进程,它先于Service Server和服务客户端运行,进入接收IPC数据的待机状态,处理来 ...

  10. C#中异步使用及回调

    1. 一句话理解异步 我叫你去吃饭,叫完你不去,那我就会一直等你,直到你和我一起去吃饭.这叫同步! 我叫你去吃饭,叫完不管你去不去,我都不会等你,我自己去吃饭.这叫异步! 2. 异步使用 static ...