「消息队列」RabbitMQ概述

版权声明:本文为博主原创文章,转载必须注明出处。 https://blog.csdn.net/jinixin/article/details/83964610

转载请注明出处: https://blog.csdn.net/jinixin/article/details/83964610

上篇文章谈了消息队列与AMQP是什么, 本篇文章将接着为大家介绍一款成熟的消息队列产品--RabbitMQ, 围绕其的安装, 配置, 管理以及搭建分布式集群来简单谈谈.

RabbitMQ是由Rabbit公司遵循AMQP协议开发的开源消息队列产品, 经过多年的迭代, 已经演变成稳定的老牌消息队列产品.

RabbitMQ是AMQP的一种实现, RabbitMQ Server是AMQP消息代理(Broker)的一种实现. RabbitMQ本质上是一个缓冲区, 其的大小只受所在主机内存和磁盘大小的限制.

安装RabbitMQ

1. mac的Homebrew方式安装

brew install rabbitmq

2. Ubuntu的包管理器方式安装

sudo apt-get install rabbitmq-server

3. 类unix系统的二进制方式安装

1) 请先注意安装Erlang, 再安装RabbitMQ. 此外注意RabbitMQ需与Erlang的版本相匹配, 具体版本匹配, 请点击这里.
2) 选择好Erlang版本后, 即可去Erlang官网下载. 解压安装包后, 可在README文件中找到安装步骤, 安装成功后请重启计算机.
3) Erlang安装完成后, 即可下载对应版本的RabbitMQ包. RabbitMQ不需要安装, 解压后即可运行sbin目录下的命令.

4. 其他方式(包括docker)安装

请点击这里查看

配置RabbitMQ

RabbitMQ有很多配置, 说实话我觉得现在连其的三分之一都没搞清楚, 但这一点也不影响我们使用它.

我们可以依照这些配置所属的不同文件将他们分为普通配置, 高级配置和环境配置三类, 其对应的文件分别为"rabbitmq.conf", "advanced.config"以及"rabbitmq-env.conf", 三个文件都被放置在RabbitMQ的配置目录下. 如果在配置目录下没有想要的配置文件, 手动创建即可. 修改配置后, 请重启RabbitMQ服务.

配置目录的默认路径为"RabbitMQ安装目录/etc/rabbitmq/", 如果你是直接解压RabbitMQ的, 则配置目录的默认路径为"RabbitMQ解压目录/etc/rabbitmq/".

1. 环境配置(rabbitmq-env.conf)

配置字段 配置字段的作用说明
LOG_BASE RabbitMQ日志的存储目录, 默认为"RabbitMQ安装目录/var/log/rabbitmq/".
NODENAME

当前节点名称, 这在集群中很有用. 默认为"rabbit@主机名", 其中主机名默认是"hostname -s"命令的结果.

RabbitMQ集群各节点间是通过主机名相互交流的, 因此该节点如果要和其他节点一起构成集群, 请务必确保主机名是域名(不能localhost)或IP, 即确保主机名可被同个子网中其他计算机ping通, 否则该节点将无法加入集群.

USE_LONGNAME 节点名是否使用长名称, 默认为"false". 短节点名样式为"rabbit@xx", 长节点名样式为"[email protected]". 如果该节点会加入集群, 我建议将该值置为"true".
NODE_IP_ADDRESS 默认会绑定到所有IP, 通过此配置可绑定到特定IP.
NODE_PORT 端口, 默认是5672.
CONFIG_FILE 获取普通配置文件(rabbitmq.conf)的路径.
ADVANCED_CONFIG_FILE 获取高级配置文件(advanced.config)的路径.
CONF_ENV_FILE 获取环境变量配置文件(rabbitmq-env.conf)路径.

上面我列举了自认为比较常用的几个配置, 所有的环境变量配置可点击这里获得. 环境配置除了使用文件的方式来处理, 也可将配置写入环境变量中, 但需要加上"RABBITMQ_"前缀, 比如"LOG_BASE"就要变为"RABBITMQ_LOG_BASE"才能写入环境变量.

2. 普通配置(rabbitmq.conf)

普通配置字段的介绍, 请点击这里. 普通配置的模板, 请点击这里.

3. 高级配置(advanced.config)

高级配置字段的介绍, 请点击这里. 高级配置的模板, 请点击这里.

4. 配置格式

3.7.0版本前仅支持使用Erlang语法进行配置, 3.7.0版本引入了sysctl格式. 相比于Erlang语法, sysctl格式更容易通过chef或puppet来产生, 也更便于人类阅读.
sysctl格式, 也就是"key=val"的格式, 用"#"表示注释. 额外请注意, 普通配置与环境配置支持Erlang语法与sysctl格式, 但高级配置只能用Erlang语法来搞, 官方给的解释是高级配置非常复杂, sysctl格式无法应付.

