问题一:RabbitMQ中的broker是指什么?cluster又是指什么?
答:broker是指一个或多个erlangnode的逻辑分组,且node上运行着RabbitMQ应用程序。cluster是在broker的基础之上,增加了node之间共享元数据的约束。
问题二:什么是元数据?元数据分为哪些类型?包括哪些内容?与cluster相关的元数据有哪些?元数据是如何保存的?元数据在cluster中是如何分布的?
答:在非cluster模式下,元数据主要分为Queue元数据(queue名字和属性等)、Exchange元数据(exchange名字、类型和属性等)、Binding元数据(存放路由关系的查找表)、Vhost元数据(vhost范围内针对前三者的名字空间约束和安全属性设置)。在cluster模式下,还包括cluster中node位置信息和node关系信息。元数据按照erlangnode的类型确定是仅保存于RAM中,还是同时保存在RAM和disk上。元数据在cluster中是全node分布的。下图所示为queue的元数据在单node和cluster两种模式下的分布图。
问题三:
RAMnode和disknode的区别?
答:RAMnode仅将fabric(即queue、exchange和binding等RabbitMQ基础构件)相关元数据保存到内存中,但disknode会在内存和磁盘中均进行存储。RAMnode上唯一会存储到磁盘上的元数据是cluster中使用的disknode的地址。要求在RabbitMQcluster中至少存在一个disknode。问题四:RabbitMQ上的一个queue中存放的message是否有数量限制?答:可以认为是无限制,因为限制取决于机器的内存,但是消息过多会导致处理效率的下降。
问题五:
RabbitMQ概念里的channel、exchange和queue这些东东是逻辑概念,还是对应着进程实体?这些东东分别起什么作用?
答:queue具有自己的erlang进程;exchange内部实现为保存binding关系的查找表;channel是实际进行路由工作的实体,即负责按照routing_key将message投递给queue。由AMQP协议描述可知,channel是真实TCP连接之上的虚拟连接,所有AMQP命令都是通过channel发送的,且每一个channel有唯一的ID。一个channel只能被单独一个操作系统线程使用,故投递到特定channel上的message是有顺序的。但一个操作系统线程上允许使用多个channel。channel号为0的channel用于处理所有对于当前connection全局有效的帧,而1-65535号channel用于处理和特定channel相关的帧。AMQP协议给出的channel复用模型如下其中每一个channel运行在一个独立的线程上,多线程共享同一个socket。
问题六:vhost是什么?起什么作用?
答:vhost可以理解为虚拟broker,即mini-RabbitMQserver。其内部均含有独立的queue、exchange和binding等,但最最重要的是,其拥有独立的权限系统,可以做到vhost范围的用户控制。当然,从RabbitMQ的全局角度,vhost可以作为不同权限隔离的手段(一个典型的例子就是不同的应用可以跑在不同的vhost中)。
【cluster相关】问题七:
在单node系统和多node构成的cluster系统中声明queue、exchange,以及进行binding会有什么不同?答:当你在单node上声明queue时,只要该node上相关元数据进行了变更,你就会得到Queue.Declare-ok回应;而在cluster上声明queue,则要求cluster上的全部node都要进行元数据成功更新,才会得到Queue.Declare-ok回应。另外,若node类型为RAMnode则变更的数据仅保存在内存中,若类型为disknode则还要变更保存在磁盘上的数据。
问题八:
客户端连接到cluster中的任意node上是否都能正常工作?
答:是的。客户端感觉不到有何不同。
问题九:若cluster中拥有某个queue的ownernode失效了,且该queue被声明具有durable属性,是否能够成功从其他node上重新声明该queue?
答:不能,在这种情况下,将得到404NOT_FOUND错误。只能等queue所属的node恢复后才能使用该queue。但若该queue本身不具有durable属性,则可在其他node上重新声明。
问题十:
cluster中node的失效会对consumer产生什么影响?若是在cluster中创建了mirroredqueue,这时node失效会对consumer产生什么影响?
答:若是consumer所连接的那个node失效(无论该node是否为consumer所订阅queue的ownernode),则consumer会在发现TCP连接断开时,按标准行为执行重连逻辑,并根据“AssumeNothing”原则重建相应的fabric即可。若是失效的node为consumer订阅queue的ownernode,则consumer只能通过ConsumerCancellationNotification机制来检测与该queue订阅关系的终止,否则会出现傻等却没有任何消息来到的问题。
问题十一:
能够在地理上分开的不同数据中心使用RabbitMQcluster么?
答:不能。第一,你无法控制所创建的queue实际分布在cluster里的哪个node上(一般使用HAProxy+cluster模型时都是这样),这可能会导致各种跨地域访问时的常见问题;第二,Erlang的OTP通信框架对延迟的容忍度有限,这可能会触发各种超时,导致业务疲于处理;第三,在广域网上的连接失效问题将导致经典的“脑裂”问题,而RabbitMQ目前无法处理(该问题主要是说Mnesia)。
【综合问题】问题十二:
为什么heavyRPC的使用场景下不建议采用disknode?
答:heavyRPC是指在业务逻辑中高频调用RabbitMQ提供的RPC机制,导致不断创建、销毁replyqueue,进而造成disknode的性能问题(因为会针对元数据不断写盘)。所以在使用RPC机制时需要考虑自身的业务场景。
问题十三:向不存在的exchange发publish消息会发生什么?向不存在的queue执行consume动作会发生什么?
答:都会收到Channel.Close信令告之不存在(内含原因404NOT_FOUND)。
问题十四:
routing_key和binding_key的最大长度是多少?
答:255字节。
问题十五:
RabbitMQ允许发送的message最大可达多大?
答:根据AMQP协议规定,消息体的大小由64-bit的值来指定,所以你就可以知道到底能发多大的数据了。
问题十六:什么情况下producer不主动创建queue是安全的?
答:1.message是允许丢失的;2.实现了针对未处理消息的republish功能(例如采PublisherConfirm机制)。
问题十七:“deadletter”queue的用途?
答:当消息被RabbitMQserver投递到consumer后,但consumer却通过Basic.Reject进行了拒绝时(同时设置requeue=false),那么该消息会被放入“deadletter”queue中。该queue可用于排查message被reject或undeliver的原因。
问题十八:
为什么说保证message被可靠持久化的条件是queue和exchange具有durable属性,同时message具有persistent属性才行?
答:binding关系可以表示为exchange–binding–queue。从文档中我们知道,若要求投递的message能够不丢失,要求message本身设置persistent属性,要求exchange和queue都设置durable属性。其实这问题可以这么想,若exchange或queue未设置durable属性,则在其crash之后就会无法恢复,那么即使message设置了persistent属性,仍然存在message虽然能恢复但却无处容身的问题;同理,若message本身未设置persistent属性,则message的持久化更无从谈起。
问题十九:
什么情况下会出现blackholed问题?
答:blackholed问题是指,向exchange投递了message,而由于各种原因导致该message丢失,但发送者却不知道。可导致blackholed的情况:1.向未绑定queue的exchange发送message;2.exchange以binding_keykey_A绑定了queuequeue_A,但向该exchange发送message使用的routing_key却是key_B。
问题二十:
如何防止出现blackholed问题?
答:没有特别好的办法,只能在具体实践中通过各种方式保证相关fabric的存在。另外,如果在执行Basic.Publish时设置mandatory=true,则在遇到可能出现blackholed情况时,服务器会通过返回Basic.Return告之当前message无法被正确投递(内含原因312NO_ROUTE)。
问题二十一:
ConsumerCancellationNotification机制用于什么场景?
答:用于保证当镜像queue中master挂掉时,连接到slave上的consumer可以收到自身consume被取消的通知,进而可以重新执行consume动作从新选出的master出获得消息。若不采用该机制,连接到slave上的consumer将不会感知master挂掉这个事
情,导致后续无法再收到新master广播出来的message。另外,因为在镜像queue模式下,存在将message进行requeue的可能,所以实现consumer的逻辑时需要能够正确处理出现重复message的情况。
问题二十二:
Basic.Reject的用法是什么?
答:该信令可用于consumer对收到的message进行reject。若在该信令中设置requeue=true,则当RabbitMQserver收到该拒绝信令后,会将该message重新发送到下一个处于consume状态的consumer处(理论上仍可能将该消息发送给当前consumer)。若设置requeue=false,则RabbitMQserver在收到拒绝信令后,将直接将该message从queue中移除。另外一种移除queue中message的小技巧是,consumer回复Basic.Ack但不对获取到的message做任何处理。而Basic.Nack是对Basic.Reject的扩展,以支持一次拒绝多条message的能力。
问题二十三:
为什么不应该对所有的message都使用持久化机制?
答:首先,必然导致性能的下降,因为写磁盘比写RAM慢的多,message的吞吐量可能有10倍的差距。其次,message的持久化机制用在RabbitMQ的内置cluster方案时会出现“坑爹”问题。矛盾点在于,若message设置了persistent属性,但queue未设置durable属性,那么当该queue的ownernode出现异常后,在未重建该queue前,发往该queue的message将被blackholed;若message设置了persistent属性,同时queue也设置了durable属性,那么当queue的ownernode异常且无法重启的情况下,则该queue无法在其他node上重建,只能等待其ownernode重启后,才能恢复该queue的使用,而在这段时间内发送给该queue的message将被blackholed。所以,是否要对message进行持久化,需要综合考虑性能需要,以及可能遇到的问题。若想达到100,000条/秒以上的消息吞吐量(单RabbitMQ服务器),则要么使用其他的方式来确保message的可靠delivery,要么使用非常快速的存储系统以支持全持久化(例如使用SSD)。另外一种处理原则是:仅对关键消息作持久化处理(根据业务重要程度),且应该保证关键消息的量不会导致性能瓶颈。
问题二十四:RabbitMQ中的cluster、mirroredqueue,以及warrens机制分别用于解决什么问题?存在哪些问题?
答:cluster是为了解决当cluster中的任意node失效后,producer和consumer均可以通过其他node继续工作,即提高了可用性;另外可以通过增加node数量增加cluster的消息吞吐量的目的。cluster本身不负责message的可靠性问题(该问题由producer通过各种机制自行解决);cluster无法解决跨数据中心的问题(即脑裂问题)。另外,在cluster前使用HAProxy可以解决node的选择问题,即业务无需知道cluster中多个node的ip地址。可以利用HAProxy进行失效node的探测,可以作负载均衡。下图为HAProxy+cluster的模型。Mirroredqueue是为了解决使用cluster时所创建的queue的完整信息仅存在于单一node上的问题,从另一个角度增加可用性。若想正确使用该功能,需要保证:1.consumer需要支持ConsumerCancellationNotification机制;2.consumer必须能够正确处理重复message。
Warrens是为了解决cluster中message可能被blackholed的问题,即不能接受producer不停republishmessage但RabbitMQserver无回应的情况。Warrens有两种构成方式,一种模型是两台独立的RabbitMQserver+HAProxy,其中两个server的状态分别为active和hot-standby。该模型的特点为:两台server之间无任何数据共享和协议交互,两台server可以基于不同的RabbitMQ版本。
另一种模型为两台共享存储的RabbitMQserver+keepalived,其中两个server的状态分别为active和cold-standby。该模型的特点为:两台server基于共享存储可以做到完全恢复,要求必须基于完全相同的RabbitMQ版本。
Warrens模型存在的问题:对于第一种模型,虽然理论上讲不会丢失消息,但若在该模型上使用持久化机制,就会出现这样一种情况,即若作为active的server异常后,持久化在该server上的消息将暂时无法被consume,因为此时该queue将无法在作为hot-standby的server上被重建,所以,只能等到异常的activeserver恢复后,才能从其上的queue中获取相应的message进行处理。而对于业务来说,需要具有:a.感知AMQP连接断开后重建各种fabric的能力;b.感知activeserver恢复的能力;c.切换回activeserver的时机控制,以及切回后,针对message先后顺序产生的变化进行处理的能力。对于第二种模型,因为是基于共享存储的模式,所以导致activeserver异常的条件,可能同样会导致cold-standbyserver异常;另外,在该模型下,要求active和cold-standby的server必须具有相同的node名和UID,否则将产生访问权限问题;最后,由于该模型是冷备方案,故无法保证cold-standbyserver能在你要求的时限内成功启动。