RabbitMQ笔记

一、简述概念

RabbitMq 是一个开源的 基于AMQP协议实现的一个完整的企业级消息中间件,服务端语言由Erlang(面向并发编程)语言编写 对于高并发的处理有着天然的优势。
MQ: message Queue 顾名思义消息队列,存放的内容先进先出,消息队列,只是里面存放的内容是消息而已。
AMQP 其实和Http一样 都是一种协议, 只不过 Http是针对网络传输的。

下面是Rabbitmq相关的概念:

  • Broker: 接收和分发消息的应用,简单来说就是消息队列服务器实体,Message Broker。

  • Virtual host: 虚拟主机,一个broker里可以开设多个vhost,用作不同用户的权限分离。

  • Connection: publisher/consumer和broker之间的TCP连接。断开连接的操作只会在client端进行,Broker不会断开连接,除非出现网络故障或broker服务出现问题。 

  • Channel: 消息通道,在客户端的每个连接里,可建立多个channel,每个channel代表一个会话任务。如果每一次访问RabbitMQ都建立一个Connection,在消息量大的时候建立TCP Connection的开销将是巨大的,效率也较低。Channel是在connection内部建立的逻辑连接,如果应用程序支持多线程,通常每个thread创建单独的channel进行通讯,AMQP method包含了channel id帮助客户端和message broker识别channel,所以channel之间是完全隔离的 。

    Channel作为轻量级的Connection极大减少了操作系统建立TCP connection的开销。 

  • Exchange: 消息交换机,它指定消息按什么规则,路由到哪个队列。

  • Queue: 消息最终被送到这里等待consumer取走。一个message可以被同时拷贝到多个queue中。

    通常队列由两部分组成: 

    一部分是AMQQueue,负责AMQP协议相关的消息处理,即接收生产者发布的消息、向消费者投递消息、处理消息confirm、acknowledge等等; 

     另一部分是BackingQueue,它提供了相关的接口供AMQQueue调用,完成消息的存储以及可能的持久化工作等。

    基本经历RAM->DISK->RAM这样的过程。这样设计的好处是:当队列负载很高的情况下,能够通过将一部分消息由磁盘保存来节省内存空间,

    当负载降低的时候,这部分消息又渐渐回到内存,被消费者获取,使得整个队列具有很好的弹性。

  • Binding: 它的作用就是把exchange和queue按照路由规则绑定起来,exchange和queue之间的虚拟连接,binding中可以包含routing key。Binding信息被保存到exchange中的查询表中,用于message的分发依据。 

  • Routing Key:路由关键字,exchange根据这个关键字进行消息投递。

  • producer:消息生产者,就是投递消息的程序。

  • consumer:消息消费者,就是接受消息的程序。

二、Exchange的类型

 交换机的功能主要是接收消息并且转发到绑定的队列,交换机不存储消息,在启用ack模式后,交换机找不到队列会返回错误。

direct : 特点是:"先匹配, 再投送"。这种类型的交换机的路由规则是根据一个routingKey的标识,交换机通过一个routingKey与队列绑定 ,
当绑定的队列的routingKey 与生产者发送的消息中的路由键(routing key)一样 那么交换机会吧这个消息发送给对应的队列。 它是完全匹配、单播的模式。
fanout: 特点是:转发消息到所有绑定队列。这种类型的交换机路由规则很简单,该Exchange它就会把消息发送给跟它绑定对应队列(与routingKey没关系),其实是一种广播行为
topic: 特点是:按规则转发消息(最灵活)。这种类型会按照正则表达式,对RoutingKey与BindingKey进行匹配,如果匹配成功,则发送到对应的Queue中。
    注意:符号 # 匹配一个或多个词,符号 * 匹配不多不少一个词。因此 usa.# 能够匹配到 usa.news.xxx,但是 usa.* 只会匹配到 usa.xxx
    假设:我绑定的routingKey有队列A和B,
    A的routingKey是:*.user,
    B的routingKey是: #.user。
    那么我生产一条消息routingKey 为:error.user ,那么此时 2个队列都能接受到,
    如果改为 topic.error.user 那么这时候 只有B能接受到了。
headers: 也是根据规则匹配, 相较于 direct 和 topic 固定地使用 routing_key , headers 则是一个自定义匹配规则的类型.
在队列与交换器绑定时, 会设定一组键值对规则, 消息中也包括一组键值对( headers 属性), 当这些键值对有一对, 或全部匹配时, 消息被投送到对应队列.
这个类型的交换机很少用到,他的路由规则 与routingKey无关 而是通过判断header参数来识别的, 基本上没有应用场景,因为上面的三种类型已经能应付了。

