在参与的平台开发中,使用RabbitMq消息队列用于业务解耦、流量削峰和延迟队列定时任务。本文在实际使用的基础之上,参考相关资料文献,对RabbitMq的相关架构概念等进行总结,并实际搭建部署RabbitMq单机、集群以及镜像配置等,重新学习以及使用RabbitMq,加深对RabbitMq的理解,同时也为后来者提供参考借鉴,文中不免疏漏之处,望读者予以指正,不胜感激!
1. RabbitMq概念RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等,支持AJAX。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。
AMQP,即Advanced Message Queuing Protocol,高级消息队列协议,是应用层协议的一个开放标准,为面向消息的中间件设计。消息中间件主要用于组件之间的解耦,消息的发送者无需知道消息使用者的存在,反之亦然。
AMQP的主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。
(1)Message消息,消息是不具名的,它由消息头和消息体组成。消息体是不透明的,而消息头则由一系列的可选属性组成,这些属性包括routing-key(路由键)、priority(相对于其他消息的优先权)、delivery-mode(指出该消息可能需要持久性存储)等。
(2)Publisher消息的生产者,也是一个向交换器发布消息的客户端应用程序。
(3)Exchange交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。
(4)Binding绑定,用于消息队列和交换器之间的关联。一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表。
(5)Queue消息队列,用来保存消息直到发送给消费者。它是消息的容器,也是消息的终点。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走。
(6)Connection网络连接,比如一个TCP连接。
(7)Channel信道,多路复用连接中的一条独立的双向数据流通道。信道是建立在真实的TCP连接内地虚拟连接,AMQP命令都是通过信道发出去的,不管是发布消息、订阅队列还是接收消息,这些动作都是通过信道完成。因为对于操作系统来说建立和销毁TCP都是非常昂贵的开销,所以引入了信道的概念,以复用一条TCP连接。
(8)Consumer消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。
(9)Virtual Host虚拟主机,表示一批交换器、消息队列和相关对象。虚拟主机是共享相同的身份认证和加密环境的独立服务器域。每个 vhost 本质上就是一个mini版的RabbitMQ服务器,拥有自己的队列、交换器、绑定和权限机制。vhost 是AMQ 概念的基础,必须在连接时指定,RabbitMQ 默认的vhost是 / 。
(10)Broker表示消息队列服务器实体。
(1)直接式交换器类型(Direct)
Direct Exchange:直接交互式处理路由键。
需要将一个队列绑定到交换机上,要求该消息与一个特定的路由键完全匹配,这是一个完整的匹配。路由键就是BindingKey。
(2)广播式交换机类型(Fanout)
Fanout Exchange:广播式路由键。只需要简单的将队列绑定到交换机上。
一个发送到交换机的消息都被转发到与该交换机绑定的所有队列上。
很像子网广播,每台子网内的主机都获得了一份复制的消息。Fanout交换机转发消息是最快的。
(3)主题式交换金类型(Topic)
Topic Exchange:主题式交换器。
通过消息的路由关键字和绑定关键字的模式匹配,将消息路由到被绑定的队列中。
这种路由器类型可以被用来支持经典的发布/订阅消息传输类型——使用主题名字空间作为消息寻址模式,将消息传递给那些部分或者全部匹配主题模式的多个消费者。
主题交换器类型的工作方式如下:绑定关键字用零个或多个标记构成,每一个标记之间用“.”字符分隔。绑定关键字必须用这种形式明确说明,并支持通配符:“*”匹配一个词组,“#”零个或多个词组。
RabbitMQ的集群节点包括内存节点、磁盘节点。内存节点就是将所有数据放在内存,磁盘节点将数据放在磁盘。不过,如果在投递消息时,打开了消息的持久化,那么即使是内存节点,数据还是安全的放在磁盘。
一个rabbitmq集群中可以共享 user,vhost,queue,exchange等,所有的数据和状态都是必须在所有节点上复制的,一个例外是,那些当前只属于创建它的节点的消息队列,尽管它们可见且可被所有节点读取。rabbitmq节点可以动态的加入到集群中,一个节点它可以加入到集群中,也可以从集群环集群会进行一个基本的负载均衡。 集群中有两种节点:
内存节点:只保存状态到内存(一个例外的情况是:持久的queue的持久内容将被保存到disk)
磁盘节点:保存状态到内存和磁盘。
内存节点虽然不写入磁盘,但是它执行比磁盘节点要好。集群中,只需要一个磁盘节点来保存状态就足够了。
如果集群中只有内存节点,那么不能停止它们,否则所有的状态,消息等都会丢失。
RabbitMQ是用erlang开发的,集群非常方便,因为erlang天生就是一门分布式语言,但其本身并不支持负载均衡。 Rabbit模式大概分为以下三种:单一模式、普通模式、镜像模式:
1.单一模式:最简单的情况,非集群模式。2.普通模式:默认的集群模式。
对于Queue来说,消息实体只存在于其中一个节点,A、B两个节点仅有相同的元数据,即队列结构。
当消息进入A节点的Queue中后,consumer从B节点拉取时,RabbitMQ会临时在A、B间进行消息传输,把A中的消息实体取出并经过B发送给consumer。
所以consumer应尽量连接每一个节点,从中取消息。即对于同一个逻辑队列,要在多个节点建立物理Queue。否则无论consumer连A或B,出口总在A,会产生瓶颈。
该模式存在一个问题就是当A节点故障后,B节点无法取到A节点中还未消费的消息实体。 如果做了消息持久化,那么得等A节点恢复,然后才可被消费;如果没有持久化的话,然后就没有然后了……3.镜像模式:把需要的队列做成镜像队列,存在于多个节点,属于RabbitMQ的HA方案。
该模式解决了上述问题,其实质和普通模式不同之处在于,消息实体会主动在镜像节点间同步,而不是在consumer取数据时临时拉取。
该模式带来的副作用也很明显,除了降低系统性能外,如果镜像队列数量过多,加之大量的消息进入,集群内部的网络带宽将会被这种同步通讯大大消耗掉。 所以在对可靠性要求较高的场合中适用。
3. RabbitMq部署
(1)安装环境以及版本
linux服务器: CentOS Linux release 7.2.1511 (Core)
rabbitmq版本: rabbitmq-server-generic-unix-3.9.4.tar.xz
erlang版本: otp_src_24.0.tar.gz
rabbitmq与erlang版本对应关系:https://www.rabbitmq.com/which-erlang.html
(2)安装erlang
(1)下载安装包
wget https://erlang.org/download/otp_src_24.0.tar.gz
(2)安装依赖
yum -y install gcc glibc-devel make ncurses-devel openssl-devel xmlto perl wget gtk2-devel binutils-devel
(3)解压、调整安装位置,以及安装路径
tar -zvxf otp_src_24.0.tar.gz
mv otp_src_24.0 /usr/local/
cd /usr/local/otp_src_24.0/
mkdir ../erlang
(4)配置安装路径、安装
./configure --prefix=/usr/local/erlang
make install
(5)校验安装成功
查看一下是否安装成功
ll /usr/local/erlang/bin添加环境变量,并刷新
echo 'export PATH=$PATH:/usr/local/erlang/bin' >> /etc/profile
source /etc/profile甩一条命令,进入erl控制命令
erl执行命令退出
halt().
(3)安装rabbitmq
(1)下载
wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.9.4/rabbitmq-server-generic-unix-3.9.4.tar.xz(2)安装解压工具xz解压安装包
yum install -y xz
xz -d rabbitmq-server-generic-unix-3.9.4.tar.xz
tar -vxf rabbitmq-server-generic-unix-3.9.4.tar(3)移动安装文件位置,配置环境变量
mv rabbitmq_server-3.9.4/ /usr/local/rabbitmq
echo 'export PATH=$PATH:/usr/local/rabbitmq/sbin' >> /etc/profile
source /etc/profile(4)启动命令
启动:
rabbitmq-server -detached停止:
rabbitmqctl stop状态:
rabbitmqctl status开启web插件
rabbitmq-plugins enable rabbitmq_managementweb访问
http://192.168.65.155:15672/本机localhost访问默认账户密码 guest/guest(5)用户管理配置权限
查看所有用户
rabbitmqctl list_users添加一个用户
rabbitmqctl add_user test_user 123456配置权限
rabbitmqctl set_permissions -p "/" test_user ".*" ".*" ".*"查看用户权限
rabbitmqctl list_user_permissions test_user设置tag
rabbitmqctl set_user_tags test_user administrator删除用户(安全起见,删除默认用户)
rabbitmqctl delete_user guest(6)重启后,即可使用账户密码test_user 123456访问
http://192.168.65.155:15672/
这里选用三台主机,主机名分别是hidden1, hidden2, hidden3
1 在这三台机器中安装rabbitmq-server, 参考上面。2 读取其中一个节点的COOKIE, 并复制到其他节点(节点之间通过COOKIE确定相互是否可通信)。
COOKIE存放在/var/lib/rabbitmq/.erlang.COOKIE或者$HOME/.erlang.COOKIE中。3 逐个启动节点
rabbitmq-server -detached4 查看各节点的状态:
rabbitmqctl status
rabbitmqctl cluster_status5 配置各节点的hosts文件( vim /etc/hosts)
192.168.65.155 rabbit_node1
192.168.65.156 rabbit_node2
192.168.65.157 rabbit_node36 建立集群
以rabbit_node1为主节点,在rabbit_node2上:
rabbitmqctl stop_app
rabbitmqctl reset
rabbitmqctl join_cluster rabbit@rabbit_node1
rabbitmqctl start_app
rabbit_node3上的操作与rabbit_node2的雷同。
最后通过rabbitmqctl cluster_status查看集群的状态信息:7 在访问web(http://192.168.65.155:15672)时,
如果在Overview中的Nodes部分看到“Node statistics not available”的信息,说明在该节点上web管理插件还未启用。
直接运行rabbitmq-plugins enable rabbitmq_management即可。注意事项:
(1)各个节点未启动之前拷贝.erlang.COOKIE文件;
(2)各节点之间防火墙端口号相互开放
默认端口说明
client端通信端口:5672
管理端口:15672
server间内部通信端口:25672
erlang发现端口:4369
上述配置的RabbitMQ默认集群模式,但并不包管队列的高可用性,尽管互换机、绑定这些可以复制到集群里的任何一个节点,然则队列内容不会复制。固然该模式解决一项目组节点压力,但队列节点宕机直接导致该队列无法应用,只能守候重启,所以要想在队列节点宕机或故障也能正常应用,就要复制队列内容到集群里的每个节点,须要创建镜像队列。
镜像队列是基于普通的集群模式的,所以还是得先配置普通集群,然后才能设置镜像队列,我们就以上面的集群接着做。
我是通过上面开启的网页的管理端来设置的镜像队列,也可以通过命令。
(1)网页设置方式:
1、点击admin菜单–>右侧的Policies选项–>左侧最下下边的Add/update a policy。
2、按照图中的内容根据自己的需求填写。
3、点击Add policy添加策略。
此时你就会来你的两台rabbitmq服务器的网页管理端amind菜单下看见刚才创建的队列了,下面我们来添加一个queues队列来看看效果,这里只是测试结果,其它的先不填写。
(2)通过命令设置镜像队列策略
在任意一个节点上执行:
rabbitmqctl set_policy ha-all "^" '{"ha-mode":"all"}'
将所有队列设置为镜像队列,即队列会被复制到各个节点,各个节点状态保持一致。查看策略:
rabbitmqctl list_policies
此时镜像集群就已经完成了,可以在任意节点上创建队列,看看其他两个节点是否会同步。
[1] https://www.cnblogs.com/guanghe/p/11023467.html
[2] https://www.jianshu.com/p/79ca08116d57
[3] https://www.cnblogs.com/fengyumeng/p/11133924.html
[4] https://blog.csdn.net/bbwangj/article/details/82954097