当前位置: 首页 > 后端技术 > Java

教你搭建RabbitMQ集群

时间:2023-04-01 23:49:22 Java

@[toc]单台RabbitMQ肯定无法做到高可用。如果你想要高可用性,你必须使用集群。今天松哥就来和大家聊一聊RabbitMQ集群的搭建。1.两种模式说到集群,你的第一个问题可能是,如果我有一个RabbitMQ集群,我的消息集群中的每个实例是否都保存一份?这其实涉及到RabbitMQ集群的两种模式:commonclustermirrorcluster1.1commoncluster普通集群模式是将RabbitMQ部署到多台服务器上,每台服务器启动一个RabbitMQ实例,多个实例相互通信。这时候我们创建的队列Queue,它的元数据(主要是Queue的一些配置信息)会在所有的RabbitMQ实例中同步,但是队列中的消息只会存在于一个RabbitMQ实例上,不会同步到其他的队列。我们在消费消息的时候,如果连接到另一个实例,那个实例会通过元数据定位到Queue的位置,然后访问Queue所在的实例,拉取数据发送给消费者。这种集群可以提高RabbitMQ的消息吞吐量,但是不能保证高可用,因为一旦一个RabbitMQ实例挂掉,消息就无法访问了。如果消息队列是持久的,它可以在RabbitMQ实例恢复后继续。访问过;如果消息队列没有持久化,消息就会丢失。大体流程图如下:1.2镜像集群与普通集群最大的区别在于Queue数据和原始数据不再分别存储在一台机器上,而是同时存储在多台机器上。也就是说,每个RabbitMQ实例都有一份镜像数据(copydata)。每写入一条消息,数据就会自动同步到多个实例,这样一旦其中一台机器出现故障,其他机器仍有一份数据可以继续提供服务,实现了高可用。大致流程图如下:1.3节点类型RabbitMQ中有两种类型的节点:RAM节点:内存节点存储了内存中vhost的所有队列、交换机、绑定、用户、权限和元数据定义。优点是可以使开关和队列声明等操作更快。磁盘节点:将元数据存储在磁盘上。单节点系统只允许磁盘类型的节点,以防止RabbitMQ重启时系统配置信息丢失。RabbitMQ要求集群中至少有一个磁盘节点,其他节点可以是内存节点,当一个节点加入或离开集群时,至少要通知一个磁盘节点发生变化。如果集群中唯一的磁盘节点挂掉了,集群还能运行,但是其他的操作(增删改查)要等到节点恢复后才能进行。为了保证集群信息的可靠性,或者不确定是使用磁盘节点还是内存节点时,建议直接使用磁盘节点。2.搭建普通集群2.1预备知识了解了大概的结构之后,我们接下来来搭建集群。让我们从一个普通的集群开始,我们将使用docker来构建它。在构建之前,有两个准备工作需要知道:构建集群时,节点中的ErlangCookie值必须保持一致。默认情况下,该文件位于/var/lib/rabbitmq/.erlang.cookie中。我们是使用docker创建一个RabbitMQ容器,你可以为它设置相应的cookie值。RabbitMQ通过主机名连接服务,必须保证主机名能ping通。您可以通过编辑/etc/hosts手动添加主机名和IP对应关系。如果无法ping通主机名,就会导致RabbitMQ服务启动失败(如果我们是在不同服务器上搭建RabbitMQ集群,需要注意这一点,接下来2.2小结,我们将使用Docker容器连接链接来实现容器访问之间的连接,略有不同)。2.2开始构建执行以下命令创建三个RabbitMQ容器:dockerrun-d--hostnamerabbit01--namemq01-p5671:5672-p15671:15672-eRABBITMQ_ERLANG_COOKIE="javaboy_rabbitmq_cookie"rabbitmq:3-managementdockerrun-d--hostnamerabbit02--namemq02--linkmq01:mylink01-p5672:5672-p15672:15672-eRABBITMQ_ERLANG_COOKIE="javaboy_rabbitmq_cookie"rabbitmq:3-managementdockerrun-d--hostnamerabbit03--namemq03--linkmq01:mylink02--linkmq02:mylink03-p5673:5672-p15673:15672-eRABBITMQ_ERLANG_COOKIE="javaboy_rabbitmq_cookie"rabbitmq:3-管理运行结果如下:三个节点现在都启动了,注意在mq02和mq03,--link参数用于实现容器连接。对这个参数有不明白的可以在公众号江南电鱼后台回复docker。松哥写的docker入门教程讲到这个。我不会在这里详细介绍。另外需要注意的是,mq03容器必须能够同时连接到mq01和mq02。接下来进入mq02容器,首先查看hosts文件,可以看到我们配置的容器连接已经生效:以后在mq02容器中,可以通过mylink01或者rabbit01访问mq01容器。接下来我们开始集群的配置。执行以下命令将mq02容器加入集群:rabbitmqctlstop_apprabbitmqctljoin_clusterrabbit@rabbit01rabbitmqctlstart_app接下来输入以下命令查看集群状态:rabbitmqctlcluster_status可以看到集群中已经有两个节点了。接下来用同样的方法将mq03添加到集群中:rabbitmqctlstop_apprabbitmqctljoin_clusterrabbit@rabbit01rabbitmqctlstart_app接下来我们可以查看集群信息:可以看到此时集群中已经有3个节点了。其实这个时候我们也可以通过网页查看集群信息。在Web上三个RabbitMQ实例的首页,我们可以看到如下内容:2.3代码测试下面我们简单测试一下集群。我们创建一个名为mq_cluster_demo的父项目,然后在其中创建两个子项目。第一个子项目名为provider,它是一个消息生产者。它在创建时引入了Web和RabbitMQ依赖,如下:然后配置applicaiton.properties,内容如下(注意集群配置):spring.rabbitmq.addresses=localhost:5671,localhost:5672,localhost:5673spring.rabbitmq.username=guestspring.rabbitmq.password=guest接下来提供一个简单的队列,如下:@ConfigurationpublicclassRabbitConfig{publicstaticfinalStringMY_QUEUE_NAME="my_queue_name";publicstaticfinalStringMY_EXCHANGE_NAME="my_exchange_name";publicstaticfinalStringMY_ROUTING_KEY="my_queue_name";@BeanQueuequeue(){returnnewQueue(MY_QUEUE_NAME,true,false,false);}@BeanDirectExchangedirectExchange(){returnnewDirectExchange(MY_EXCHANGE_NAME,true,false);}@BeanBindingbinding(){returnBindingBuilder.bind(queue()).to(directExchange()).with(MY_ROUTING_KEY);}}这个没什么好说的,都是基础内容,接下来我们会在测试单元中的消息发送测试:@SpringBootTestclassProviderApplicationTests{@AutowiredRabbitTemplaterabbit模板;@TestvoidcontextLoads(){rabbitTemplate.convertAndSend(null,RabbitConfig.MY_QUEUE_NAME,"江南小雨你好");}}这条消息发送成功后,在RabbitMQweb管理端,我们会看到三个RabbitMQ实例上会显示一条消息,但实际上消息本身只存在于一个RabbitMQ实例中。接下来,我们将创建一个消息消费者。消息消费者的依赖和配置与消息生产者完全一致,这里不再赘述。向消息消费者添加消息接收器:@ComponentpublicclassMsgReceiver{@RabbitListener(queues=RabbitConfig.MY_QUEUE_NAME)publicvoidhandleMsg(Stringmsg){System.out.println("msg="+msg);}}当消息消费者启动成功后,该方法只收到一条消息,进一步验证了我们搭建的RabbitMQ集群没有问题。2.4逆向测试接下来松哥再给出两个反例来证明消息没有同步到其他RabbitMQ实例。确保三个RabbitMQ实例都已启动,关闭Consumer,然后通过provider发送消息。消息发送成功后,关闭mq01实例,再启动Consumer实例。这个时候Consumer实例不会消费消息,而是会报错说mq01实例连接不上,这个例子可以说明消息在mq01上,但是没有同步到另外两个MQ上。反之,如果provider在发送消息成功后,并没有关闭mq01实例,而是关闭了mq02实例,那么你会发现消息的消费并不受影响。3、构建镜像集群所谓镜像集群模式不需要额外构建,只需要我们将队列配置为镜像队列即可。这个配置可以通过网页配置,也可以通过命令行配置。我们分开来看。3.1网页端配置镜像队列首先我们看一下如何在网页端配置镜像队列。点击Admin选项卡,然后点击右侧的Policies,再点击Add/updateapolicy,如下图:接下来添加一个策略,如下图:各参数含义如下:名称:策略的名称。Pattern:队列的匹配模式(正则表达式)。definition:镜像定义,主要有三个参数:ha-mode、ha-params、ha-sync-mode。ha-mode:表示镜像队列的模式,有效值为all,确切地说,nodes。其中,all表示对集群中的所有节点进行镜像(这是默认的);exactly表示对指定数量的节点进行镜像,节点数量由ha-params指定;nodes表示在指定的节点上进行镜像,节点名通过ha-params指定。ha-params:ha-mode模式需要的参数。ha-sync-mode:同步队列中的消息,有效值为automatic和manual。priority为可选参数,表示策略的优先级。配置完成后,点击下方的添加/更新策略按钮即可完成策略的添加,如下:添加完成后,我们可以进行简单的测试。首先确认三个RabbitMQ都启动了,然后使用上面的provider向消息队列发送消息。发送后关闭mq01实例。接下来,启动消费者。此时发现消费者可以完成消息的消费(注意和之前的逆向测试不同),说明镜像队列构建成功。3.2命令行配置镜像队列命令行配置格式如下:rabbitmqctlset_policy[-pvhost][--prioritypriority][--apply-toapply-to]{name}{pattern}{definition}给一个简单的配置案例:rabbitmqctlset_policy-p/--apply-toqueuesmy_queue_mirror"^"'{"ha-mode":"all","ha-sync-mode":"automatic"}'4.总结OK,这是宋哥给大家分享的RabbitMQ中的集群搭建。有兴趣的朋友不妨试试看~