三、生产者和消费者的运转过程

生产者
1.
建立链接(Connection)
2. 在链接(Connection)上开启一个信道(Channel)
3. 声明一个交换机(Exchange)
4. 声明一个队列(Queue)
5. 使用路由键(RoutingKey)将队列(Queue)和交换机(Exchange)绑定起来
6. 根据路由键(RoutingKey)发送消息到交换机(Exchange)
7. [MQ]根据交换机(Exchange)和路由键(RoutingKey),将消息或存放到队列(Queue),或丢弃,或退回给生产者
8. 关闭信道(Channel)
9. 关闭链接(Connection)
消费者
1. 建立链接(Connection)
2. 在链接(Connection)上开启一个信道(Channel)
3. 请求消费指定队列(Queue)的消息,并设置回调函数(onMessage)
4. [MQ]将消息推送给消费者
5. 消费者发送消息确定(Ack[acknowledge])
6. [MQ]删除被确认的消息
7. 关闭信道(Channel)
8. 关闭链接(Connection)
可以注意到,无论是生产者还是消费者,第一步都是需要建立连接,再在连接的基础上建立信道。
生产者需要使用交换机来转发信息队列,其中消息绑定的标识就是路由键RoutingKey了。
消费者需要请求队列,消费队列消息,还能使用Ack向消息队列来返回确认信息。
生产和消费操作完毕后,都需要关闭信道再关闭连接,这点和数据库类似。

 

四、消费者订阅消息

在RabbitMQ中消费者有2种方式获取队列中的消息:
a) 一种是通过basic.consume命令,订阅某一个队列中的消息,channel会自动在处理完上一条消息之后,接收下一条消息。(同一个channel消息处理是串行的)。
  除非关闭channel或者取消订阅,否则客户端将会一直接收队列的消息。
b) 另外一种方式是通过basic.get命令主动获取队列中的消息,但是绝对不可以通过循环调用basic.get来代替basic.consume,这是因为basic.get RabbitMQ在实际执行的时候,
  是首先consume某一个队列,然后检索第一条消息,然后再取消订阅。如果是高吞吐率的消费者,最好还是建议使用basic.consume。
  如果有多个消费者同时订阅同一个队列的话,RabbitMQ是采用[循环的方式]分发消息的,每一条消息只能被一个订阅者接收。
  例如,有队列Queue,其中ClientA和ClientB都Consume了该队列,MessageA到达队列后,被分派到ClientA,ClientA回复服务器收到响应,服务器删除MessageA;
  再有一条消息MessageB抵达队列,服务器根据“循环推送”原则,将消息会发给ClientB,然后收到ClientB的确认后,删除MessageB;
  等到再下一条消息时,服务器会再将消息发送给ClientA。
  这里我们可以看出,消费者再接到消息以后,都需要给服务器发送一条确认命令,这个即可以在handleDelivery里显示的调用basic.ack实现,
  也可以在Consume某个队列的时候,设置autoACK属性为true实现。这个ACK仅仅是通知服务器可以安全的删除该消息,而不是通知生产者,与RPC不同。
  如果消费者在接到消息以后还没来得及返回ACK就断开了连接,消息服务器会重传该消息给下一个订阅者,如果没有订阅者就会存储该消息
  既然RabbitMQ提供了ACK某一个消息的命令,当然也提供了Reject某一个消息的命令。当客户端发生错误,调用basic.reject命令拒绝某一个消息时,可以设置一个requeue的属         性,如果为true,
     则消息服务器会重传该消息给下一个订阅者;如果为false,则会直接删除该消息。当然,也可以通过ack,让消息服务器直接删除该消息并且不会重传。

 五、持久化

Rabbit MQ默认是不持久队列、Exchange、Binding以及队列中的消息的,这意味着一旦消息服务器重启,所有已声明的队列,Exchange,Binding以及队列中的消息都会丢失。
通过设置Exchange和MessageQueue的durable属性为true,可以使得队列和Exchange持久化,但是这还不能使得队列中的消息持久化,这需要生产者在发送消息的时候,将delivery mode设置为2,只有这3个全部设置完成后,才能保证服务器重启不会对现有的队列造成影响。
这里需要注意的是,只有durable为true的Exchange和durable为ture的Queues才能绑定,否则在绑定时,RabbitMQ都会抛错的。
持久化会对RabbitMQ的性能造成比较大的影响,可能会下降10倍不止。

