会员登录 - 用户注册 - 设为首页 - 加入收藏 - 网站地图 面试基操:MQ怎么保障消息可靠性?!

面试基操:MQ怎么保障消息可靠性?

时间:2025-11-05 02:13:13 来源:益强数据堂 作者:系统运维 阅读:509次

大家好,面试欢迎来到Tlog4J课堂,基操我是保障Jensen。

面试官:在MQ的消息性整个消息生产消费过程中,如何保障消息100%被消费?可靠

候选人:MQ有个ACK机制,确保消息100%被消费。面试

面试官:好吧,基操可以回去等通知了……

这道面试题在考察MQ组件时算是保障老生常谈了,不知道你是消息性如何回答的?

我们平时都在使用MQ,但使用技术框架只是可靠第一步,去弄明白它的面试底层原理、深挖技术真相,基操才是保障每一位IT从业者的基操。

这里说明一点,消息性想要回答好面试官的可靠问题,最好还是要有金字塔思维——金字塔思维就是从不同维度上来思考问题的一种方式,不重不漏,集体穷尽。

MQ作为异步通讯的消息中间件,其功能除了解耦生产者与消费者,b2b供应网还能用于大流量的削峰填谷,解决业务的最终一致性问题,那么消息的“可靠性”就显得尤为重要了,比如说商品出库后的库存数据通过MQ同步到财务系统,如果消息的可靠性没有保障,那财务系统的存货成本分析数据就无法有效支撑财务团队。

准确来说,我们需要保障MQ消息的可靠性,需要从三个层面/维度解决:生产者100%投递、MQ持久化、消费者100%消费,这里的100%消费指的是消息不少消费,也不多消费。

由于MQ是基础网络通讯的中间件,网络通讯必然因丢包、网络抖动等原因产生数据丢失,MQ组件本身也会由于宕机或软件崩溃而中止服务,从而造成数据丢失,那么我们就需要从这两个根本原因着手补偿,这里科普一下RabbitMQ和Kafka是怎么解决的云服务器提供商

RabbitMQ

这里我必须先提一提RabbitMQ的消息协议——AMQP(Advanced Message Queuing Protocol,高级消息队列协议),在面试时我经常问候选人一个问题:RabbitMQ用的是什么消息协议?大部分候选人是回答不出来AMQP的,更不用说AMQP模型是如何设计的了。

在服务器中,三个主要功能模块连接成一个处理链完成预期的功能:

Exchange:接收发布应用程序发送的消息,并根据一定的规则将这些消息路由到消息队列。Queue:存储消息,直到这些消息被消费者安全处理完为止。Binding:定义了exchange和queue之间的关联,提供路由规则。

使用这个模型我们可以很容易地模拟出存储转发队列和主题订阅这些典型的消息中间件概念。

接下来我们看看RabbitMQ的消息确认机制是如何保障消息可靠性的。

一、生产者端通过API将信道(channel)设置为confirm模式,则每条消息会被分配一个唯—ID。如果消息投递成功,企商汇也就是说消息已经到达broker了,信道会发送ack给生产者,回调ConfirmCallback接口,带上唯一ID。如果发生错误导致消息丢失,比如通过某个RoutingKey无法路由到某个Queue,则会发送nack给生产者,回调ReturnCallback接口,并带上唯一ID和异常信息。ack和nack只有一个被触发,只触发一次,而且是异步执行,意味着生产者不需要等待,可以继续发送新消息。二、消费者端声明队列时,指定noack=false, 表示消费者不会自动提交ack,broker会等待消费者手动返回ack、才会删除消息,否则立刻删除。broker的ack没有超时机制,只会判断链接是否断开,如果断开了(比如消费者处理消息过程中宕机),消息会被重新发送,所以消费者要做好消息幂等性处理。三、MQ本身

通常来说,消息是在内存中存储通讯的,而基于内存的都是会有数据丢失的问题产生,服务一重启,数据就随之销毁。

在RabbitMQ中对数据的持久化有三方面:交换机持久化、队列持久化、消息持久化。

