RabbitMQ学习文档(环境安装篇)

目录

  • 一、概念
    • 1、含义
    • 2、使用场景
      • (1)流量消峰
      • (2)应用解耦
      • (3)异步处理
    • 3、MQ分类
      • (1)ActiveMq
      • (2)Kafak
      • (3)RocketMQ
      • (4)RabbitMQ
    • 4、核心概念
      • (1)生产者
      • (2)消费者
      • (3)交换机
      • (4)队列
      • (5)绑定
      • (6)虚拟主机
      • (7)连接
      • (8)通道
  • 二、环境搭建
    • 1、windows
      • (1)单机版
        • 1)前提要求
        • 2)选择RabbitMQ和Erlang的合适版本
        • 3)下载并安装Erlang
        • 4)下载并安装RabbitMQ
    • 2、linux
      • (1)单机版
        • 1)选择RabbitMQ和Erlang的合适版本
        • 2)安装socat
        • 3)下载并安装Erlang
        • 4)下载并安装RabbitMQ
      • (2)集群版
    • 3、docker
      • (1)单机版
    • 4、k8s
      • (1)单机版
      • (2)集群版
  • 三、代码

一、概念

1、含义

MQ的全英文名称是Message Queue,也就是消息队列,简单来说就是一个以的队列结构来存储消息的中间件。

2、使用场景

(1)流量消峰

总结:扛不住

假设A应用能提供文本图谱抽取能力,但是抽取能力很差,甚至需要几分钟才能把一段几千字的文本抽取完成。而B应用需要把很多段不同文本发送给A应用进行抽取,这时候使用API调用方法不太好,毕竟A应用响应时间也是很长的,不能让B应用等待这么久。另外A应用接收能力有限,所以不能把A应用给压爆了,这个时候就可以使用消息队列。

B应用把需要抽取的文字发到消息队列中,然后它就不需要管了,之后A应用去消息队列获取待抽取文本,然后将抽取完成的图谱结果放在消息队列中,之后B应用取出结果就可以了,即使B应用发送再多的文本也不用害怕了,毕竟消息在消息队列中存放,A应用只需要根据自己服务的能力去进行抽取就可以了,这样就可以完成流量消峰

(2)应用解耦

总结:有关系,但不想那么紧密

上面的例子其实也可以算作是应用解耦,毕竟按照API接口调用这种方式,B服务需要直接调用A服务获取结果,假设A服务挂了,这个时候A服务的业务自然也会受到影响。如果使用消息队列,即使A服务挂了,我们B服务也是可以正常运行的,等到A服务上线之后就可以抽取文本图谱了,然后把图谱抽取结果放在消息队列中,这样B服务就能拿到文本图谱抽取结果了

(3)异步处理

总结:非必要,不同步处理

其实上面举出的例子也是异步处理,毕竟不是同步嘛,碍于A服务抽取能力有限(几分钟才能抽取一段文本),所以我们采取的方式也是异步处理,不过不需要多做啥操作,毕竟消费者的监听是实时的,不需要我们用数据库或者多线程去特殊处理

根据我在公司看到的情况,我们给出一个应用内部逻辑借用消息队列来异步处理的例子,我们公司有一个即时通讯系统,你可以把它理解成是一个简易版QQ,当用户甲给用户乙发消息的时候,并不是直接发给用户乙的,而是用户甲先通过websocket把消息发送到后端服务,然后后端服务把消息发送到消息队列上,当用户乙登录的时候,后端服务连接上该队列,进而完成消息消费,然后通过websocket的形式把消息发送到用户乙的页面上。与此类似的还有我们的一个协同-思维导图系统,这个系统在做系统功能的时候也是通过rabbitmq来进行消息中转的。
无论是即时通讯,又或者是协同-思维导图,在消息传递过程中对消息是异步处理的,其实这也是对代码的一种解耦,毕竟发消息和收消息是两种不同的逻辑,如果简单通过逻辑代码方法的调用,代码是非常难于理解,并且非常臃肿,因此采用消息队列来进行解耦是一个不错的选择。

3、MQ分类

(1)ActiveMq

