大家好,我是 yes。
最近我始终扎在音讯队列实现细节之中无法自拔,曾经写了 3 篇 Kafka 源码剖析
,还剩很多没肝完。之前还存着RocketMQ 源码剖析
还没整顿。今儿临时先跳进去盘一盘大方向上的音讯队列有哪些外围留神点。
外围点有很多,为了更 贴合理论场景,我从常见的面试问题动手:
- 如何保障音讯不失落?
- 如果解决反复音讯?
- 如何保障音讯的有序性?
- 如果解决音讯沉积?
当然在分析这几个问题之前须要简略的介绍下 什么是音讯队列 ,音讯队列常见的一些 根本术语和概念。
接下来进入注释。
什么是音讯队列
来看看维基百科怎么说的,顺带学学英语这波不亏:
In computer science, message queues and mailboxes are software-engineering components typically used for inter-process communication (IPC), or for inter-thread communication within the same process. They use a queue for messaging – the passing of control or of content. Group communication systems provide similar kinds of functionality.
翻译一下:在计算机科学畛域,音讯队列和邮箱都是软件工程组件,通常用于过程间或同一过程内的线程通信。它们通过队列来传递音讯 - 传递管制信息或内容,群组通信零碎提供相似的性能。
简略的概括下下面的定义:音讯队列就是一个应用队列来通信的组件。
下面的定义没有错,但就当初而言咱们日常所说的 音讯队列经常指代的是消息中间件,它的存在不仅仅只是为了通信这个问题。
为什么须要音讯队列
从实质上来说是因为互联网的疾速倒退,业务一直扩张,促使技术架构须要一直的演进。
从以前的单体架构到当初的微服务架构,成千盈百的服务之间互相调用和依赖。从互联网初期一个服务器上有 100 个在线用户曾经很了不得,到当初坐拥 10 亿日活的微信。咱们须要有一个「货色」来解耦服务之间的关系、管制资源正当合时的应用以及缓冲流量洪峰等等。
音讯队列就应运而生了。它罕用来实现:异步解决、服务解耦、流量管制。
异步解决
随着公司的倒退你可能会发现你我的项目的 申请链路越来越长,例如刚开始的电商我的项目,能够就是粗犷的扣库存、下单。缓缓地又加上积分服务、短信服务等。这一路同步调用下来客户可能等急了,这时候就是音讯队列退场的好时机。
调用链路长、响应就慢了,并且绝对于扣库存和下单,积分和短信没必要这么的 “ 及时 ”。因而只须要在下单完结那个流程,扔个音讯到音讯队列中就能够间接返回响应了。而且积分服务和短信服务能够并行的生产这条音讯。
能够看出音讯队列能够 缩小申请的期待,还能让服务异步并发解决,晋升零碎总体性能。
服务解耦
下面咱们说到加了积分服务和短信服务,这时候可能又要来个营销服务,之后领导又说想做个大数据,又来个数据分析服务等等。
能够发现订单的上游零碎在一直的裁减,为了投合这些上游零碎订单服务须要常常地批改,任何一个上游零碎接口的变更可能都会影响到订单服务,这订单服务组可疯了,真 ·「外围」项目组。
所以个别会选用音讯队列来解决零碎之间耦合的问题,订单服务把订单相干音讯塞到音讯队列中,上游零碎谁要谁就订阅这个主题。这样订单服务就解放啦!
流量管制
想必大家都听过「削峰填谷」,后端服务相对而言都是比拟「弱」的,因为业务较重,解决工夫较长。像一些例如秒杀流动爆发式流量打过去可能就顶不住了。因而须要引入一个中间件来做缓冲,音讯队列再适宜不过了。
网关的申请先放入音讯队列中,后端服务尽本人最大能力去音讯队列中生产申请。超时的申请能够间接返回谬误。
当然还有一些服务特地是某些后台任务,不须要及时地响应,并且业务解决简单且流程长,那么过去的申请先放入音讯队列中,后端服务依照本人的节奏解决。这也是很 nice 的。
下面两种状况别离对应着生产者生产过快和消费者生产过慢两种状况,音讯队列都能在其中施展很好的缓冲成果。
留神
引入音讯队列诚然有以上的益处,然而多引入一个中间件零碎的稳定性就降落一层,运维的难度贬低一层。因而要 权衡利弊 , 零碎是演进的。
音讯队列基本概念
音讯队列有两种模型:队列模型 和公布 / 订阅模型。
队列模型
生产者往某个队列外面发送音讯,一个队列能够存储多个生产者的音讯,一个队列也能够有多个消费者,
然而消费者之间是竞争关系,即每条音讯只能被一个消费者生产。
公布 / 订阅模型
为了解决一条音讯能被多个消费者生产的问题 ,公布 / 订阅模型就来了。该模型是将音讯发往一个Topic
即主题中,所有订阅了这个 Topic
的订阅者都能生产这条音讯。
其实能够这么了解,公布 / 订阅模型等于咱们都退出了一个群聊中,我发一条音讯,退出了这个群聊的人都能收到这条音讯。
那么队列模型就是一对一聊天,我发给你的音讯,只能在你的聊天窗口弹出,是不可能弹出到他人的聊天窗口中的。
讲到这有人说,那我一对一聊天对每个人都发同样的音讯不就也实现了一条音讯被多个人消费了嘛。
是的,通过多队列全量存储雷同的音讯,即数据的冗余能够实现一条音讯被多个消费者生产。RabbitMQ
就是采纳队列模型,通过 Exchange
模块来将音讯发送至多个队列,解决一条音讯须要被多个消费者生产问题。
这里还能看到假如群聊里除我之外只有一个人,那么此时的公布 / 订阅模型和队列模型其实就一样了。
小结一下
队列模型每条音讯只能被一个消费者生产,而公布 / 订阅模型就是为让一条音讯能够被多个消费者生产而生的,当然队列模型也能够通过音讯全量存储至多个队列来解决一条音讯被多个消费者生产问题,然而会有数据的冗余。
公布 / 订阅模型兼容队列模型,即只有一个消费者的状况下和队列模型基本一致。
RabbitMQ
采纳队列模型,RocketMQ
和Kafka
采纳公布 / 订阅模型。
接下来的内容都基于公布 / 订阅模型。
罕用术语
个别咱们称发送音讯方为生产者 Producer
,承受生产音讯方为消费者Consumer
,音讯队列服务端为Broker
。
音讯从 Producer
发往 Broker
,Broker
将音讯存储至本地,而后 Consumer
从Broker
拉取音讯,或者 Broker
推送音讯至Consumer
,最初生产。
为了进步并发度,往往 公布 / 订阅模型 还会引入 队列 或者 分区 的概念。即音讯是发往一个主题下的某个队列或者某个分区中。RocketMQ
中叫队列,Kafka
叫分区,实质一样。
例如某个主题下有 5 个队列,那么这个主题的并发度就进步为 5,同时能够有 5 个消费者 并行生产 该主题的音讯。个别能够采纳轮询或者 key hash
取余等策略来将同一个主题的音讯调配到不同的队列中。
与之对应的消费者个别都有组的概念 Consumer Group
, 即消费者都是属于某个生产组的。一条音讯会发往多个订阅了这个主题的生产组。
假如当初有两个生产组别离是Group 1
和 Group 2
,它们都订阅了Topic-a
。此时有一条音讯发往Topic-a
,那么这两个生产组都能接管到这条音讯。
而后这条音讯理论是写入 Topic
某个队列中,生产组中的某个消费者对应生产一个队列的音讯。
在物理上除了正本拷贝之外,一条音讯在 Broker
中只会有一份,每个生产组会有本人的 offset
即生产点位来标识生产到的地位。在生产点位之前的音讯表明曾经生产过了。当然这个 offset
是队列级别的。每个生产组都会保护订阅的 Topic
下的每个队列的offset
。
来个图看看应该就很清晰了。
基本上相熟了音讯队列常见的术语和一些概念之后,咱们再来看看音讯队列常见的外围面试点。
如何保障音讯不失落
就咱们市面上常见的音讯队列而言,只有 配置切当,咱们的音讯就不会丢。
先来看看这个图,
能够看到一共有三个阶段,别离是 生产音讯、存储音讯和生产音讯。咱们从这三个阶段别离动手来看看如何确保音讯不会失落。
生产音讯
生产者发送音讯至 Broker
,须要解决Broker
的响应,不论是同步还是异步发送音讯,同步和异步回调都须要做好 try-catch
,妥善的解决响应,如果Broker
返回写入失败等谬误音讯,须要重试发送。当屡次发送失败须要作报警,日志记录等。
这样就能保障在生产音讯阶段音讯不会失落。
存储音讯
存储音讯阶段须要在 音讯刷盘之后 再给生产者响应,假如音讯写入缓存中就返回响应,那么机器忽然断电这音讯就没了,而生产者认为曾经发送胜利了。
如果 Broker
是集群部署,有多正本机制,即音讯不仅仅要写入以后Broker
, 还须要写入正本机中。那配置成至多写入两台机子后再给生产者响应。这样基本上就能保障存储的牢靠了。一台挂了还有一台还在呢(如果怕两台都挂了.. 那就再多些)。
那如果来个地震机房机子都挂了呢?emmmmmm… 大公司基本上都有异地多活。
那要是这几个地都地震了呢?emmmmmm… 这时候还是先关怀关怀人吧。
生产音讯
这里常常会有同学犯错,有些同学当消费者拿到音讯之后间接存入内存队列中就间接返回给 Broker
生产胜利,这是不对的。
你须要思考拿到音讯放在内存之后消费者就宕机了怎么办。所以咱们应该在 消费者真正执行完业务逻辑之后,再发送给 Broker
生产胜利,这才是真正的生产了。
所以只有咱们在音讯业务逻辑解决实现之后再给 Broker
响应,那么生产阶段音讯就不会失落。
小结一下
能够看出,保障音讯的可靠性须要 三方配合。
生产者
须要解决好 Broker
的响应,出错状况下利用重试、报警等伎俩。
Broker
须要管制响应的机会,单机状况下是音讯刷盘后返回响应,集群多正本状况下,即发送至两个正本及以上的状况下再返回响应。
消费者
须要在执行完真正的业务逻辑之后再返回响应给Broker
。
然而要留神 音讯可靠性加强了,性能就降落了,期待音讯刷盘、多正本同步后返回都会影响性能。因而还是看业务,例如日志的传输可能丢那么一两条关系不大,因而没必要等音讯刷盘再响应。
如果解决反复音讯
咱们先来看看能不能防止音讯的反复。
假如咱们发送音讯,就管发,不论 Broker
的响应,那么咱们发往 Broker
是不会反复的。
然而个别状况咱们是不容许这样的,这样音讯就齐全不牢靠了,咱们的根本需要是音讯至多得发到 Broker
上,那就得等 Broker
的响应,那么就可能存在 Broker
曾经写入了,过后响应因为网络起因生产者没有收到,而后生产者又重发了一次,此时音讯就反复了。
再看消费者生产的时候,假如咱们消费者拿到音讯生产了,业务逻辑曾经走完了,事务提交了,此时须要更新 Consumer offset
了,而后这个消费者挂了,另一个消费者顶上,此时 Consumer offset
还没更新,于是又拿到方才那条音讯,业务又被执行了一遍。于是音讯又反复了。
能够看到失常业务而言 音讯反复是不可避免的 ,因而咱们只能从 另一个角度 来解决反复音讯的问题。
关键点就是 幂等。既然咱们不能避免反复音讯的产生,那么咱们只能在业务上解决反复音讯所带来的影响。
幂等解决反复音讯
幂等是数学上的概念,咱们就了解为同样的参数屡次调用同一个接口和调用一次产生的后果是统一的。
例如这条 SQL update t1 set money = 150 where id = 1 and money = 100;
执行多少遍 money
都是 150,这就叫幂等。
因而须要革新业务解决逻辑,使得在反复音讯的状况下也不会影响最终的后果。
能够通过下面我那条 SQL 一样,做了个 前置条件判断 ,即money = 100
状况,并且间接批改,更通用的是做个 version
即版本号管制,比照音讯中的版本号和数据库中的版本号。
或者通过 数据库的束缚例如惟一键,例如insert into update on duplicate key...
。
或者 记录要害的 key,比方解决订单这种,记录订单 ID,如果有反复的音讯过去,先判断下这个 ID 是否曾经被解决过了,如果没解决再进行下一步。当然也能够用全局惟一 ID 等等。
基本上就这么几个套路,真正利用到理论中还是得看具体业务细节。
如何保障音讯的有序性
有序性分:全局有序和局部有序。
全局有序
如果要保障音讯的全局有序,首先只能由一个生产者往 Topic
发送音讯,并且一个 Topic
外部只能有一个队列(分区)。消费者也必须是单线程生产这个队列。这样的音讯就是全局有序的!
不过个别状况下咱们都不须要全局有序,即便是同步 MySQL Binlog
也只须要保障单表音讯有序即可。
局部有序
因而绝大部分的有序需要是局部有序,局部有序咱们就能够将 Topic
外部划分成咱们须要的队列数,把音讯通过特定的策略发往固定的队列中,而后每个队列对应一个单线程解决的消费者。这样即实现了局部有序的需要,又能够通过队列数量的并发来进步音讯解决效率。
图中我画了多个生产者,一个生产者也能够,只有同类音讯发往指定的队列即可。
如果解决音讯沉积
音讯的沉积往往是因为 生产者的生产速度与消费者的生产速度不匹配。有可能是因为音讯生产失败重复重试造成的,也有可能就是消费者生产能力弱,慢慢地音讯就积压了。
因而咱们须要 先定位生产慢的起因 ,如果是bug
则解决 bug
,如果是因为自身生产能力较弱,咱们能够优化下生产逻辑,比方之前是一条一条音讯生产解决的,这次咱们批量解决,比方数据库的插入,一条一条插和批量插效率是不一样的。
如果逻辑咱们曾经都优化了,但还是慢,那就得思考程度扩容了,减少 Topic
的队列数和消费者数量,留神队列数肯定要减少 ,不然新减少的消费者是没货色生产的。 一个 Topic 中,一个队列只会调配给一个消费者。
当然你消费者外部是单线程还是多线程生产那看具体场景。不过要留神下面进步的音讯失落的问题,如果你是将承受到的音讯写入 内存队列 之后,而后就返回响应给Broker
,而后多线程向内存队列生产音讯,假如此时消费者宕机了,内存队列外面还未生产的音讯也就丢了。
最初
下面的几个问题都是咱们在应用音讯队列的时候常常能遇到的问题,并且也是面试对于音讯队列方面的外围考点。明天没有深刻具体音讯队列的细节,然而套路就是这么个套路,大方向上搞明确很要害。之后再接着写无关 Kafka
的源码剖析文章,有趣味的小伙伴请急躁期待。
往期举荐:
图解 + 代码 | 常见限流算法以及限流在单机分布式场景下的思考
Kafka 申请解决全流程剖析
Kafka 索引设计的亮点
Kafka 日志段读写剖析
我是 yes,从一点点到亿点点,咱们下篇见。