六、事务

 对事务的支持是AMQP协议的一个重要特性。客户端将信道设置为事务模式。假设当生产者将一个持久化消息发送给服务器时,因为consume命令本身没有任何Response返回,所以即使服务器崩溃,没有持久化该消息,生产者也无法获知该消息已经丢失。如果此时使用事务,即通过txSelect()开启一个事务,然后发送消息给服务器,然后通过txCommit()提交该事务,即可以保证,如果txCommit()提交了,则该消息一定会持久化,如果txCommit()还未提交即服务器崩溃,则该消息不会服务器接收。当然Rabbit MQ也提供了txRollback()命令用于回滚某一个事务。只有当消息被RabbitMQ接收,事务才能提交成功,否则在捕获异常后进行回滚。使用事务会使得性能有所下降。 

、Confirm机制

使用事务固然可以保证只有提交的事务,才会被服务器执行。但是这样同时也将客户端与消息服务器同步起来,这背离了消息队列解耦的本质。
Rabbit MQ提供了一个更加轻量级的机制来保证生产者可以感知服务器消息是否已被路由到正确的队列中——Confirm。
如果设置channel为confirm状态,则通过该channel发送的消息都会被分配一个唯一的ID,然后一旦该消息被正确的路由到匹配的队列中后,服务器会返回给生产者一个Confirm,
该Confirm包含该消息的ID,这样生产者就会知道该消息已被正确分发。
对于持久化消息,只有该消息被持久化后,才会返回Confirm。

优点:
Confirm机制的最大优点在于异步,生产者在发送消息以后,即可继续执行其他任务。而服务器返回Confirm后,会触发生产者的回调函数,生产者在回调函数中处理Confirm信息。
如果消息服务器发生异常,导致该消息丢失,会返回给生产者一个nack,表示消息已经丢失,这样生产者就可以通过重发消息,保证消息不丢失。
Confirm机制在性能上要比事务优越很多。

缺点:
但是Confirm机制,[无法进行回滚],就是一旦服务器崩溃,生产者无法得到Confirm信息,生产者其实本身也不知道该消息是否已经被持久化,只有继续重发来保证消息不丢失,
但是如果原先已经持久化的消息,并不会被回滚,这样队列中就会存在两条相同的消息,系统需要支持去重或者业务系统支持幂等性

 、镜像机制 

内存、磁盘。支持少量堆积。
RabbitMQ的消息分为持久化的消息和非持久化消息,不管是持久化的消息还是非持久化的消息都可以写入到磁盘。
持久化的消息在到达队列时就写入到磁盘,并且如果可以,持久化的消息也会在内存中保存一份备份,这样可以提高一定的性能,当内存吃紧的时候会从内存中清除。
非持久化的消息一般只存在于内存中,在内存吃紧的时候会被换入到磁盘中,以节省内存。就是一个RAM->DISK->RAM的过程。
而同一个队列的slave也会均匀地分布在不同的服务器上,保证负载均衡和高可用性。

RabbitMQ支持简单集群,'复制'模式,对高级集群模式支持不好。

1) 镜像队列(Mirrored Queue)

RabbitMQ集群的队列(Queue)在默认的情况下只存在单一节点(node)上。我们也可以把队列配置成同时存在在多个节点上,也就是说队列可以被镜像到多个节点上。
RabbitMQ的每一个节点,要么是内存节点,要么是磁盘节点,集群中至少要有一个是磁盘节点。
在RabbitMQ集群中创建队列,集群只会在单个节点创建队列进程和完整的队列信息(元数据、状态、内容),而不是在所有节点上创建。
引入镜像队列,可以避免单点故障,确保服务的可用性,但是需要人为地为某些重要的队列配置镜像。
发布(publish)到镜像队列上的消息(message)会被复制(replicated)到所有的节点上,保证这些队列的消息不会丢失。一个镜像队列包含一个主(master)和多个从(slave)。
除发送消息外的所有动作都向master发送,然后[由master将命令执行结果广播给各个slave,发消息由slave操作],RabbitMQ会让master均匀地分布在不同的服务器上,
配置镜像的队列,都包含一个主节点master和多个从节点slave,如果master失效,加入时间最长的slave会被提升为新的master,

2) 非同步的Slave(unsynchronised slave)