这是一个老牌的MQ了,优点是“消息稳定不丢失、支持主从架构”,缺点是“官方社区很少维护,毕竟出现问题的时候得自己去解决,并且高吞吐量场景很少使用”

(2)Kafak

大数据宠儿,以每秒百万级消息吞吐量闻名,缺点是“消息失败不支持重试,社区更新较慢,没有官方UI页面”

(3)RocketMQ

阿里产品,虽然阿里用的很多,那说明很强,缺点是”社区活跃度一般,另外如果出现问题,你可能要自己改“

(4)RabbitMQ

这是我们学习文档的主角,针对名称来说,Rabbit是兔子的意思,那说明它还是很快的,优点是“支持每秒接受上万条消息,支持众多语言,官方UI界面用起来非常Nice,社区活跃度很高,拥有众多模式,可以支持多种场景使用”,缺点是“商业版需要收费,模式众多导致学习成本较高”

4、核心概念

(1)生产者

消息生产者,即生产消息的一方

(2)消费者

消息消费者,即消费消息的一方

(3)交换机

消息中转站,即决定消息去向的地方

(4)队列

消息临时存储箱,即临时存储队列的地方

(5)绑定

交换机和队列之间是通过路由键来进行绑定的,这种绑定关系叫做绑定

(6)虚拟主机

这是一个虚拟分组,不同的分组中的交换机、队列都是逻辑隔离的,互不影响

(7)连接

生产者和RabbitMQ、消费者和RabbitMQ之间都存在连接

(8)通道

通道是给消费者用的,一个连接中可以有多个通道,每个通道都是逻辑隔离的,可以节省连接

总结:

对于一条消息的生命周期来说,生产者将它生产出来,然后交给交换机,交换机将消息分发到消息队列中,然后消费者将消息从队列中取出并消费,到此为止就完成了对象的整个生命周期

我们平时写代码的时候写的是交换机名称是空字符串,其实它代表的是默认交换机,所以无论什么消息都会经过这些生命周期

RabbitMQ学习文档(环境安装篇)_第1张图片

二、环境搭建

1、windows

(1)单机版

1)前提要求

安装之前需要保证C盘用户目录下面的用户名是英文的,如果不是英文的,请先根据文章win10操作系统如何把用户名改成英文的改变用户名之后在按照下面步骤安装,切勿强行操作,这坑我是走过的!!!!!!

2)选择RabbitMQ和Erlang的合适版本

说明:

由于RabbitMQ服务端代码是使用并发式语言Erlang编写的,所以安装RabbitMQ的前提是安装Erlang环境。既然需要先安装Erlang环境,那就需要先确定ErlangRabbitMQ的版本对应关系,进而完成安装。

确定ErlangRabbitMQ的版本对应关系:

直接访问https://www.rabbitmq.com/which-erlang.html#compatibility-matrix即可,页面如下:

RabbitMQ学习文档(环境安装篇)_第2张图片

3)下载并安装Erlang

(1)下载

打开Erlang下载地址,选择合适Erlang版本,进行下载即可。这里给大家提供otp_win64_21.3.exe安装包,如下:

链接:https://pan.baidu.com/s/1UxoAQaMz9DCMjUQO4B3p7g?pwd=oj7i
提取码:oj7i

RabbitMQ学习文档(环境安装篇)_第3张图片

2、安装

双击下载exe文件进行安装:

RabbitMQ学习文档(环境安装篇)_第4张图片

选择Erlang安装位置(注意:不限定位置),然后点击Next按钮,在点击Finish按钮:

RabbitMQ学习文档(环境安装篇)_第5张图片

3、配置系统环境变量

首先新增系统环境变量,其中变量名ERLANG_HOME变量值是Erlang安装地址

RabbitMQ学习文档(环境安装篇)_第6张图片

然后在变量名为path的系统环境变量中配置新建%ERLANG_HOME%\bin,点击确定按钮即可,如下:

RabbitMQ学习文档(环境安装篇)_第7张图片
最后来验证Erlang环境是否配置成功,需要点击windows键+R键,输入cmd打开DOS窗口,然后输入erl回车,当进入Erlang控制台说明安装成功了

在这里插入图片描述

4)下载并安装RabbitMQ

1、下载