启动RabbitMQ

RabbitMQ服务可通过"RabbitMQ安装目录/sbin"下的"rabbitmq-server"命令来启动, 具体如下:

1. 前台启动

通过"rabbitmq-server"可在前台启动服务, 启动后当前命令行随即block住, 这表示RabbitMQ的消息代理已经成功运行.

2. 后台启动

通过"rabbitmq-server -detached"可以后台方式启动服务, 虽然当前命令行不会block住, 但会导致pid不能被写入pid文件, 从而收到"Warning: PID file not written; -detached was passed"的警告, 可忽略.

RabbitMQ服务启动后, 可通过"rabbitmqctl"命令来结束服务. 我猜测, Rabbit服务应该包含两部分, 其一是Erlang进程, 其二是RabbitMQ应用. "rabbitmq-server"将开启一个Erlang进程, 并在该进程上启动RabbitMQ应用.

RabbitMQ集群

集群一般由多个节点构成, 但不像其他的分布式系统有主从节点之分, RabbitMQ中所有节点都是一致的. RabbitMQ集群可通过多种方式构建, 比如下面会介绍到的自动节点发现, 或是通过rabbitmqctl手动搭建等. 集群中的一个节点异常或恢复后, 集群会自动下线/上线, 无需我们额外操作.

1. 集群的搭建条件

1) 所有待组网计算机(节点)必须在同一子网中. 子网是通过中继器或交换机相连接而组成的网络, 通俗点可认为是局域网.

2) 所有待组网计算机(节点)设置的主机名必须可以相互ping通, 具体原因我在"配置RabbitMQ-->环境配置"里提过.

3) 所有待组网计算机(节点)必须具有相同的Erlang和RabbitMQ版本, 以及相同的cookie(下面会提).

2. 镜像队列

虽然RabbitMQ组成集群后, 虚拟主机, 交换机, 用户会自动在集群的各个节点间拷贝, 但队列以及其中的消息数据只会存在于一个节点上.

你也许会问, 当我想获取某队列的数据, 为什么在任意节点上都可以访问到这个队列呢? 原因是当客户端连接某节点后, 如果该节点上没有目标队列, 该节点会自动将命令路由到目标节点的目标队列上, 并取回数据.

比如一个集群有A, B两个节点, A节点上有Animal队列, B节点上有Plant队列. 我想访问A节点Animal队列上的数据, 但连接到了集群的B节点, 由于Animal队列默认在B节点上没有拷贝, 所以B节点会替我连接A节点转达命令并取回数据, 而我全程是无感的.

这样也存在一个问题, 如果A节点发生了不可恢复的灾难, 即使数据设置了持久化, 但A上的数据仍会全部丢失, 这将是非常糟糕的一件事, 怎么预防呢? RabbitMQ提供了一种容错方法, 即开通镜像队列. 其的具体介绍与配置, 请点击这里.

3. cookie

在"集群的搭建条件"中, 我提到了cookie. 说实话, 刚看到cookie这个词的时候, 我一脸吃惊. 莫非说的就是存在于客户端的cookie? 用于请求时自动向服务器传输必要数据? 非也非也, 此cookie非彼cookie.

由于节点间要想交流, 必须具有相同的的秘钥, 该秘钥即Erlang cookie, 简称cookie. 该cookie不同于传统的客户端(浏览器)cookie, 其主要用于不同节点间互相识别身份, 只有具有相同cookie的节点, 才会被认为是同个集群的, 两者之间才可以互相交换数据. Erlang cookie的一般存储位置为"用户主目录/.erlang.cookie".

那这个cookie是由谁在什么时候产生的呢? 一般是当前节点的RabbitMQ服务启动时, 如果不存在"用户主目录/.erlang.cookie"这一文件, 则Erlang虚拟机会自动创建, 并随机赋值. Erlang cookie是一个长度不超过255的字符串.

4. 节点类型

节点按类型可以分为磁盘节点与内存节点.

1) 磁盘节点

磁盘节点顾名思义, 是将交换机, 队列以及消息都存储到硬盘上.

2) 内存节点

内存节点则是在内存中保存交换机, 队列这些数据, 因此其不会像磁盘节点那样频繁访问硬盘, 所以会有更好的表现. 但注意队列内的消息数据仍然会存储在硬盘上, 故生产或消费数据并不会得到性能提升, 但对增加/删除队列这类操作还是有点作用的.

内存节点在启动后会从集群的其他节点上同步数据, 因此请确保集群中至少存在一个磁盘节点. 内存节点非常脆弱, 一旦被关闭, 你将不能再重启它, 并会丢失所有数据. 因此建议一个集群为了性能, 可以部署少量的内存节点, 但不可以全部为内存节点.

3) 改变节点类型

我们可以在某个节点首次加入集群时, 通过"--ram"标志, 将其声明为内存节点, 如下:

rabbitmqctl stop_app
rabbitmqctl join_cluster --ram 节点名称
rabbitmqctl start_app

也可以改变集群已存在节点的类型:

rabbitmqctl change_cluster_node_type ram  # 将节点变为内存节点
rabbitmqctl change_cluster_node_type disc  # 将节点变为磁盘节点

6. 搭建集群

简单介绍三种, 前两种为通过配置自动搭建集群(未验证), 后一种为手动搭建集群(验证可行).

1) 自动节点发现

第一种是在普通配置文件中指明集群的所有节点:

cluster_formation.peer_discovery_backend = rabbit_peer_discovery_classic_config
cluster_formation.classic_config.nodes.1 = [email protected]  # 节点1
cluster_formation.classic_config.nodes.2 = [email protected]  # 节点2

第二种是在普通配置文件中配置DNS自动发现:

cluster_formation.peer_discovery_backend = rabbit_peer_discovery_dns
cluster_formation.dns.hostname = example.local  # 该域名下所有IP的对应主机都会成为一个节点

若一个域名关联了多个IP可以考虑使用这种方式, 即RabbitMQ服务通过DNS获得所有IP地址, 再通过IP反向找到域名. 比如上面配置中的example.local对应有两个IP地址: 192.168.100.1和192.168.100.2. 这些IP的反向DNS查找分别返回hostname1.example.local和hostname2.example.local. 因此该集群将包含两个节点: [email protected][email protected].

2) 通过rabbitmqctl手动搭建

假设同一子网中的node1节点与node2节点分别运行有RabbitMQ服务, 下面将把node2节点与node1节点组成集群:

[1] 首先确保node1与node2节点都已满足组成集群的条件
[2] 通过"rabbit-server"分别启动node1节点与node2节点上的RabbitMQ服务
[3] 在node2节点命令行上键入"rabbitmqctl stop_app", 用以关闭node2节点上的RabbitMQ应用, 但Erlang进程还活着
[4] 在node2节点命令行上键入"rabbitmqctl join_cluster node1的节点名称", 用以将node2节点加入node1所在集群
[5] 在node2节点命令行上键入"rabbitmqctl start_app", 用以打开node2节点上的RabbitMQ应用

7. 解散集群

解散集群一共有两种方式, 本地操作(验证可行)和远程操作(未验证). 下面会将"6"中创建的集群解散, 即把node2节点从当前集群中移出, 使其成为独立节点.

1) 本地操作

[1] 在node2节点命令行上键入"rabbitmqctl stop_app", 用于关闭node2节点上的RabbitMQ服务
[2] 在node2节点命令行上键入"rabbitmqctl reset", 用于重设node2节点, 此时集群已解散
[3] 在node2节点命令行上键入"rabbitmqctl start_app", 用于打开node2节点上的RabbitMQ服务

2) 远程操作

[1] 在node1节点命令行上键入"rabbitmqctl forget_cluster_node node2的节点名称", 用以从集群中移出node2节点.
[2] 执行完[1]中的操作后, node2节点还认为其和node1节点是同一集群的. 因此之后重启node2节点, 会报"Error: inconsistent_cluster: Node XX thinks it's clustered with node"的错误. 此时只要在node2节点的命令行上键入"rabbitmqctl reset"进行重置, 再重启即可.

杂谈

下面简单谈下RabbitMQ中的"消息确认机制"与"消息持久化", 这两个概念在上篇博文中就提过, 这里再提一次来加深印象.

1. 消息确认机制

为确保消息不丢失, RabbitMQ提供有消息确认机制, 默认是自动确认模式.

你也许会担心某条消息的处理可能会非常耗时, 这样一段时间后万一触发了RabbitMQ的超时, 进而导致RabbitMQ进行重发, 岂不是会导致消息被多次重复处理?

你不必担心, 因为只有消费者死亡时, 才会进行重发. 如果只是耗时很长, RabbitMQ会耐心等待的, 这也是AMQP所规定的.

2. 消息持久化

上面的消息确认机制只是保证了消息不会丢失, 但如果某节点的RabbitMQ服务奔溃后, 该怎么让她回忆起曾经在一起的美好时光呢? 哦不是, 跑题了抱歉, 我还没有过女朋友呢, 额不是, 收住收住, 严肃点.

该怎么找回节点上的交换机, 队列以及队列里的消息呢?

这个AMQP做了规定, 需要将交换机, 队列与消息都标记为持久化, 而后RabbitMQ会自动将数据存储在磁盘上. 注意这三者(交换机, 队列, 消息)都要标记为持久化, 缺一不可.

而具体怎么来设置, 这个将在本系列的第四篇文章中结合具体语言(Python)与库(pika)给出.

文中如有不当之处, 还望包容和指出, 感谢.

猜你喜欢

转载自blog.csdn.net/jinixin/article/details/83964610