在rabbitmq中同步(synchronised)是用来描述master和slave之间的数据状态是否一致的。如果slave包含master中的所有message,则这个slave是synchronised,
如果这个slave并没有包含master中所有的message,则这个slave是unsynchronised。

 3) 在什么情况下会出现unsynchronisedslave?

当一个新slave加入到一个镜像队列时,这时这个新slave是空的,而master中这时可能包含之前接收到的消息。假设这时master包含了N条消息,这是第N+1条消息被添加到这个镜像队列中,
这个新slave会从这个第N+1条消息开始接收。此时这个slave就是unsynchronised slave。随着前5条消息从镜像队列中被消费掉(consumed), 这个slave变成了synchronised。
slave 重新加入(rejoin)到镜像队列时,也会出现非同步的情况。一个slave要重新加入镜像队列之前,slave可能已经接收了一些消息,要重新加入镜像队列,
就要清空自己之前已经接收的所有消息,好像自己是第一次加入队列一样。(slave在很多情况下会需要重新加入镜像队列,例如:网络分区(networkpartition))

九、负载均衡

对负载均衡的支持不好。
1)消息被投递到哪个队列是由交换器和key决定的,交换器、路由键、队列都需要手动创建。
RabbitMQ客户端发送消息要和broker建立连接,需要事先知道broker上有哪些交换器,有哪些队列。
通常要声明要发送的目标队列,如果没有目标队列,会在broker上创建一个队列,如果有,就什么都不处理,接着往这个队列发送消息。
假设大部分繁重任务的队列都创建在同一个broker上,那么这个broker的负载就会过大。
可以在上线前预先创建队列,无需声明要发送的队列,但是发送时不会尝试创建队列,可能出现找不到队列的问题,

RabbitMQ的备份交换器会把找不到队列的消息保存到一个专门的队列中,以便以后查询使用。
使用镜像队列机制建立RabbitMQ集群可以解决这个问题,形成master-slave的架构,master节点会均匀分布在不同的服务器上,让每一台服务器分摊负载。
slave节点只是负责转发,在master失效时会选择加入时间最长的slave成为master。
当新节点加入镜像队列的时候,队列中的消息不会同步到新的slave中,除非调用同步命令,但是[调用同步命令后(可以理解为主从同步),队列会阻塞],不能在生产环境中调用同步命令。
2)当RabbitMQ队列拥有多个消费者的时候,队列收到的消息将以[轮询的分发方式]发送给消费者。每条消息只会发送给订阅列表里的一个消费者,不会重复
这种方式非常适合扩展,而且是专门为并发程序设计的。
如果某些消费者的任务比较繁重,那么可以设置basicQos限制信道上消费者能保持的最大未确认消息的数量,在达到上限时,RabbitMQ不再向这个消费者发送任何消息。

也就是说,如果同时给consumerA , consumerB发消息,但是如果consumerA消费比consumerB慢,则可以设置上限,表示它已经达到上限,暂时不接收消息了,可以把消息发给consumerB,等consumerA

消费完了后,再继续接收消息。可以理解为"能者多劳"。
3)对于RabbitMQ而言,客户端与集群建立的TCP连接不是与集群中所有的节点建立连接,而是挑选其中一个节点建立连接。

但是RabbitMQ集群可以借助HAProxy、LVS技术,或者在客户端使用算法实现负载均衡,引入负载均衡之后,各个客户端的连接可以分摊到集群的各个节点之中。

 十、客户端均衡算法

a. 轮询法:按顺序返回下一个服务器的连接地址。
b. 加权轮询法:给配置高、负载低的机器配置更高的权重,让其处理更多的请求;而配置低、负载高的机器,给其分配较低的权重,降低其系统负载。
c. 随机法:随机选取一个服务器的连接地址。
d. 加权随机法:按照概率随机选取连接地址。
e. 源地址哈希法:通过哈希函数计算得到的一个数值,用该数值对服务器列表的大小进行取模运算。
f. 最小连接数法:动态选择当前连接数最少的一台服务器的连接地址。

 十一、 消息重复、吞吐量TPS 、顺序消息

支持at least once、at most once
吞吐量TPS 比较大
不支持 顺序消息

 十二、消息确认