交换机持久化:exchange_declare创建交换机时通过参数durable=true指定,如:channel.exchangeDeclare(exchangeName, “direct/topic/header/fanout”, true);第三个参数就是设置durable值。队列持久化:queue_declare创建队列时通过参数durable=true指定,如:channel.queueDeclare("queue.persistent.name", true, false, false, null),第二个参数就是设置durable值。消息持久化:new AMPQMessage创建消息时通过参数指定,如:channel.basicPublish("exchange.persistent", "persistent", MessageProperties.PERSISTENT_TEXT_PLAIN, "persistent_test_message".getBytes()),或者设置参数deliveryMode=2来指定:AMQP.BasicProperties.Builder builder = new AMQP.BasicProperties.Builder();builder.deliveryMode(2)。

上面只是说了API层的实现,那RabbitMQ底层又是怎么做消息持久化的呢?

如果指定了持久化参数,它们会以append的方式写文件,会根据文件大小(默认16M)自动切割,生成新的文件,RabbitMQ启动时会创建两个进程,一个负责持久化消息的存储,另一个负责非持久化消息的存储(当内存不够时会用到)。

消息存储时,会在一个叫ets的表中记录消息在文件中的映射以及相关信息(包括ID、偏移量、有效数据、左边文件、右边文件),消息读取时根据该信息到文件中读取,同时更新信息。

消息删除时只从ets删除,变为垃圾数据,当垃圾数据超出比例(默认50%),并且文件数达到3个,就会触发垃圾回收:锁定左右两个文件,整理左边文件有效数据、将左边文件有效数据写入左边,更新文件信息,删除右边,完成合并;当一个文件的有效数据等于0时,删除该文件。

写入文件前先写入buffer缓冲区,如果buffer已满,则写入文件,注意,此时只是操作系统的页存,还没落盘。

每隔25ms刷一次磁盘(比如Linux中的fsync命令),不管buffer(fd的读、写缓存区)满没满,都将buffer和页存中的数据落盘。

还有另外一种落盘机制:每次消息写入后,如果没有后续写入请求,则直接刷盘。

此外,RabbitMQ除了消息确认机制,还有另一种方式——使用事务消息:消息生产端发送commit命令,MQ同步返回commit ok命令,这种方式由于需要同步阻塞等待MQ返回是否投递成功,才能执行别的操作,性能较差,因此不推荐使用。

Kafka

Kafka在MQ领域以性能高、吞吐能力强、消息堆积能力强等等优势称著,常常用于日志收集、消息系统、用户活动跟踪、运营指标、流式处理等等场景,讲之前先简单聊聊Kafka的架构设计:

Consumer Group:消费者组,消费者组内每个消费者负责消费不同分区的数据,提高消费能力,这是逻辑上的一个订阅者。Topic:可以理解为一个队列,Topic将消息分类,生产者和消费者面向的是同一个Topic。

Partition:为了实现扩展性,提高并发能力,一个Topic以多个Partition的方式分布到多个Broker上,每个Partition是一个有序的队列,一个Topic的每个Partition都有若干个副本(Replica),一个Leader和若干个Follower;生产者发送数据的对象,以及消费者消费数据的对象,都是通过Leader,Follower负责实时从Leader中同步数据,保持和Leader数据的同步;当Leader发生故障时,某个Follower还会成为新的Leader。

一、生产者端

Kafka消息发送端有个ACK机制。

设置ack参数:ack=0,表示不重试,Kafka不需要返回ack,极有可能各种原因造成丢失;ack=1,表示Leader写入成功就返回ack了,Follower不一定同步成功;ack=all或ack=-1,表示ISR列表中的所有Follower同步完成再返回ack。设置参数unclean.leader.election.enable:false,禁止选举ISR以外的Follower为Leader,只能从ISR列表中的节点中选举Leader;可能会牺牲Kafka的可用性,但是能够提高消息的可靠性。重试机制,设置tries > 1,表示消息重发次数。设置最小同步副本数min.insync.replicas > 1,没满足该值前,Kafka不提供读写服务,写操作会异常。

