MQ的全英文名称是Message Queue,也就是消息队列,简单来说就是一个以的队列结构来存储消息的中间件。
总结:扛不住
假设A应用能提供文本图谱抽取能力,但是抽取能力很差,甚至需要几分钟才能把一段几千字的文本抽取完成。而B应用需要把很多段不同文本发送给A应用进行抽取,这时候使用API调用方法不太好,毕竟A应用响应时间也是很长的,不能让B应用等待这么久。另外A应用接收能力有限,所以不能把A应用给压爆了,这个时候就可以使用消息队列。
B应用把需要抽取的文字发到消息队列中,然后它就不需要管了,之后A应用去消息队列获取待抽取文本,然后将抽取完成的图谱结果放在消息队列中,之后B应用取出结果就可以了,即使B应用发送再多的文本也不用害怕了,毕竟消息在消息队列中存放,A应用只需要根据自己服务的能力去进行抽取就可以了,这样就可以完成流量消峰
总结:有关系,但不想那么紧密
上面的例子其实也可以算作是应用解耦,毕竟按照API接口调用这种方式,B服务需要直接调用A服务获取结果,假设A服务挂了,这个时候A服务的业务自然也会受到影响。如果使用消息队列,即使A服务挂了,我们B服务也是可以正常运行的,等到A服务上线之后就可以抽取文本图谱了,然后把图谱抽取结果放在消息队列中,这样B服务就能拿到文本图谱抽取结果了
总结:非必要,不同步处理
其实上面举出的例子也是异步处理,毕竟不是同步嘛,碍于A服务抽取能力有限(几分钟才能抽取一段文本),所以我们采取的方式也是异步处理,不过不需要多做啥操作,毕竟消费者的监听是实时的,不需要我们用数据库或者多线程去特殊处理
根据我在公司看到的情况,我们给出一个应用内部逻辑借用消息队列来异步处理的例子,我们公司有一个即时通讯系统,你可以把它理解成是一个简易版QQ,当用户甲给用户乙发消息的时候,并不是直接发给用户乙的,而是用户甲先通过websocket把消息发送到后端服务,然后后端服务把消息发送到消息队列上,当用户乙登录的时候,后端服务连接上该队列,进而完成消息消费,然后通过websocket的形式把消息发送到用户乙的页面上。与此类似的还有我们的一个协同-思维导图系统,这个系统在做系统功能的时候也是通过rabbitmq来进行消息中转的。
无论是即时通讯,又或者是协同-思维导图,在消息传递过程中对消息是异步处理的,其实这也是对代码的一种解耦,毕竟发消息和收消息是两种不同的逻辑,如果简单通过逻辑代码方法的调用,代码是非常难于理解,并且非常臃肿,因此采用消息队列来进行解耦是一个不错的选择。
这是一个老牌的MQ了,优点是“消息稳定不丢失、支持主从架构”,缺点是“官方社区很少维护,毕竟出现问题的时候得自己去解决,并且高吞吐量场景很少使用”
大数据宠儿,以每秒百万级消息吞吐量闻名,缺点是“消息失败不支持重试,社区更新较慢,没有官方UI页面”
阿里产品,虽然阿里用的很多,那说明很强,缺点是”社区活跃度一般,另外如果出现问题,你可能要自己改“
这是我们学习文档的主角,针对名称来说,Rabbit是兔子的意思,那说明它还是很快的,优点是“支持每秒接受上万条消息,支持众多语言,官方UI界面用起来非常Nice,社区活跃度很高,拥有众多模式,可以支持多种场景使用”,缺点是“商业版需要收费,模式众多导致学习成本较高”
消息生产者,即生产消息的一方
消息消费者,即消费消息的一方
消息中转站,即决定消息去向的地方
消息临时存储箱,即临时存储队列的地方
交换机和队列之间是通过路由键来进行绑定的,这种绑定关系叫做绑定
这是一个虚拟分组,不同的分组中的交换机、队列都是逻辑隔离的,互不影响
生产者和RabbitMQ、消费者和RabbitMQ之间都存在连接
通道是给消费者用的,一个连接中可以有多个通道,每个通道都是逻辑隔离的,可以节省连接
总结:
对于一条消息的生命周期来说,生产者将它生产出来,然后交给交换机,交换机将消息分发到消息队列中,然后消费者将消息从队列中取出并消费,到此为止就完成了对象的整个生命周期
我们平时写代码的时候写的是交换机名称是空字符串,其实它代表的是默认交换机,所以无论什么消息都会经过这些生命周期
安装之前需要保证C盘用户目录下面的用户名是英文的,如果不是英文的,请先根据文章win10操作系统如何把用户名改成英文的改变用户名之后在按照下面步骤安装,切勿强行操作,这坑我是走过的!!!!!!
说明:
由于RabbitMQ
服务端代码是使用并发式语言Erlang
编写的,所以安装RabbitMQ
的前提是安装Erlang
环境。既然需要先安装Erlang
环境,那就需要先确定Erlang
和RabbitMQ
的版本对应关系,进而完成安装。
确定Erlang
和RabbitMQ
的版本对应关系:
直接访问https://www.rabbitmq.com/which-erlang.html#compatibility-matrix即可,页面如下:
(1)下载
打开Erlang下载地址,选择合适Erlang
版本,进行下载即可。这里给大家提供otp_win64_21.3.exe
安装包,如下:
链接:https://pan.baidu.com/s/1UxoAQaMz9DCMjUQO4B3p7g?pwd=oj7i
提取码:oj7i
2、安装
双击下载exe文件进行安装:
选择Erlang
安装位置(注意:不限定位置),然后点击Next
按钮,在点击Finish
按钮:
3、配置系统环境变量
首先新增系统环境变量,其中变量名
是ERLANG_HOME
,变量值
是Erlang安装地址
然后在变量名为path
的系统环境变量中配置新建%ERLANG_HOME%\bin
,点击确定
按钮即可,如下:
最后来验证Erlang环境是否配置成功,需要点击windows键
+R键
,输入cmd
打开DOS窗口,然后输入erl
回车,当进入Erlang
控制台说明安装成功了
1、下载
打开RabbitMQ下载地址,大家选好版本之后往下滑动页面下载即可,如下图:
这里给大家提供rabbitmq-server-3.7.14.exe
安装包,该安装包和上面Erlang
安装包能对应起来,如下:
链接:https://pan.baidu.com/s/1X-NNAhb34JAhS6yes7lTGw?pwd=jb6k
提取码:jb6k
2、安装RabbitMQ
一直点击Next
,中间需要选择自己的安装地址,其他没有需要注意的地方
3、安装RabbitMQ插件
在RabbitMQ
安装目录下面,找到sbin
目录,在地址栏输入cmd
回车:
然后输入rabbitmq-plugins enable rabbitmq_management
命令回车后就可以安装插件了
4、启动RabbitMQ
在RabbitMQ
安装目录sbin目录
下,双击rabbitmq-server.bat
文件就可以启动rabbitmq了
5、访问RabbitMQ
在浏览器地址栏输入http://127.0.0.1:15672
之后回车就可以访问了,登录页面上的初始用户名
和密码
都是guest
6、安装延迟交换机插件(可选)
(1)首先从https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases下载对应版本的插件
(2)解压到rabbitmq安装目录下面的plugins目录下面
(3)在rabbitmq安装目录下面的sbin目录下打开DOS窗口,然后执行rabbitmq-plugins enable rabbitmq_delayed_message_exchange
就可以让插件起作用
(4)我们打开rabbitmq控制台之后,选择交换机,然后就可以看到如下的交换机类型
拓展:上面给出了延迟交换机插件的获取方式,这里我也把其他插件的下载方式告诉大家,点击查看插件列表
说明:
由于RabbitMQ
服务端代码是使用并发式语言Erlang
编写的,所以安装RabbitMQ
的前提是安装Erlang
环境。既然需要先安装Erlang
环境,那就需要先确定Erlang
和RabbitMQ
的版本对应关系,进而完成安装。
确定Erlang
和RabbitMQ
的版本对应关系:
直接访问https://www.rabbitmq.com/which-erlang.html#compatibility-matrix即可,页面如下:
yum -y install socat
(1)下载
打开Erlang下载地址,选择合适Erlang
版本,进行下载即可。这里给大家提供erlang-21.3.8.8-1.el7.x86_64.rpm
安装包如下(备注: 文件名称中的el7
代表可以在Red Hat 7.x、CentOS 7.x平台上使用):
链接:https://pan.baidu.com/s/12w48I8jPQRq7JbmMIzbfIQ?pwd=o567
提取码:o567
(2)安装
# 例如:rpm -ivh erlang-21.3.8.8-1.el7.x86_64.rpm
rpm -ivh Erlang的rpm安装包名称
(1)下载
打开RabbitMQ下载地址,大家选好版本之后往下滑动页面下载即可,如下图:
这里给大家提供rabbitmq-server-3.7.14-1.el7.noarch.rpm
安装包如下(备注: 文件名称中的el7
代表可以在Red Hat 7.x、CentOS 7.x平台上使用):
链接:https://pan.baidu.com/s/1JCdP4XSAJMPnW_NouQxAgg?pwd=e414
提取码:e414
2、安装RabbitMQ
# 例如:rpm -ivh rabbitmq-server-3.7.14-1.el7.noarch.rpm
rpm -ivh RabbitMQ的rpm安装包名称
3、启动RabbitMQ控制台管理页面插件
rabbitmq-plugins enable rabbitmq_management
4、启动RabbitMQ
systemctl start rabbitmq-server
如果启动过程出现如下问题:
根据提示执行journalctl -xe
命令之后看到如下内容:
这个问题的根源是“rabbitmq无法通过主机名找到本机正确ip,进而导致RabbitMQ无法启动
”(备注:可以通过hostname命令查看自己主机名,比如我的就是10.0.2.15,估计和安装docker有管),解决办法是执行以下命令即可:
echo "NODENAME=rabbit@localhost" > /etc/rabbitmq/rabbitmq-env.conf
这样配置之后,RabbitMQ就会通过rabbit
协议和localhost
主机ip进而连通本机,之后重新执行启动RabbitMQ命令就可以了,如下:
systemctl start rabbitmq-server
以上解决问题的办法是RabbitMQ安装(官方推荐方式),开机启动(centos7)文章中提出的,特此感谢!!!
6、为RabbitMQ创建管理员用户
# 1、添加用户,其中用户名是admin,密码是admin123456
rabbitmqctl add_user admin admin123456
# 2、为用户授予角色,下面为上面创建的admin用户授予管理员角色
rabbitmqctl set_user_tags admin administrator
说明: 由于默认用户guest
只能在虚拟机本地使用,无法在RabbitMQ控制台登录,所以我们需要重新创建具有管理员角色的用户
7、访问RabbitMQ
直接在浏览器上访问http://ip:15672
,然后通过用户名admin和密码admin123456就可以访问RabbitMQ了,其中ip是虚拟机ip
第一步:下载延迟交换机插件
我们先访问rabbitmq-delayed-message-exchange下载适合RabbitMQ版本的延迟交换机插件,以RabbitMQ-3.7.14为例,我选择的延迟交换机插件是rabbitmq_delayed_message_exchange-3.8.0.ez
,如下:
这里我给大家提供rabbitmq_delayed_message_exchange-3.8.0.ez
安装包,如下:
链接:https://pan.baidu.com/s/1m7aS6tBXmOFarhWOP6_amQ?pwd=lbb7
提取码:lbb7
第二步:上传延迟交换机插件
在虚拟机上执行以下命令:
# *是RabbitMQ版本,由于大家版本不同,所以直接用*代替,所以任何一个版本都是ok的,比如我的RabbitMQ版本是3.7.14
cd /usr/lib/rabbitmq/lib/rabbitmq_server-*/plugins
第三步:安装延迟交换机插件
rabbitmq-plugins enable rabbitmq_delayed_message_exchange
第四步:查看延迟交换机插件安装效果
登录RabbitMQ控制台,在交换机页签下往下滑动,打开添加新交换机的type列表,然后能看到x-delayed-message
就说明延迟交换机插件安装成功
拓展:上面给出了延迟交换机插件的获取方式,这里我也把其他插件的下载方式告诉大家,点击查看插件列表
9、RabbitMQ命令汇总
# 1、在任意目录下启动RabbitMQ
systemctl start rabbitmq-server
# 2、在任意目录下重启RabbitMQ
systemctl restart rabbitmq-server
# 3、在任意目录下停止RabbitMQ
systemctl stop rabbitmq-server
# 4、在任意目录下查看RabbitMQ状态
systemctl status rabbitmq-server
# 5、在任意目录下查询可用指令
rabbitmqctl help
# 6、在任意目录下查询用户列表
rabbitmqctl list_users
# 7、在任意目录下查询相关插件
rabbitmqctl list_users
# 8、在任意目录下查询插件管理列表
rabbitmq-plugins list
根据搭建集群这一集进行部署
// 1、创建/var/lib/rabbitmq目录,用来保存RabbitMQ数据
mkdir -p /var/lib/rabbitmq
// 2、创建RabbitMQ容器
docker run -d --name rabbitmq -p 5672:5672 -p 15672:15672 -v /var/lib/rabbitmq:/var/lib/rabbitmq rabbitmq:management
// 3、访问RabbitMQ容器,在浏览器页面上访问http://ip:15672就可以看到页面了,其中初始用户名和密码都是guest(注意:提到的ip是服务器信息)
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: rabbitmq
namespace: rabbitmq
spec:
replicas: 1
selector:
matchLabels:
app: rabbitmq
serviceName: rabbitmq
template:
metadata:
labels:
app: rabbitmq
spec:
containers:
- env:
- name: RABBITMQ_DEFAULT_VHOST
value: /
- name: RABBITMQ_DEFAULT_USER
value: admin
- name: RABBITMQ_DEFAULT_PASS
value: admin123456
image: rabbitmq:3.8.3-management
imagePullPolicy: IfNotPresent
name: rabbitmq
ports:
- containerPort: 15672
name: rabbitmq-web
protocol: TCP
- containerPort: 5672
name: rabbitmq-app
protocol: TCP
volumeMounts:
- mountPath: /var/lib/rabbitmq
name: rabbitmq-data
subPath: rabbitmq-data
volumeClaimTemplates:
- apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: rabbitmq-data
spec:
accessModes:
- ReadWriteMany
resources:
requests:
storage: 500Mi
storageClassName: managed-nfs-storage
---
apiVersion: v1
kind: Service
metadata:
labels:
app: rabbitmq
name: rabbitmq
namespace: rabbitmq
spec:
ports:
- name: mq
port: 15672
protocol: TCP
targetPort: 15672
- name: mq-app
port: 5672
protocol: TCP
targetPort: 5672
selector:
app: rabbitmq
type: NodePort
kind: ConfigMap
apiVersion: v1
metadata:
name: rabbitmq-cluster-config
namespace: rabbitmq
labels:
addonmanager.kubernetes.io/mode: Reconcile
data:
enabled_plugins: |
[rabbitmq_management,rabbitmq_peer_discovery_k8s].
rabbitmq.conf: |
default_user = admin
default_pass = admin123456
## Cluster formation. See https://www.rabbitmq.com/cluster-formation.html to learn more.
cluster_formation.peer_discovery_backend = rabbit_peer_discovery_k8s
cluster_formation.k8s.host = kubernetes.default.svc.cluster.local
## Should RabbitMQ node name be computed from the pod's hostname or IP address?
## IP addresses are not stable, so using [stable] hostnames is recommended when possible.
## Set to "hostname" to use pod hostnames.
## When this value is changed, so should the variable used to set the RABBITMQ_NODENAME
## environment variable.
cluster_formation.k8s.address_type = hostname
## How often should node cleanup checks run?
cluster_formation.node_cleanup.interval = 30
## Set to false if automatic removal of unknown/absent nodes
## is desired. This can be dangerous, see
## * https://www.rabbitmq.com/cluster-formation.html#node-health-checks-and-cleanup
## * https://groups.google.com/forum/#!msg/rabbitmq-users/wuOfzEywHXo/k8z_HWIkBgAJ
cluster_formation.node_cleanup.only_log_warning = true
cluster_partition_handling = autoheal
## See https://www.rabbitmq.com/ha.html#master-migration-data-locality
queue_master_locator=min-masters
## See https://www.rabbitmq.com/access-control.html#loopback-users
loopback_users.guest = false
cluster_formation.randomized_startup_delay_range.min = 0
cluster_formation.randomized_startup_delay_range.max = 2
# default is rabbitmq-cluster's namespace
# hostname_suffix
cluster_formation.k8s.hostname_suffix = .rabbitmq-cluster.rabbitmq.svc.cluster.local
# memory
vm_memory_high_watermark.absolute = 100Mi
# disk
disk_free_limit.absolute = 100Mi
---
kind: Service
apiVersion: v1
metadata:
labels:
app: rabbitmq-cluster
name: rabbitmq-cluster
namespace: rabbitmq
spec:
clusterIP: None
ports:
- name: rmqport
port: 5672
targetPort: 5672
selector:
app: rabbitmq-cluster
---
kind: Service
apiVersion: v1
metadata:
labels:
app: rabbitmq-cluster
name: rabbitmq-cluster-manage
namespace: rabbitmq
spec:
ports:
- name: http
port: 15672
protocol: TCP
targetPort: 15672
selector:
app: rabbitmq-cluster
type: NodePort
---
apiVersion: v1
kind: ServiceAccount
metadata:
name: rabbitmq-cluster
namespace: rabbitmq
---
kind: Role
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: rabbitmq-cluster
namespace: rabbitmq
rules:
- apiGroups: [""]
resources: ["endpoints"]
verbs: ["get"]
---
kind: RoleBinding
apiVersion: rbac.authorization.k8s.io/v1beta1
metadata:
name: rabbitmq-cluster
namespace: rabbitmq
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: rabbitmq-cluster
subjects:
- kind: ServiceAccount
name: rabbitmq-cluster
namespace: rabbitmq
---
kind: StatefulSet
apiVersion: apps/v1
metadata:
labels:
app: rabbitmq-cluster
name: rabbitmq-cluster
namespace: rabbitmq
spec:
replicas: 3
selector:
matchLabels:
app: rabbitmq-cluster
serviceName: rabbitmq-cluster
template:
metadata:
labels:
app: rabbitmq-cluster
spec:
containers:
- args:
- -c
- cp -v /etc/rabbitmq/rabbitmq.conf ${RABBITMQ_CONFIG_FILE}; exec docker-entrypoint.sh
rabbitmq-server
command:
- sh
env:
- name: TZ
value: 'Asia/Shanghai'
- name: RABBITMQ_ERLANG_COOKIE
value: 'SWvCP0Hrqv43NG7GybHC95ntCJKoW8UyNFWnBEWG8TY='
- name: K8S_SERVICE_NAME
value: rabbitmq-cluster
- name: POD_IP
valueFrom:
fieldRef:
fieldPath: status.podIP
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: POD_NAMESPACE
valueFrom:
fieldRef:
fieldPath: metadata.namespace
- name: RABBITMQ_USE_LONGNAME
value: "true"
- name: RABBITMQ_NODENAME
value: rabbit@$(POD_NAME).$(K8S_SERVICE_NAME).$(POD_NAMESPACE).svc.cluster.local
- name: RABBITMQ_CONFIG_FILE
value: /var/lib/rabbitmq/rabbitmq.conf
image: rabbitmq:3.8.3-management
imagePullPolicy: IfNotPresent
name: rabbitmq
ports:
- containerPort: 15672
name: http
protocol: TCP
- containerPort: 5672
name: amqp
protocol: TCP
volumeMounts:
- mountPath: /etc/rabbitmq
name: config-volume
readOnly: false
- mountPath: /var/lib/rabbitmq
name: rabbitmq-storage
readOnly: false
- name: timezone
mountPath: /etc/localtime
readOnly: true
serviceAccountName: rabbitmq-cluster
terminationGracePeriodSeconds: 30
volumes:
- name: config-volume
configMap:
items:
- key: rabbitmq.conf
path: rabbitmq.conf
- key: enabled_plugins
path: enabled_plugins
name: rabbitmq-cluster-config
- name: timezone
hostPath:
path: /usr/share/zoneinfo/Asia/Shanghai
volumeClaimTemplates:
- metadata:
name: rabbitmq-storage
spec:
accessModes:
- ReadWriteMany
storageClassName: "managed-nfs-storage"
resources:
requests:
storage: 100Mi