打开RabbitMQ下载地址,大家选好版本之后往下滑动页面下载即可,如下图:

RabbitMQ学习文档(环境安装篇)_第8张图片

这里给大家提供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学习文档(环境安装篇)_第9张图片RabbitMQ学习文档(环境安装篇)_第10张图片

然后输入rabbitmq-plugins enable rabbitmq_management命令回车后就可以安装插件了

4、启动RabbitMQ

RabbitMQ安装目录sbin目录下,双击rabbitmq-server.bat文件就可以启动rabbitmq了

RabbitMQ学习文档(环境安装篇)_第11张图片

5、访问RabbitMQ

在浏览器地址栏输入http://127.0.0.1:15672之后回车就可以访问了,登录页面上的初始用户名密码都是guest

RabbitMQ学习文档(环境安装篇)_第12张图片

6、安装延迟交换机插件(可选)

(1)首先从https://github.com/rabbitmq/rabbitmq-delayed-message-exchange/releases下载对应版本的插件

RabbitMQ学习文档(环境安装篇)_第13张图片

(2)解压到rabbitmq安装目录下面的plugins目录下面

RabbitMQ学习文档(环境安装篇)_第14张图片

(3)在rabbitmq安装目录下面的sbin目录下打开DOS窗口,然后执行rabbitmq-plugins enable rabbitmq_delayed_message_exchange就可以让插件起作用

(4)我们打开rabbitmq控制台之后,选择交换机,然后就可以看到如下的交换机类型

RabbitMQ学习文档(环境安装篇)_第15张图片
拓展:上面给出了延迟交换机插件的获取方式,这里我也把其他插件的下载方式告诉大家,点击查看插件列表

2、linux

(1)单机版

1)选择RabbitMQ和Erlang的合适版本

说明:

由于RabbitMQ服务端代码是使用并发式语言Erlang编写的,所以安装RabbitMQ的前提是安装Erlang环境。既然需要先安装Erlang环境,那就需要先确定ErlangRabbitMQ的版本对应关系,进而完成安装。

确定ErlangRabbitMQ的版本对应关系:

直接访问https://www.rabbitmq.com/which-erlang.html#compatibility-matrix即可,页面如下:

RabbitMQ学习文档(环境安装篇)_第16张图片

2)安装socat
yum -y install socat
3)下载并安装Erlang

(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

RabbitMQ学习文档(环境安装篇)_第17张图片

(2)安装

# 例如:rpm -ivh erlang-21.3.8.8-1.el7.x86_64.rpm
rpm -ivh Erlang的rpm安装包名称
4)下载并安装RabbitMQ

(1)下载

打开RabbitMQ下载地址,大家选好版本之后往下滑动页面下载即可,如下图:

RabbitMQ学习文档(环境安装篇)_第18张图片

这里给大家提供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学习文档(环境安装篇)_第19张图片

这个问题的根源是“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学习文档(环境安装篇)_第20张图片
8、安装延迟交换机插件(可选)

第一步:下载延迟交换机插件

我们先访问rabbitmq-delayed-message-exchange下载适合RabbitMQ版本的延迟交换机插件,以RabbitMQ-3.7.14为例,我选择的延迟交换机插件是rabbitmq_delayed_message_exchange-3.8.0.ez,如下:

RabbitMQ学习文档(环境安装篇)_第21张图片

这里我给大家提供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就说明延迟交换机插件安装成功

RabbitMQ学习文档(环境安装篇)_第22张图片

拓展:上面给出了延迟交换机插件的获取方式,这里我也把其他插件的下载方式告诉大家,点击查看插件列表

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

(2)集群版

根据搭建集群这一集进行部署

3、docker

(1)单机版

// 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是服务器信息)

4、k8s

(1)单机版

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

(2)集群版

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

三、代码

  1. RabbitMQ学习文档(入门篇(Demo使用Spring编写))
  2. RabbitMQ学习文档(进阶篇(Demo使用Spring编写))
  3. RabbitMQ学习文档(入门篇(Demo使用SpringBoot编写))
  4. RabbitMQ学习文档(进阶篇(Demo使用SpringBoot编写))

你可能感兴趣的:(rabbitmq,学习,数据库)