通过设置最小同步副本数和ACK机制,可以让MQ在性能与可靠性上达到平衡。

二、消费者端

手工提交offset(偏移量):Kafka消费者在拉取消息后,默认会自动提交offset,由于消费者每次都会根据offset来消费消息的,如果消费者处理业务失败,实际上我们是要重新消费的,所以我们要在消息处理成功后再手工提交offset,确认消息能够成功消费。

三、MQ本身

很简单,通过减小broker刷盘间隔来实现高可靠。

要深究其原理,得从Kafka的持久化机制来看。

磁盘的顺序读写:与RabbitMQ不同,Kafka是基于磁盘读写的,那为什么Kafka的吞吐量还这么大呢?原因是Kafka的读写是用顺序读写的,不需要寻址随机读写,而由于是用磁盘来写数据,消息堆积能力必然比内存型的RabbitMQ更强

利用了操作系统的零拷贝技术:避免CPU将数据从一块存储拷贝到另外一块存储,关于零拷贝这里不详述,与Java应用不同,Kafka的消息不需要在用户缓冲区处理磁盘数据再返回,所以才能用零拷贝技术。分区分段+索引:Kafka的消息实际上分布存储在一个一个小的segment中的,每次文件读写也是直接操作segment,为了进一步优化查询,Kafka又默认为分段后的数据文件建立了索引文件(就是文件系统上的.index文件),这种分区分段+索引的设计,不仅提升了数据读取的效率,同时也提高了数据操作的并行度(类似ConcurrentHashMap的分段锁机制)。批量压缩&批量读写:多条消息一起压缩进行传输(比如gzip格式)与读写,节省带宽。直接操作page cache:虽然Kafka是Java写的,也基于JVM运行,但Kafka的消息读写是直接操作操作系统页存的,而不是在JVM的堆内存,这样就避免JVM的GC耗时及对象创建耗时,且读写速度更高,JVM进程重启缓存也不会丢失。

理解了Kafka的持久化机制是直接读写页存+定时刷盘的方式,我们只需要设置刷盘策略即可在性能与可靠性上权衡。

Kafka提供3个参数来优化刷盘机制:

log.flush.interval.messages //多少条消息刷盘1次。log.flush.interval.ms //隔多长时间刷盘1次。log.flush.scheduler.interval.ms //周期性的刷盘。总结一下

关于框架类的面试题,最重要是得掌握技术框架的底层实现原理、适用场景,基本上回答出这两方面就OK了,其它奇奇怪怪的细节问题要是答不出来,咱就引导面试官说出自己对框架的理解即可,毕竟细节的问题太多了。

那怎么才算掌握呢?起码能通过框架的特性,根据需要实现一个简易版本,比如说自己实现一个Spring框架、实现一个MQ组件等等。

(责任编辑:数据库)