支持。
1)发送方确认机制,消息被投递到所有匹配的队列后,返回成功。如果消息和队列是可持久化的,那么在写入磁盘后,返回成功。支持批量确认和异步确认
2)接收方确认机制,设置autoAck为false,需要显式确认,设置autoAck为true,自动确认。
当autoAck为false的时候,RabbitMQ队列会分成两部分,一部分是等待投递给consumer的消息,一部分是已经投递但是没收到确认的消息。
如果一直没有收到确认信号,并且consumer已经断开连接,RabbitMQ会安排这个消息重新进入队列,投递给原来的消费者或者下一个消费者
未确认的消息不会有过期时间,如果一直没有确认,并且没有断开连接,RabbitMQ会一直等待,RabbitMQ允许一条消息处理的时间可以很久很久

 十三、消息重试

不支持,但是可以利用消息确认机制实现。RabbitMQ接收方确认机制,设置autoAck为false。当autoAck为false的时候,RabbitMQ队列会分成两部分,

一部分是等待投递给consumer的消息,一部分是已经投递但是没收到确认的消息。如果一直没有收到确认信号,并且consumer已经断开连接,

RabbitMQ会安排这个消息重新进入队列,投递给原来的消费者或者下一个消费者

十四、并发度

并发度极高。本身是用Erlang语言写的,并发性能高。
可在消费者中开启多线程,最常用的做法是一个channel对应一个消费者,每一个线程把持一个channel,多个线程复用connection的tcp连接,减少性能开销。
当RabbitMQ队列拥有多个消费者的时候,队列收到的消息将以轮询的分发方式发送给消费者。每条消息只会发送给订阅列表里的一个消费者,不会重复。
这种方式非常适合扩展,而且是专门为并发程序设计的。
如果某些消费者的任务比较繁重,那么可以设置basicQos限制信道上消费者能保持的最大未确认消息的数量,在达到上限时,RabbitMQ不再向这个消费者发送任何消息。

 

十五、消息存储

message主要存储在RAMdisk里面。
所有队列中的消息都以append的方式写到一个文件中,当这个文件的大小超过指定的限制大小后,关闭这个文件再创建一个新的文件供消息的写入。文件名(*.rdq)从0开始然后依次累加。
在进行消息的存储时,rabbitmq会在ets表中记录消息在文件中的映射,以及文件的相关信息。消息读取时,根据消息ID找到该消息所存储的文件,在文件中的偏移量,然后打开文件进行读取。
rabbitmq在启动时会创建msg_store_persistentmsg_store_transient两个进程,一个用于持久消息的存储,一个用于内存不够时,将存储在内存中的非持久化数据转存到磁盘中。
所有队列的消息的写入和删除最终都由这两个进程负责处理,而消息的读取则可能是队列本身直接打开文件进行读取,
也可能是发送请求由msg_store_persisteng/msg_store_transient进程进行处理。

Ets数据结构:
-record( msg_location,{   msg_id, //消息ID
            ref_count, //引用计数
            file, //消息存储的文件名
            offset, //消息在文件中的偏移量
            total_size //消息的大小
}).

十六、 GC过程

