微信公众号: IT一刻钟
大型现实非严肃主义现场
一刻钟与你分享优质技术架构与见闻,作一个有剧情的程序员
关注可了解更多精彩内容。问题或建议,请公众号留言。
在好久好久之前,人们之间的通讯方式就是面对面交谈,你说一句,我听一句,虽然简单可靠,可是弊端也很大。
好比,当你成为一个军队的首领,每一个属下一有状况就马上向你汇报,一个还好,但当你的属下有几十个几百个的时候,他们天天不分时间不看场合,都在叽叽喳喳和你汇报状况,那你可能什么都听不到,并且脑壳都要炸掉了。这个时候,你说停,都给我停下,要汇报状况的,去门口排队,一个一个的来,这个就叫作流量削峰,一群人不要蜂拥而上,都乖乖给我排队去。
而后你就一个接一个的听,听了整整24个小时,实在困的不行,寻思着这样不行呀,如此下去可能就要天妒英才了,因而你又说,来人,发笔和纸,都把要汇报的消息写在纸上,写完后告诉吕秀才,而后听吕秀才的指示,沿着屋里右面墙根,按照指示的位置叠放整齐,汇报的人就能够退下该作啥作啥去吧,等我休息一下,再来看大家的汇报内容,这就叫作异步处理,你终于能够由本身掌控消息获取的进度了,美滋滋的去睡觉了。
而汇报的人把内容写在纸上,叠放好,就能够退下本身作本身该作的事情,而不是一直在门口等待汇报,这个就叫作解耦。
削峰,异步,解耦。这就是消息队列最经常使用的三大场景。
故事中的下属们,就是消息生产者角色,屋子右面墙根那块地就是消息持久化,吕秀才就是消息调度中心,而你就是消息消费者角色。下属们汇报的消息,应该叠放在哪里,这个消息又应该在哪里才能找到,全靠吕秀才的惊人记忆力,才可让消息准确的被投放以及消费。git
在RocketMQ里,就有一个角色和吕秀才的做用同样,叫作NameServer,它是整个分布式消息调度的总控制,是RocketMQ的灵魂之所在,倘如没有了它,RocketMQ会分崩离析没法工做。
那么,它是怎么工做的呢?
咱们先来看一张RocketMQ物理架构图:程序员
乱如蜘蛛丝?不要惧怕,换句话说,先忘掉这张图吧。github
咱们来类比一下现实生活,有一我的想要给另一我的寄快件,那么就须要先由这我的在网上查询有哪些邮局,而后选择其中一个邮局,把快件投递给它,再由邮局配送到目标人。编程
须要完成这一整个业务流程,首先须要将邮局自身的信息注册到卫星网络上,卫星负责信息的调度,这样发件人就知道有哪些邮局能够选择,收件人经过卫星网络知道快件到了哪一个邮局,能够联系邮局沟通适合的配送时间,而邮局则负责接收配送存储快件。
类比RocketMQ简线图就是以下:安全
Producer:消息⽣产者,⽤于向消息服务器发送消息,就是图中的寄件人。
NameServer:路由注册中⼼,就是图中的卫星。
Broker:消息存储服务器,就是图中的邮局。
Consumer:消息消费者,不是今天的重点,图中未标出,就是收件人。服务器
因而可知,NameServer做为分布式消息队列的协调者,具备信息路由注册与发现的做用。微信
邮局在竣工后,须要与卫星联网,将本身归入卫星网络管理中,这样就至关于对外宣布,我这个邮局开始运营了,能够收发邮件快递了。
邮局并网以后,如何让卫星持续并及时感知这个邮局在线以及邮局自身信息的调整,使卫星能够随时协调这个邮局呢?这个时候就须要邮局定时向卫星发一条信息:
“哔哔哔————我是邮局C,编号SHC,地址XXXXX,归属中国上海集群,在线,此时此刻2019年3月15日13点21秒”
卫星接收到消息后,拿个小本本记录下来:
“邮局B,BJB,北京,2019年3月15日13点10秒,活着...”
“邮局A,BJA,北京,2019年3月15日13点15秒,活着...”
“邮局C,SHC,上海,2019年3月15日13点21秒,活着...”
......网络
上面这个故事,就讲述了NameServer路由注册的基本原理。
NameServer就至关于卫星,内部会维护一个Broker表,用来动态存储Broker的信息。
而Broker就至关于邮局,在启动的时候,会先遍历NameServer列表,依次发起注册请求,保持长链接,而后每隔30秒向NameServer发送心跳包,心跳包中包含BrokerId、Broker地址、Broker名称、Broker所属集群名称等等,而后NameServer接收到心跳包后,会更新时间戳,记录这个Broker的最新存活时间。
NameServer在处理心跳包的时候,存在多个Broker同时操做一张Broker表,为了防止并发修改Broker表致使不安全,路由注册操做引入了ReadWriteLock读写锁,这个设计亮点容许多个消息生产者并发读,保证了消息发送时的高并发,可是同一时刻NameServer只能处理一个Broker心跳包,多个心跳包串行处理。这也是读写锁的经典使用场景,即读多写少。多线程
突然有一天,邮局C的机房进老鼠了,咬断电源线宕机了,而卫星不知道邮局C业务故障了,依旧将带有邮局C的邮局表信息传给寄件人(生产者),寄件人联系邮局C发送快件,可是邮局C机房宕机,业务暂停,处于瘫痪状态,天然也就没法接收快件了。
另外一方面,由于快件未能被邮局C收入,也就没法将快件转交给收件人,顾客们久久等不到本身的快件,纷纷投诉,为此邮局C的管理层备受责难。
因而邮政总局技术部开始研究讨论,怎么让卫星能够感知到邮局“失联了”,从而自动排除故障邮局,将其负责的业务交付给其余正常的邮局处理,这样就不会由于某一个邮局出现问题,而致使这个邮局所管辖的部分业务没法处理。
你们众说纷纭,最后敲定了一个方案,让卫星每隔一段时间扫描邮局信息表,若是发现某个邮局上报信息时间与当时扫描时间之间的差值超过了某个预设的阈值,就断定这个邮局“失联了”,将此邮局信息从邮局表中剔除。这样寄件人查询到的邮局表里都是正常营业的邮局信息。
新功能上线运营后,效果不错,你们不再用担忧由于某个邮局故障而致使业务停滞,又过上了泡茶报纸的生活。架构
这个故事一样在RocketMQ中上演。
正常状况下,若是Broker关闭,则会与NameServer断开长链接,Netty的通道关闭监听器会监听到链接断开事件,而后会将这个Broker信息剔除掉。
异常状况下,NameServer中有一个定时任务,每隔10秒扫描一下Broker表,若是某个Broker的心跳包最新时间戳距离当前时间超多120秒,也会断定Broker失效并将其移除。
细心的人会发现一个问题,NameServer在清除失活Broker以后,并无主动通知生产者,生产者每隔30秒会请求NameServer并获取最新的路由表,那么就意味着,消息生产者总会有30秒的延时,没法实时感知Broker服务器的宕机。因此在这个30秒里,生产者依旧会向失活Broker发送消息,那么消息发送的高可用性如何保证呢?
要解决这个问题得首先谈一谈Broker的负载策略,消息发送队列默认采用轮询机制,消息发送时默认选择异常重试机制来保证消息发送的高可用。当Broker宕机后,虽然消息发送者没法第一时间感知Broker 宕机,可是当消息生产者向Broker发送消息返回异常后,消息生产者会选择另一个Broker上的消息队列,这样就规避了发生故障的Broker,结合重试机制,巧妙实现消息发送的高可用,同时因为不须要NameServer通知众多不固定的生产者,也下降了NameServer实现的复杂性。
在下降NameServer实现复杂性方面,还有一个设计亮点就是NameServer之间是彼此独立无交流的,也就是说NameServer服务器之间在某个时刻的数据并不会彻底相同,可是异常重试机制使得这种差别不会形成任何影响。
天上的卫星是有限的,不易变的,而地上的寄件人是繁多的,易变的。因此寄件人想要知道有哪些邮局,很明显最适合的方式是向卫星发请求,拉取邮局表信息,而不是等卫星给每一个人推送。
因此在RocketMQ中,NameServer是不主动推送会客户端的,而是由客户端拉取主题的最新路由信息。
NameServer做为注册和发现中心,是整个分布式消息队列调度的灵魂,谈及到分布式,就逃不开CAP理论,C是Consistency,A是Availability,P是Partiton Tolerance,对于分布式架构,网络条件不可控,出现网络分区是不可避免的,所以必须具有分区容错性,那么NameServer就是在AP仍是CP中选择了,因为NameServer之间相互独立,很明显,是一个AP设计。
ZooKeeper为分布式应用程序提供协调服务。那为何RocketMQ要本身造轮子,开发集群的管理程序呢?由于ZooKeeper的功能很强大,包括自动Master选举等,RocketMQ的架构设计决定了它不须要进行Master选举,用不到这些复杂的功能,只须要一个轻量级的元数据服务器就足够了。
中间件对稳定性要求很高,RocketMQ的NameServer只有不多的代码,容易维护,因此不须要再依赖另外一个中间件,从而减小总体维护成本。
1.长链接编程模型⾥⼼跳的实现原理
2.多线程编程中读写锁的经典使⽤⽅式
3.追求简单⾼效⼜可靠的实现⽅式
想要研究NameServer源代码的,请点击连接:https://github.com/MrChiu/Roc...
里面附有我标注的注释,易于通读代码