最新内容
推荐内容
  • 电脑吃鸡按键教程(了解按键设置,轻松成为绝地求生大神)
  • 电脑小报详细教程步骤(从零基础到高级技能,轻松学习电脑使用方法)
  • 假如您的操作系统密码忘了怎么办呢 操作系统可没有忘记密码这个选项,怎么办 您可以会毫不犹豫的说这种事情是不可能发生的,但凡是不怕一万就怕个万一。我们就假设你在装Ubuntu的求快不在意随便输入一串字符密码后来启动的时候又忘了,或者比较久没开机把用户密码给忘了或其他什么原因。这时候您可以首先想的是花几十分钟重装一次系统。但这样又费时间,又把原来的很多数据都丢失掉。但有一种方法可以在您的电脑启动时经过简单的5步就可以修改您忘记的密码,是不是很方便 1. 打开电脑,当屏幕上出现Press Esc to enter grub message时立刻按下Esc键。2. 然后就会出现三个选项,您选择recovery mode按回车(如图1所示)。3. 您的电脑将会从启动进入到shell,当出现命令提示符的时候输入passwd username这里的username就是您的用户名,假如您连用户名都忘了的话,不急有办法,您可以先输入ls /home查看用户名,再输入以上命令。4. 出现提示符时就可以直接输入您的新密码了,再确认一次。5. 这时再输入shutdown -r now重启您的电脑,用您的新密码进入啦。相比之下,这么简单的方法要对重装系统方便得多。图1 当进入设置时选择recovery mode
  • 先介绍一下环境:虚拟机:debian7.2.0主机: Win7由于有时候无法联网就需要离线安装软件包,可以把debian官网上列出的几个DVD镜像都下下来,其实主要是DVD1,这样就不需要每次安装软件都得联网。下面是我从Debian官网上下下来的DVD镜像:debian-7.2.0-i386-DVD-1debian-7.2.0-i386-DVD-2debian-7.2.0-i386-DVD-3在每张DVD中都有一个README.txt,其中有如下说明:About This Disc ===============   This disc is labeled        Debian GNU/Linux 7.2.0 Wheezy - Official i386 DVD Binary-1        20131012-12:56   which means that this disc is number 1 of a set of 10 discs It  contains programs (binaries) for `i386 computers.   The programs on the Binary discs are ordered by popularity. The  Binary-1 disc contains the most popular programs and the installation  tools; it is possible to install and run Debian with only the Binary-1  disc. The other discs, up to Binary-10, contain mostly  special-interest programs.   The Release Notes for wheezy are available on the Debian web site. 在DVD1中有关于Debian的安装手册,中文的手册路径是doc/manual/zh_CN/,其中也有关于DVD安装的介绍CD(和 DVD) 里面包含的软件包按照流行程度排列次序。也就是说,第一张光盘里面是最常用的软件,而最后一张光盘里面包含的是很少有人用到的软件。这也意味着购买或者下载刻录所有 CD 是一种浪费,因为大部分光盘您根本不会用到。多数情况下,只需要使用前 3 到 8 张 CD,然后从网络镜像安装其他要用的软件包。DVD 的情况类似:只要一张,或者前两张 DVD 就能满足您大部分要求。根据经验,常见的桌面安装(使用 GNOME 桌面环境)只需要前三张 CD。其他的桌面环境(KDE 或 Xfce),就需要额外的 CD。而第一张 DVD 就可以轻松地满足所有的三种桌面环境。 下面开始安装:先给虚拟机添加三个虚拟光驱,将刚下载的三个DVD镜像装入如下图所示:(注意:由于已经安装了Debian7.2,所以我把DVD1放在了第二个,即第一IDE控制器从通道,将DVD2放在了第一IDE控制器主通道上,目的是防止每次启动时都自动进入Debian安装界面,DVD1中也有很多软件包)由于添加了三个虚拟光驱,需要修改/etc/fstab,修改为:同时在/media目录下新建一个目录cdrom2,重启后系统就自动把DVD2挂载到/media/cdrom0,将DVD1挂载到/media/cdrom1,将DVD3挂载到/media/cdrom2下面,如下图:挂载好之后,就可以修改/etc/apt/sources.list了,添加如下内容:假如不知道路径后面写什么可以执行 apt-cdrom add,系统会自动搜索cdrom,并自动填写/etc/apt/sources.list文件,可以看他是怎么写的,使用apt-cdrom add这种方法,有事会出现无法找到软件包的问题。最后就可以执行 apt-get update了。
  • 神舟电脑支架拍照教程(利用神舟电脑支架,发挥你的摄影潜能)
  • 假如你使用大显示器或者多个显示器,Ubuntu 13.10的全局菜单将不便于操作。假如你想禁用全局菜单,你可以很容易实现。使用快捷键(Ctrl+Alt+T)打开终端并输入下面的命令:复制代码代码如下:sudo apt-get remove indicator-appmenu系统将会移除indicator-appmenu并提示释放多少空间,需要输入y确定并继续,关闭终端后会发现全局菜单已经消失,但firefox的全局菜单还需要单独修改才能禁用全局菜单。打开firefox并在地址栏中输入about:config,此时将出现警告提示,点击I’ll be careful, I promise!按钮,搜索unity会看到ui.use_unity_menubar这项,其默认Value值为true,将其改为flase即可禁用全局菜单。
热点内容