消息的删除只是从flying_ets表删除指定消息的相关信息,同时更新消息对应存储的文件的相关信息、更新文件有效数据大小。当垃圾数据超过一定比例后(默认比例为40%),rabbitmq触发垃圾回收。
垃圾回收会先找到符合要求的两个文件(根据#file_summary{}中left,right找逻辑上相邻的两个文件,并且两个文件的有效数据可在一个文件中存储),
然后锁定这两个文件,并先对左边文件的有效数据进行整理,再将右边文件的有效数据写入到左边文件,同时更新消息的相关信息(存储的文件,文件中的偏移量)、
文件的相关信息(文件的有效数据,左边文件,右边文件),最后将右边的文件删除。有点像JVM里面的"复制算法"。

 十七、 消息丢失

消息投递时 可能发生丢失的场景:
1.生产者------msg------> MQ 。可开启消息投递结果回调,确保每条消息都收到了回调。
2.MQ。将Queue与消息设置成可持久化,搭建镜像集群队列。
3.MQ-------callback---->生产者。回调时失败,某条消息在一段时间内未收到回调,则默认投递失败,生产者需要再次投递该消息到MQ。(该场景下会导致同一条消息被重复投递,消费者端需要自行保证消息幂等消费)

 十八、 死信消息

•消息会变成死信消息的场景:
i.消息被 (basic.reject() or basic.nack()) and requeue = false ,即消息被消费者拒绝签收,并且重新入队为false。
1.1 有一种场景需要注意下:消费者设置了自动ACK,当重复投递次数达到了设置的最大retry次数之后,消息也会投递到死信队列,但是内部的原理还是调用了 nack / reject 。
ii.消息过期,过了TTL存活时间
iii.队列设置了 x-max-length 最大消息数量且当前队列中的消息已经达到了这个数量,再次投递,消息将被挤掉,被挤掉的是最靠近被消费那一端的消息。

 十九、可靠性到达方案

一般可用方案是将消息入库或者捕获异常机制发送到邮箱、人工手段处理等方式,参考:
https://mp.weixin.qq.com/s/2wBdnkIWPH8VHGme7EMhJg
https://yq.aliyun.com/articles/754363?spm=a2c4e.11163080.searchblog.19.7a912ec1nlVgvQ

   

二十、模式

Rabbit模式大概分为以下三种:单一模式、普通模式、镜像模式
单一模式:最简单的情况,非集群模式,即单实例服务。

普通模式:默认的集群模式。
queue创建之后,如果没有其它policy,则queue就会按照普通模式集群。对于Queue来说,消息实体只存在于其中一个节点,A、B两个节点仅有相同的元数据,即队列结构,但队列的元数据仅保存有一份,即创建该队列的rabbitmq节点(A节点),当A节点宕机,你可以去其B节点查看,./rabbitmqctl list_queues 发现该队列已经丢失,但声明的exchange还存在。
当消息进入A节点的Queue中后,consumer从B节点拉取时,RabbitMQ会临时在A、B间进行消息传输,把A中的消息实体取出并经过B发送给consumer。
所以consumer应尽量连接每一个节点,从中取消息。即对于同一个逻辑队列,要在多个节点建立物理Queue。否则无论consumer连A或B,出口总在A,会产生瓶颈。
该模式存在一个问题就是当A节点故障后,B节点无法取到A节点中还未消费的消息实体。
如果做了消息持久化,那么得等A节点恢复,然后才可被消费;如果没有持久化的话,队列数据就丢失了。

镜像模式:把需要的队列做成镜像队列,存在于多个节点,属于RabbitMQ的HA方案。
该模式解决了上述问题,其实质和普通模式不同之处在于,消息实体会主动在镜像节点间同步,而不是在consumer取数据时临时拉取。
该模式带来的副作用也很明显,除了降低系统性能外,如果镜像队列数量过多,加之大量的消息进入,集群内部的网络带宽将会被这种同步通讯大大消耗掉。
所以在对可靠性要求较高的场合中适用,一个队列想做成镜像队列,需要先设置policy,然后客户端创建队列的时候,rabbitmq集群根据“队列名称”自动设置是普通集群模式或镜像队列。具体如下:
队列通过策略来使能镜像。策略能在任何时刻改变,rabbitmq队列也近可能的将队列随着策略变化而变化;非镜像队列和镜像队列之间是有区别的,前者缺乏额外的镜像基础设施,没有任何slave,因此会运行得更快。
为了使队列称为镜像队列,你将会创建一个策略来匹配队列,设置策略有两个键“ha-mode和 ha-params(可选)”。

了解集群中的基本概念:
RabbitMQ的集群节点包括内存节点、磁盘节点。顾名思义内存节点就是将所有数据放在内存,磁盘节点将数据放在磁盘。不过,如前文所述,如果在投递消息时,打开了消息的持久化,那么即使是内存节点,数据还是安全的放在磁盘。
一个rabbitmq集群中可以共享user,vhost,queue,exchange等,所有的数据和状态都是必须在所有节点上复制的,一个例外是,那些当前只属于创建它的节点的消息队列,尽管它们可见且可被所有节点读取。rabbitmq节点可以动态的加入到集群中,一个节点它可以加入到集群中,也可以从集群环集群会进行一个基本的负载均衡。

集群中有两种节点:
1 内存节点:只保存状态到内存(一个例外的情况是:持久的queue的持久内容将被保存到disk)
2 磁盘节点:保存状态到内存和磁盘。
内存节点虽然不写入磁盘,但是它执行比磁盘节点要好。集群中,只需要一个磁盘节点来保存状态就足够了如果集群中只有内存节点,那么不能停止它们,否则所有的状态,消息等都会丢失。 

参考: 

https://www.cnblogs.com/smallJunJun/p/11356076.html
https://www.oschina.net/question/3915715_2307503
https://www.jianshu.com/p/1620d8c722ef

原文地址:https://www.cnblogs.com/coloz/p/12728339.html