通过剖析,我们知道了prefetch过大随意马虎导致内存溢出,prefetch过小又会导致消费吞吐量过低,以是在实际项目中须要慎重测试和设置。
这篇文章,我们转移到中间件的生产端,一起来看看如何担保投递到MQ的数据不丢失。
如果投递出去的在网络传输过程中丢失,或者在RabbitMQ的内存中还没写入磁盘的时候宕机,都会导致生产端投递到MQ的数据丢失。
而且丢失之后,生产端自己还感知不到,同时还没办法来补救。
下面的图就展示了这个问题。
以是本文呢,我们就来逐步剖析一下。
二、担保投递不丢失的confirm机制
实在要办理这个问题,相信大家看过之前的消费端ack机制之后,也都猜到了。
很大略,便是生产端(比如上图的订单做事)首先须要开启一个confirm模式,接着投递到MQ的,如果MQ一旦将持久化到磁盘之后,必须也要回传一个confirm给生产端。
这样的话,如果生产真个做事吸收到了这个confirm,就知道是已经持久化到磁盘了。
否则如果没有吸收到confirm,那么就解释这条半路可能丢失了,此时你就可以重新投递到MQ去,确保不要丢失。
而且一旦你开启了confirm模式之后,每次投递也同样是有一个delivery tag的,也是起到唯一标识一次投递的浸染。
这样,MQ回传ack给生产真个时候,会带上这个delivery tag。你就知道详细对应着哪一次投递了,可以删除这条。
此外,如果RabbitMQ吸收到一条之后,结果内部出错创造无法处理这条,那么他会回传一个nack给生产端。此时你就会感知到这条可能处理有问题,你可以选择重新再次投递这条到MQ去。
或者另一种情形,如果某条很永劫光都没给你回传ack/nack,那可能是极度意外情形发生了,数据也丢了,你也可以自己重新投递到MQ去。
通过这套confirm机制,就可以实现生产端投递不会丢失的效果。大家来看看下面的图,一起来感想熏染一下。
三、confirm机制的代码实现
下面,我们再来看看confirm机制的代码实现:
四、confirm机制投递的高延迟性
这里有一个很关键的点,便是一旦启用了confirm机制投递到MQ之后,MQ是不担保什么时候会给你一个ack或者nack的。
由于RabbitMQ自己内部将持久化到磁盘,本身便是通过异步批量的办法来进行的。
正常情形下,你投递到RabbitMQ的都会先驻留在内存里,然后过了几百毫秒的延迟韶光之后,再一次性批量把多条持久化到磁盘里去。
这样做,是为了兼顾高并发写入的吞吐量和性能的,由于假如你来一条就写一次磁盘,那么性能会很差,每次写磁盘都是一次fsync逼迫刷入磁盘的操作,是很耗时的。
以是正是由于这个缘故原由,你打开了confirm模式之后,很可能你投递出去一条,要间隔几百毫秒之后,MQ才会把写入磁盘,接着你才会收到MQ回传过来的ack,这个便是所谓confirm机制投递的高延迟性。
大家看看下面的图,一起来感想熏染一下。
五、高并发下如何投递才能不丢失
大家可以考虑一下,在生产端高并发写入MQ的场景下,你会面临两个问题:
1、你每次写一条到MQ,为了等待这条的ack,必须把保存到一个存储里。并且这个存储不建议是内存,由于高并发下是很多的,每秒可能都几千乃至上万的投递出去,的ack要等几百毫秒的话,放内存可能有内存溢出的风险。
2、绝对不能以同步写 + 等待ack的办法来投递,那样会导致每次投递一个都同步壅塞等待几百毫秒,会导致投递性能和吞吐量大幅度低落。针对这两个问题,相对应的方案实在也呼之欲出了。
首先,用来临时存放未ack的存储须要承载高并发写入,而且我们不须要什么繁芜的运算操作,这种存储首选绝对不是MySQL之类的数据库,而建议采取kv存储。kv存储承载高并发能力极强,而且kv操作性能很高。
其次,投递之后等待ack的过程必须是异步的,也便是类似上面那样的代码,已经给出了一个初步的异步回调的办法。
投递出去之后,这个投递的线程实在就可以返回了,至于每个的异步回调,是通过在channel注册一个confirm监听器实现的。
收到一个ack之后,就从kv存储中删除这条临时;收到一个nack之后,就从kv存储提取这条然后重新投递一次即可;也可以自己对kv存储里的做监控,如果超过一定时长没收到ack,就主动重发。
大家看看下面的图,一起来体会一下:
六、中间件全链路100%数据不丢失能做到吗?
到此为止,我们已经把生产端和消费端如何担保不丢失的干系技能方案结合RabbitMQ这种中间件都给大家剖析过了。
实在,架构思想是通用的, 无论你用的是哪一种MQ中间件,他们供应的功能是不太一样的,但是你都须要考虑如下几点:
1.生产端如何担保投递出去的不丢失:在半路丢失,或者在MQ内存中宕机导致丢失,此时你如何基于MQ的功能担保不要丢失?
2.MQ自身如何担保不丢失:最少须要让MQ对是有持久化到磁盘这个机制。
3.消费端如何担保消费到的不丢失:如果你处理到一半消费端宕机,导致丢失,此时怎么办?
目前来说,我们初步的借着RabbitMQ举例,已经把从前到后一整套技能方案的事理、设计和实现都给大家剖析了一遍了。
但是此时真的能做到100%数据不丢失吗?恐怕未必,大家再考虑一下个分外的场景。
生产端投递了到MQ,而且持久化到磁盘并且回传ack给生产端了。
但是此时MQ还没投递给消费端,结果MQ支配的机器溘然宕机,而且由于未知的缘故原由磁盘破坏了,直接在物理层面导致MQ持久化到磁盘的数据找不回来了。
这个大家千万别以为是开玩笑的,大家如果留神留神行业新闻,这种磁盘破坏导致数据丢失的是真的有的。
那么此时纵然你把MQ重启了,磁盘上的数据也丢失了,数据是不是还是丢失了?
你说,我可以用MQ的集群机制啊,给一个数据做多个副本,比如后面我们就会给大家剖析RabbitMQ的镜像集群机制,确实可以做到数据多副本。
但是纵然数据多副本,一定可以做到100%数据不丢失?
比如说你的机房溘然碰着地震,结果机房里的机器全部没了,数据是不是还是全丢了?
说这个,并不是说要抬杠。而是见告大家,技能这个东西,100%都是理论上的期望。
该当说,我们凡事都朝着100%去做,但是理论上是不可能完备做到100%担保的,可能便是做到99.9999%的可能性数据不丢失,但是还是有千万分之一的概率会丢失。
当然,从实际的情形来说,能做到这耕田地,实在基本上已经基本数据不会丢失了。
------------- END -------------
其余推举儒猿教室的1元系列课程给您,欢迎加入一起学习~
互联网Java工程师口试突击课(1元专享)SpringCloudAlibaba零根本入门到项目实战(1元专享)亿级流量下的电商详情页系统实战项目(1元专享)Kafka中间件内核源码精讲(1元专享)12个实战案例带你玩转Java并发编程(1元专享)Elasticsearch零根本入门到精通(1元专享)基于Java手写分布式中间件系统实战(1元专享)基于ShardingSphere的分库分表实战课(1元专享)