第一章 RabbitMQ简介
消息队列中间件:利用高效可靠的消息传递机制进行与平台无关的数据交流,并基于数据通信来进行分布式系统的集成。分为点对点和发布订阅的传递模式。消息中间件的作用:解耦、冗余(存储)、扩展性、削峰、可恢复性、顺序保证、缓冲、异步通信。RabbitMQ的特点: 可靠性:持久化、传输确认及发布确认等;灵活的路由:消息进入队列之前,通过交换器来路由消息;扩展性:多个节点可组成集群;高可用性:队列可在集群的机器上设置镜像;多种协议:支持AMQP、STOMP、MQTT等多种消息中间件协议;多语言客户端;管理界面;插件机制。第二章 RabbitMQ入门
概念介绍: Producer:生产者;Consumer:消费者;Broker:消息中间件的服务节点。Queue:队列,用于存储消息,多个消费者可以订阅同一个队列,之后轮询处理。Exchange:交换器,生产者将消息发送到Exchange,由交换器将消息路由到一个或者多个队列中。RoutingKey:路由键,生产者将消息发给交换器时指定RoutingKey,用来指定这个消息的路由规则,而RoutingKey与BindingKey联合使用才能生效。Binding:绑定,将交换器与队列关联起来,就能正确的将消息路由到队列了 交换器类型 fanout:把所有发送到该交换器的消息路由到所有与该交换器绑定的队列中。direct:把消息路由到那些BingdingKey和RoutingKey完全匹配的队列中topic:把消息路由到BingdingKey和RoutingKey符合特殊匹配规则的队列中。其中 * 用于匹配一个单词,# 用于匹配多个单词headers:根据发送的消息内容中的headers属性进行匹配,在绑定队列和交换器时指定一组键值对。但其性能差,用途少 一个Connection可对应多个Channel,(类似于NIO)即TCP连接复用AQMP协议包含三层 ModuleLayer:最高层,定义了供客户端调用的命令SessionLayer:中间层,将客户端的命令发送给服务器。TransportLayer:最底层,传输二进制数据流,提供帧的处理,信道复用、错误检测和数据表示等第三章 客户端开发向导
创建Connection:Connection可以用来创建多个Channel实例,但多线程间共享Channel实例是非线程安全的。Channel和Connection中有isOpen方法可以用来检测是否处于开启状态,但该方法的返回值依赖于shutdownCause,有可能会产生竞争。交换器的API:队列的API消息的拉与推消息确认机制:通过autoAck参数实现,当值为false时,队列中的消息分为等待投递给消费者的消息和已经投递给消费者的消息,RabbitMQ不会对消息设置过期时间,若消费该消息的消费者连接断开就重新投递给消费者。因为RabbitMQ允许消费者消费一条消息很久。第四章 RabbitMQ进阶
mandatory与immediate参数 mandatory参数:当值为true时,交换器无法根据自身的类型和路由键找到一个符合条件的队列,就会调用Basic.Return命令将消息返回给生产者,生产者可调用channel.addReturnListener添加监听器获取没有被正确路由的消息。若为false,则消息直接被丢弃。immedate参数:当值为true时,若交换器在消息路由到队列时发现队列上并不存在任何消费者,该消息不会存入队列,当与路由键匹配的所有队列中都没有消费者时,会调用Basic.Return返回给生产者。但在RabbitMQ3.0之后不支持该参数,因为会影响镜像队列的性能,增加了代码复杂性,建议采用TTL和DLX的方法替代。 备份交换器:可在调用channel.exchangeDeclare()方法时添加alternate-exchange参数实现,也可通过策略的方式实现,但参数优先级更高。备份交换器建议设置为fanout类型。若与mandatory参数同时使用,则mandatory参数无效。过期时间(TTL):对消息和队列设置TTL。设置消息的TTL的两种方式 队列中所有消息过期时间相同: 通过队列属性channel.queueDeclare的x-message-ttl设置,若将TTL设置为0,则表示若消息不能直接投递到消费者就会被立刻丢弃,可部分替代immediate参数的功能(返回消息的功能可用死信队列实现)通过channel.queueDeclare方法中的x-expires参数设置,该参数可控制队列被自动删除前处于未使用状态,即队列上没有任何的消费者,队列也没有被重新声明,在过期时间段内也未调用过Basic.Get命令 对消息进行单独设置:在channel.basicPublish方法加入expiration参数,每条消息的TTL可以不同)。如果同时使用则取较小值。 死信队列(DLX):当消息在一个队列中变为死信之后,它能被重新发送到另一个交换器中。通过在channel.queueDeclare方法设置x-dead-letter-exchange参数添加DLX。消息变为死信的情况: 消息被拒绝,并且设置requeue参数为false;消息过期;队列达到最大长度。 延迟队列:存储对应的延迟消息,即当消息被发送以后,并不想让消费者立刻拿到消息,而是等待特定时间后才拿到这个消息进行消费。RabbitMQ本身没有直接支持延迟队列的功能,可通过DLX和TTL实现,优先级队列:具有高优先级的队列具有高的优先权从而具备优先被消费的特权。可通过设置队列的x-max-priority参数实现。默认最低0。RPC实现:在请求消息中发送一个回调队列用来接收响应的消息。BasicProperties类常用的两个属性:replyTo用来设置一个回调队列,correlationId用来关联请求和调用RPC之后的回复。持久化:分为交换器、队列、消息的持久化durable参数。为了确保消息不丢失,可将消费队列的autoACK参数设置为false,引入镜像队列机制等。生产者确认的解决方式:事务机制或发送方确认机制。 事务机制:channel.txSelect、channel.txCommit、channel.txRollback,会降低消息吞吐量。发送方确认机制:生产者调用channel.confirmSelect方法将信道设置为confirm模式,调用channel.waitForConfirm方法等待服务端确认。也可使用批量confirm或异步confirm的方法。注:事务与confirm机制互斥,若都开启会报错。两种确认机制能保证消息正确的发送至交换器,但若此交换器没有匹配的队列,消息就会丢失,所以可配置mandatory参数或alternate参数来提高消息的可靠性。 消费端要点介绍:消息分发、消息顺序性、弃用QueueingConsumer消息分发的方式:轮询或channel.basicQoS方法允许限制信道上的消费者所能保持的最大未确认消息的数量。但对拉模式的消费方式无效。消息顺序性:若发生异常事务回滚、延迟队列、设置了优先级的消息不能保证顺序消费,若要保证消息的顺序性,需要业务保证,比如在消息内加入全局有序标识。弃用QueueingConsumer:有内存溢出的问题、非事件驱动。同步递归调用会产生死锁、会拖累同一个Connection下的所有信息,使其性能降低。消息传输保障:RabbitMQ支持最多一次和最少一次层级。第五章 Rabbit管理
rabbitmqctl常用命令多租户与权限,vhost:每一个RabbitMQ服务器都能创建虚拟的消息服务器。每一个vhost本质上是一个独立的小型RabbitMQ服务器,拥有自己独立的队列、交换器、绑定关系和自己独立的权限等,vhost之间是绝对隔离的。RabbitMQ默认创建的vhost为/。管理方式: Web端管理:默认地址localhost:15672,用户名密码:guest/guestHTTP API接口管理:可通过curl命令调用接口完成队列的创建:curl -i -u root:root123 -H “content-type:application/json” -XPUT -d ‘{“auto_delete”:false, “durable”:true, “node”:”rabbit@node2”}’ http://127.0.0.1:15672/api/queue/%2F/queue,其中%2F表示默认的vhost,即/,
第六章 RabbitMQ配置
环境变量:可在rabbit-env.conf环境变量的定义文件中设置,但shell环境优先。配置文件:RabbitMQ在启动时会打印相关信息,其中config file(s)是目前配置文件所在的路径运行时参数和策略: vhost级别的参数对应的命令有三种:set_parameter、list_parameter、clear_parameterglobal级别对应的命令有:set_global_parameter name value、list_global_parameter、clear_global_parameter name第七章 RabbitMQ运维
集群搭建:配置各个节点的hosts文件;编辑RabbitMQ的cookie文件(相当于密钥令牌),以确保各个节点的cookie文件使用的是同一个值;配置集群,通过rabbitmqctl、rabbitmq.config配置文件或rabbitmq-autocluster插件配置。集群节点的类型:分为内存节点或磁盘节点,加入节点时–ram参数指定为内存节点。可使用rabbitmqctl change_cluster_node_type {disc, ram}命令切换节点的类型。但在集群中至少有一个磁盘节点。查看服务日志:开启、关闭RabbitMQ、创建集群都会输出日志。单节点故障恢复:故障可分为硬件故障、网络异常、进程异常等。集群迁移:RabbitMQ集群迁移包括元数据重建、数据迁移、及客户端的切换 元数据重建:在新的集群中创建原集群的队列、交换器;绑定关系、vhost、用户和权限等数据信息。可通过web管理界面的高效方式重建。数据迁移:先从原集群中将数据消费出来,存入一个缓存区中,另一个线程读取缓存区中的消息再发布到新的集群中。 自动化迁移:将集群资源的使用分为三个部分:客户端、集群、ZooKeeper配置管理。集群监控:通过HTTP API接口或客户端监控。第八章 跨越集群的界限
分布式部署的方式:集群、Federation和Shovel。Federation插件的设计目标:是RabbitMQ在不同的Broker节点之间进行消息传递而无须建立集群。Federation插件可让多个交换器或者队列进行联邦。联邦交换器:联邦队列:可在多个broker节点或集群之间为单个队列提供负载均衡的功能。北京的clientB向广州的exchangeA发消息,会有一定的延迟,尤其是开启了publisher confirm或事务机制。可通过Federation link插件解决,该插件会在broker1上建立一个同名的交换器exchangeA,同时建立一个内部的交换器exchangeA->broker3B,并通过路由键rkA将这两个交换器绑定,还会建立一个队列federation:exchangeA->broker3B与交换器exchangeA->broker3B绑定,对外部来说这条federation link建立在broker1的exchangeA与broker3的exchangeA之间。之后,北京的ClientB可以连接到broker1并向exchangeA发送消息,而消息会通过Federation link转发到broker3的交换器exchangeA中。最终消息会存入与exchangeA绑定的队列queueA中。经过Federation link转发的消息会带有特殊的headers属性标记。
Federation的使用,需要配置一个或多个upstream(可通过federation management插件来完成),每个upstream均定义了到其它节点的Federation link,还需要定义匹配交换器或队列的一种/多种策略。Federation upstream的信息全部保存在RabbitMQ的Mnesia数据库中,包括用户信息、权限信息、队列信息等Federation的3种级别配置 Upstreams:每个upstream用于定义与其他Broker建立连接的信息。Upstream sets:每个upstream set用于对一系列使用Federation功能的upstream进行分组。Policies:每一个Policy会选定一组交换器、队列或全选,作用于一个单独的upstream或upstream set之上。 Shovel:能够可靠、持续地从一个Broker中的队列拉取数据并转发至另一个Broker中的交换器Shovel的优点:松耦合、支持广域网、高度定制。queue1和queue2原来在broker2中,由于某种需求将其配置为federated queue并将broker1作为upstream,Federation插件会在broker1上创建同名的队列queue1和queue2,与broker2中的两个队列分别建立两条单向独立的Federation link。当有消费者ClientA连接broker2消费,但queue1无消息堆积或被消费完了,则会通过Federation link拉取broker1中queue1中的消息,然后存储到本地,之后再被ClientA消费。消费者既可以消费broker2中的队列又可以消费broker1中的队列,这种分布式队列的部署可以提高单个队列的容量。与federated exchange不同,一条消息可以在联邦队列间转发无限次。
Shovel的部署方式:静态与动态 静态方式:rabbitmq.config配置文件动态方式:会保存到RabbitMQ的Mnesia数据库中。每一个Shovel link都由一个相应的Parameter定义,这个Parameter可通过rabbitmactl工具、API接口或Web管理界面设置 实例:当消息堆积严重时(千万级别),可通过Shovel将消息转发至备份集群中。Federation/Shovel与集群的区别和联系queue1和exchange2之间配置一个Shovel link,一条消息从Client发送到exchange1,最终会存储在queue2中。通常使用Shovel时配置队列作为源端,交换器作为目的端。
第九章 RabbitMQ高阶
存储机制:持久层包含两个部分:队列索引和消息存储。rabbit_queue_index负责维护队列中落盘消息的消息,包括消息的存储地点、是否已被交付给消费者等,每个队列都有与之对应的一个rabbit_queue_index。而rabbit_msg_store以键值对的形式存储消息,它被所有的队列共享,在每个节点中有且只有一个。队列的结构:队列由rabbit_amqqueue_process和backing_queue组成,rabbit_amqqueue_process负责接收生产者发布的消息、向消费者交付消息、处理消息的确认等。backing_queue是消息存储的具体形式和引擎,并向rabbit_amqqueue_process提供相关的接口以供调用。RabbitMQ的队列消息的4种状态 alpha:消息内容和消息索引都存储在内存中beta:消息内容保存在磁盘中,消息索引保存在内存中gamma:消息内容保存在磁盘中,消息索引在磁盘和内存中都有。(只有持久化消息才有的状态)delta:消息内容和索引都在磁盘中。 惰性队列:3.6.0及之后,尽可能的将消息存入磁盘中,而在消费者消费到相应的消息时才会被加载到内存中。它的设计目标是能够支持更多的消息存储。队列的两种模式:default和lazy。内存及磁盘告警:当内存使用超过配置的阈值或磁盘剩余空间低于配置的阈值时,RabbitMQ都会暂时阻塞客户端的连接并停止接收从客户端发来的消息。心跳检测也会失效。流控:基于信用证算法的流控机制来限制发送消息的速率来避免消息的发送速率过快而导致服务器难以支撑的场景流控机制作用于Connection、Channel、Queue。其中rabbit_reader:Connection的处理进程,负责接收、解析AMQP协议数据包等rabbit_channel:Channel的处理进程,负责处理AMQP协议的各种方法等rabbit_amqqueue_process:队列的处理进程,负责实现队列的所有逻辑。rabbit_msg_store:负责实现消息的持久化。 镜像队列:可将队列镜像到集群中的其他Broker节点之上,保证服务的可用性。所有对master的操作都会通过组播GM(Guaranteed Multicast)的方式同步到各个slave中。GM模块实现的是一种可靠的组播通信协议,该协议保证组播消息的原子性。 每个节点都会监控位于自己左右两边的节点。