一:MQ(Message queuing)简介
1.1:MQ由来
Message Queue 的需求由来已久,在 19 世纪 80 年代金融交易中,美国高盛等公
司采用 Teknekron 公司的产品,当时的 Message queuing 软件叫做(the information
bus(TIB),后来 TIB 被电信和通讯等公司采用,然后路透社收购了 Teknekron 公
司,再然后 IBM 公司开发了 MQSeries,并且微软也开发了 Microsoft Message
Queue(MSMQ),但是这些商业 MQ 供应商的问题是厂商锁定及使用价格高昂,
于是 2001 年,Java Message queuing 试图解决锁定和交互性的问题,但对应用来
说反而更加麻烦了,于是 2004 年,摩根大通和 iMatrix 开始着手 Advanced Message
Queuing Protocol (AMQP)开放标准的开发,2006 年,AMQP 规范发布,2007
年,Rabbit 技术公司基于 AMQP 标准开发的 RabbitMQ 1.0 发布。
路透社:
(Reuters,LSE:RTR,NASDAQ: RTRSY)是世界上最早创办的通讯社之一,也是目前英国最大的通讯社和西方四大通讯社之一,路透社是世界前三大的多媒体、新闻通讯社,提供各类新闻和金融数据,在 128 个国家运行。摩根大通集团,业界称西摩或小摩,总部位于美国纽约,总资产 2.5 万亿美元,总存款 1.5 万亿美元,占美国存款总额的 25%,分行 6000 多家,是美国最大金融服务机构之一。
iMatrix 是一个企业级的 JAVA 快速开发平台。
1.2:MQ定义
消息队列的目的是为了实现各个 APP 之间的通讯,APP 基于 MQ 实现消息的发送和接收实现应用程序之间的通讯,这样多个应用程序可以运行在不同的主机上,通过 MQ 就可以实现夸网络通信,因此 MQ 实现了业务的解耦和异步机
制。
1.3:MQ使用场合
消息队列作为高并发系统的核心组件之一,能够帮助业务系统结构提升开发效率
和系统稳定性,消息队列主要具有以下特点:
削峰填谷(主要解决瞬时写压力大于应用服务能力导致消息丢失、系统奔溃等问题)
系统解耦(解决不同重要程度、不同能力级别系统之间依赖导致一死全死)
提升性能(当存在一对多调用时,可以发一条消息给消息系统,让消息系统通知相关系统)
蓄流压测(线上有些链路不好压测,可以通过堆积一定量消息再放开来压测)
1.4:MQ分类
目前主流的消息队列软件有 RabbitMQ、kafka、ActiveMQ、RocketMQ 等,还有小众的消息队列软件如 ZeroMQ、Apache Qpid 等。
二:RabbitMQ:
https://www.rabbitmq.com/
https://www.aliyun.com/product/ons?spm=5176.234368.h2v3icoap.427.2620db25lcHi1Q&aly_as=Tz_Lue_o #阿里云消息队列
2.1:RabbitMQ简介
RabbitMQ 采用 Erlang 语言开发,Erlang 语言由 Ericson 设计,Erlang 在分布式编
程和故障恢复方面表现出色,电信领域被广泛使用。
https://www.erlang.org/
Broker: 接收和分发消息的应用,RabbitMQ Server 就是 Message Broker。
Virtual host: 出于多租户和安全因素设计的,把 AMQP 的基本组件划分到一个虚拟的分组中,类似于网络中的 namespace 概念,当多个不同的用户使用同一个RabbitMQ server 提供的服务时,可以划分出多个 vhost,每个用户在自己的 vhost创建 exchange/queue 等。
Connection: publisher/consumer 和 broker 之间的 TCP 连接。
Channel: 如果每一次访问 RabbitMQ 都建立一个 Connection,在消息量大的时候建立 TCP Connection 的开销将是巨大的,效率也较低。Channel 是在 connection内部建立的逻辑连接,如果应用程序支持多线程,通常每个 thread 创建单独的channel 进行通讯,AMQP method 包含了 channel id 帮助客户端和 message broker识别 channel,所以 channel 之间是完全隔离的。Channel 作为轻量级的 Connection极大减少了操作系统建立 TCP connection 的开销。
Exchange: message 到达 broker 的第一站,根据分发规则,匹配查询表中的 routing key,分发消息到 queue 中去。常用的类型有:direct (point-to-point), topic (publishsubscribe) and fanout (multicast)。Queue: 消息最终被送到这里等待 consumer 取走。
Binding: exchange 和 queue 之间的虚拟连接,binding 中可以包含 routing key。Binding 信息被保存到 exchange 中的查询表中,用于 message 的分发依据。
2.2:rabbitmq优势
基于 erlang 语言开发,具有高并发优点、支持分布式
具有消息确认机制、消息持久化机制,消息可靠性和集群可靠性高
简单易用、运行稳定、跨平台、多语言
开源
2.3:Queue的特性
消息基于先进先出的原则进行顺序消费
消息可以持久化到磁盘节点服务器
消息可以缓存到内存节点服务器提高性能
2.4:RabbitMQ 中的生产者消费者示例:
生产者发送消息到broker server(RabbitMQ),在Broker内部,用户创建Exchange/Queue,通过Binding规则将两者联系在一起,Exchange分发消息,根据类型/binding的不同分发策略有区别,消息最后到Queue中,等待消费者取走。
JMS 是在 2001 年发布的 Java 消息服务(Java Message Service)应用程序接口,是一个 Java 平台中关于面向消息中间件(MOM,message oriented middleware)的 API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。
三:RabbitMQ部署
3.1:RabbitMQ单机部署
https://www.rabbitmq.com/download.html #官网下载地址
https://github.com/rabbitmq/rabbitmq-server/releases #github 下载地址
3.1.1:下载erlang和rabbitmq安装包
wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.12.12/rabbitmq-server-3.12.12-1.suse.noarch.rpm
wget https://github.com/rabbitmq/erlang-rpm/releases/download/v26.2.1/erlang-26.2.1-1.el7.x86_64.rpm
3.1.2:安装erlang和rabbitmq
yum install -y socat erlang-26.2.1-1.el7.x86_64.rpm rabbitmq-server-3.12.12-1.suse.noarch.rpm
3.1.2:启动rabbimq
systemctl start rabbitmq-server
systemctl enable rabbitmq-server
3.1.3:RabbitMQ插件管理
https://www.rabbitmq.com/management.html
开启web界面插件:
# rabbitmq-plugins enable rabbitmq_management
5672:消费者访问的 端口
15672:web 管理端口
25672:集群状态通信端口
3.1.4:登录web管理界面
rabbitmq 从 3.3.0 开始禁止使用 guest/guest 权限通过除 localhost 外的访问
解决登录权限方式:
vim /usr/lib/rabbitmq/lib/rabbitmq_server-3.8.3/ebin/rabbit.app
39 {loopback_users, []}, #删除被禁止登陆的 guest 账户
systemctl restart rabbitmq-server.service #重启 rabbitmq 服务
3.2:RabbitMQ集群部署:
Rabbitmq集群分为两种方式:
普通模式: 创建好RabbitMQ集群之后的默认模式。
镜像模式: 把需要的队列做成镜像队列
普通模式集群模式:
queue创建之后,如果没有其他policy,消息实体只存在于其中一个节点,A、B两个Rabbitmq节点仅有相同的元数据,既队结构,但队列的数据仅保存有一份,即创建该队列的rabbitmq节点(A节点),当消息进入A节点的Queue中后,consumer从B节点拉取时,RabbitMQ会临时在A、B间进行消息传输,把A中的消息实体取出并经过B发送给consumer,所以consumer可以连接每一个节点,从中取消息,该模式存在一个问题就是当A节点故障后,B节点无法取到A节点中还未消费的消息实体。
镜像集群模式:
把需要的队列做成镜像队列,存在于多个节点,属于RabbitMQ的HA方案(镜像模式是在普通模式的基础上,增加一些镜像策略)
该模式解决了普通模式中的数据丢失问题,其实质和普通模式不同之处在于,消息实体会主动在镜像节点间同步,而不是在consumer取数据时临时拉取,该模式带来的副作用也很明显,除了降低系统性能外,如果镜像队列数量过多,加之大量的消息进入,集群内部的网络带宽将会被这种同步通讯大大消耗掉,所以在对可靠性要求较高的场合中适用,一个队列想做成镜像队列,需要先设置policy,然后客户端创建队列的时候,rabbitmq集群根据"队列名称"自动设置普通集群模式或镜像队列。
集群中有两种节点类型
内存节点: 只将数据保存到内存
磁盘节点: 保存数据到内存和磁盘
内存节点虽然不写入磁盘,但是它执行比磁盘节点要好,集群中,只需要一个磁盘节点来保存数据就足够了如果集群中只有内存节点,那么不能全部停止它们,否则所有数据消息在服务器全部停机之后都会丢失。
推荐设计架构
在一个rabbitmq集群里,有3台或以上机器,其中一台使用磁盘模式,其它节点使用内存模式,内存节点无访问速度更快,由于磁盘IO相对较慢,因此可作为数据备份使用
3.2.1:centos安装集群版RabbitMQ:
集群环境,三台服务器,具体IP如下:
172.16.10.22
172.16.10.23
172.16.10.24
3.2.1.1:主机名解析配置
各MQ服务器配置本地主机名解析
172.16.10.22 mq-server1 mq-server1.magedu.net
172.16.10.23 mq-server2 mq-server2.magedu.net
172.16.10.24 mq-server3 mq-server3.magedu.net
3.2.1.2:各服务器安装RabbitMQ:
yum install -y erlang rabbitmq
3.2.1.3:启动RabbitMQ服务:
Server1:
root@mq-server1:~# systemctl enable rabbitmq-server
root@mq-server1:~# systemctl start rabbitmq-server
Server2:
root@mq-server2:~# systemctl start rabbitmq-server
root@mq-server2:~# systemctl enable rabbitmq-server
Server3:
root@mq-server3:~# systemctl start rabbitmq-server
root@mq-server3:~# systemctl enable rabbitmq-server
3.2.1.4:创建RabbitMQ集群
RabbitMQ的集群是依赖于erlang的集群来工作的,所以必须先构建起erlang的集群环境,而Erlang的集群中各节点是通过一个magic cookie来实现的,这个cookie存放在/var/lib/rabbitmq/.erlang.cookie中,文件是400的权限,所以必须保证各节点cookie保持一致,否则节点之间就无法通信。
3.2.1.4.1:各服务器关闭RabbitMQ:
root@mq-server1:~# systemctl stop rabbitmq-server
root@mq-server2:~# systemctl stop rabbitmq-server
root@mq-server3:~# systemctl stop rabbitmq-server
在 mq-server1 同步.erlang.cookie 至其他两台服务器:
# scp /var/lib/rabbitmq/.erlang.cookie
172.16.10.23:/var/lib/rabbitmq/.erlang.cookie
# scp /var/lib/rabbitmq/.erlang.cookie
172.16.10.24:/var/lib/rabbitmq/.erlang.cookie
3.2.1.4.2:各服务器启动RabbitMQ
root@mq-server1:~# systemctl start rabbitmq-server
root@mq-server2:~# systemctl start rabbitmq-server
root@mq-server3:~# systemctl start rabbitmq-server
3.2.1.4.3:查看当前集群状态
3.7.X及早期版本单节点状态
root@mq-server1:~# rabbitmqctl cluster_status
Cluster status of node rabbit@mq-server1 ...
[{nodes,[{disc,['rabbit@mq-server1']}]},
{running_nodes,['rabbit@mq-server1']},
{cluster_name,<<"rabbit@mq-server1">>},
{partitions,[]},
{alarms,[{'rabbit@mq-server1',[]}]}]
root@mq-server2:~# rabbitmqctl cluster_status
Cluster status of node rabbit@mq-server2 ...
[{nodes,[{disc,['rabbit@mq-server2']}]},
{running_nodes,['rabbit@mq-server2']},
{cluster_name,<<"rabbit@mq-server2">>},
{partitions,[]},
{alarms,[{'rabbit@mq-server2',[]}]}]
root@mq-server3:~# rabbitmqctl cluster_status
Cluster status of node rabbit@mq-server3 ...
[{nodes,[{disc,['rabbit@mq-server3']}]},
{running_nodes,['rabbit@mq-server3']},
{cluster_name,<<"rabbit@mq-server3">>},
{partitions,[]},
{alarms,[{'rabbit@mq-server3',[]}]}]
3.8.X 版本单节点状态:
# rabbitmqctl cluster_status
Cluster status of node rabbit@mq-server2 ...
Basics
Cluster name: rabbit@mq-server2
Disk Nodes
rabbit@mq-server2
Running Nodes
rabbit@mq-server2
Versions
rabbit@mq-server2: RabbitMQ 3.8.3 on Erlang 22.3
3.2.1.4.4:创建RabbitMQ集群
在mq-server1作为内存节点添加到mq-server3,并作为内存节点,在mq
-server1执行一下命令:
root@mq-server1:~# rabbitmqctl stop_app #停止 app 服务
root@mq-server1:~# rabbitmqctl reset #清空元数据
Resetting node rabbit@mq-server1 ...
#将 rabbitmq-server1 添加到集群当中,并成为内存节点,不加--ram 默认是磁盘节点
root@mq-server1:~# rabbitmqctl join_cluster rabbit@mq-server3 --ram
Clustering node rabbit@mq-server1 with rabbit@mq-server3
root@mq-server1:~# rabbitmqctl start_app #启动 app 服务
Starting node rabbit@mq-server1 ...
completed with 3 plugins.
在mq-server2作为内存节点添加到mq-server3,并作为内存节点,在mq-server2执行一下命令:
root@mq-server2:~# rabbitmqctl stop_app
Stopping rabbit application on node rabbit@mq-server2 ...
root@mq-server2:~# rabbitmqctl reset
Resetting node rabbit@mq-server2 ...
root@mq-server2:~# rabbitmqctl join_cluster rabbit@mq-server3 --ram
Clustering node rabbit@mq-server2 with rabbit@mq-server3
root@mq-server2:~# rabbitmqctl start_app
Starting node rabbit@mq-server2 ...
completed with 0 plugins.
3.2.1.4.5:将集群设置为镜像模式:
只要在其中一台节点执行以下命令即可:
root@mq-server1:~# rabbitmqctl set_policy ha-all "#" '{"ha-mode":"all"}'
Setting policy "ha-all" for pattern "#" to "{"ha-mode":"all"}" with priority "0" for
vhost "/" ...
3.2.1.4.6:验证当前集群状态:
3.7.X及早期版本集群状态:
root@mq-server1:~# rabbitmqctl cluster_status
Cluster status of node rabbit@mq-server1 ...
[{nodes,[{disc,['rabbit@mq-server3']}, #集群中至少有一个节点是磁盘节点用于数
据持久化
{ram,['rabbit@mq-server2','rabbit@mq-server1']}]}, #集群中的内存节点
{running_nodes,['rabbit@mq-server2','rabbit@mq-server3','rabbit@mq-server1']},
{cluster_name,<<"rabbit@mq-server3">>}, #当前正在运行的节点
{partitions,[]},
{alarms,[{'rabbit@mq-server2',[]},
{'rabbit@mq-server3',[]},
{'rabbit@mq-server1',[]}]}]
3.8.X版本集群状态:
# rabbitmqctl cluster_status
Cluster status of node rabbit@mq-server3 ...
Basics
Cluster name: rabbit@mq-server1
Disk Nodes
rabbit@mq-server3
RAM Nodes
rabbit@mq-server1
rabbit@mq-server2
Running Nodes
rabbit@mq-server1
rabbit@mq-server2
rabbit@mq-server3
Versions
rabbit@mq-server1: RabbitMQ 3.8.3 on Erlang 22.3
rabbit@mq-server2: RabbitMQ 3.8.3 on Erlang 22.3
rabbit@mq-server3: RabbitMQ 3.8.3 on Erlang 22.3
3.2.2:web界面验证集群状态
3.2.2.1:web界面验证当前集群状态:
不启用 web 插件的 rabbitmq 服务器,会在 web 节点提示节点统计信息不可用
(Node statistics not available)
3.2.2.2:各服务器启动web插件
root@mq-server2:~# rabbitmq-plugins enable rabbitmq_management
Enabling plugins on node rabbit@mq-server2:
rabbitmq_management
The following plugins have been configured:
rabbitmq_management
rabbitmq_management_agent
rabbitmq_web_dispatch
Applying plugin configuration to rabbit@mq-server2...
The following plugins have been enabled:
rabbitmq_management
rabbitmq_management_agent
rabbitmq_web_dispatch
started 3 plugins.
root@mq-server3:~# rabbitmq-plugins enable rabbitmq_management
Enabling plugins on node rabbit@mq-server3:
rabbitmq_management
The following plugins have been configured:
rabbitmq_management
rabbitmq_management_agent
rabbitmq_web_dispatch
Applying plugin configuration to rabbit@mq-server3...
The following plugins have been enabled:
rabbitmq_management
rabbitmq_management_agent
rabbitmq_web_dispatch
started 3 plugins.
3.2.2.3:开启web插件后的集群状态
3.3:RabbitMQ常用命令
#创建vhost
root@mq-server1:~# rabbitmqctl add_vhost magedu
Adding vhost "magedu" ...
#列出所有vhost
root@mq-server1:~# rabbitmqctl list_vhosts
Listing vhosts ...
name
/
#列出所有队列
root@mq-server1:~# rabbitmqctl list_queues
Timeout: 60.0 seconds ...
Listing queues for vhost / ...
#删除指定vhost
root@mq-server1:~# rabbitmqctl delete_vhost magedu
Deleting vhost "magedu" ...
#添加账户 jack 密码为 123456
root@mq-server1:~# rabbitmqctl add_user jack 123456
Adding user "jack" ...
#更改用户密码
root@mq-server1:~# rabbitmqctl change_password jack 1234
Changing password for user "jack" ...
#设置jack用户对magedu的vhost有读写权限,三个点为配置正则、读和写
root@mq-server1:~# rabbitmqctl set_permissions -p magedu jack ".*" ".*" ".*"
Setting permissions for user "jack" in vhost "magedu" ...
3.4:RabbitMQ API:
https://rawcdn.githack.com/rabbitmq/rabbitmq-management/rabbitmq_v3_6_9/priv/www/api/index.html
3.5:RabbitMQ Cluster Monitor:
3.5.1:集群状态监控
# cat rabbitmq_memory.py
# !/bin/env python
# coding:utf-8
# Author: GaoFan
import subprocess
running_list = []
error_list = []
false = "false"
true = "true"
def get_status():
obj = subprocess.Popen(("curl -s -u guest:guest http://localhost:15672/api/nodes"), shell=True,
stdout=subprocess.PIPE)
data = obj.stdout.read()
data1 = eval(data)
for i in data1:
if i.get("running") == "true":
running_list.append(i.get("name"))
else:
error_list.append(i.get("name"))
def count_server():
if len(running_list) < 3: # 可以判断错误列表大于 0 或者运行列表小于 3,3 未总计的节点数量
print(101) # 100 就是集群内有节点运行不正常了
else:
print(50) # 50 为所有节点全部运行正常
def main():
get_status()
count_server()
if __name__ == "__main__":
main()
3.5.2:内存使用监控:
import subprocess
import sys
running_list = []
error_list = []
false = "false"
true = "true"
def get_status():
obj = subprocess.Popen(("curl -s -u gf:1 http://172.16.10.22:15672/api/nodes"),
shell=True,
stdout=subprocess.PIPE)
data = obj.stdout.read()
print(data)
data1 = eval(data)
for i in data1:
if i.get("name") == "rabbit@mq-server1":
print(i.get("mem_used"))
def main():
get_status()
if __name__ == "__main__":
main()
####赞助一下小编,以后更新文档会嘎嘎卖力########