阶段三:消息中间件RabbitMQ
一、课程目标
- 掌握MQ 解决了什么问题。
- 掌握RabbitMQ 中 Producer、Exchange、Queue、Consumer 的关系。
- 掌握direct、topic、fanout 三类常见交换机。
- 掌握项目中的
@RabbitListener队列绑定。
- 掌握云岚到家项目为什么使用 RabbitMQ 承接 Canal 数据同步和支付状态通知。
- 能根据日志和管理后台排查消息没有消费、队列堆积、连接失败等问题
二、云岚到家微服务项目为什么需要MQ
在单体系统中,业务方法可以直接调用本地代码。
到了微服务架构后,订单、支付、用户、服务、搜索、历史订单等能力被拆成多个服务。服务之间如果全部同步调用,会带来几个问题:
- 调用链变长,一个服务慢会拖慢整个流程。
- 服务耦合变强,下游异常会影响上游主流程。
- 瞬时流量高峰直接压到数据库或业务服务。
- 一份数据变化需要通知多个系统,同步调用扩展性差。
MQ 的核心价值:
- 异步处理:主流程先完成,后续动作交给消费者处理。
- 应用解耦:发送方只依赖 MQ,不直接依赖所有下游服务。
- 削峰填谷:高峰请求先进入队列,消费者按能力处理。
- 数据分发:一条消息可以按规则分发给多个业务系统。
- 最终一致:通过重试、补偿、幂等让跨服务状态逐步一致。
三、RabbitMQ 的核心角色
RabbitMQ 遵循 AMQP 思想,核心角色包括:
- Producer:消息生产者,负责发送消息。
- Exchange:交换机,负责按规则路由消息。
- Queue:队列,负责保存消息,等待消费者消费。
- Binding:绑定关系,连接 Exchange 和 Queue。
- Routing Key:路由键,生产者发送消息时携带。
- Consumer:消费者,从队列中取消息处理业务。
四、常见交换机类型
1、Direct Exchange
- 精确匹配 routing key。
- 适合一类消息只进入指定队列。
- 例:
order.pay.success只投递给订单支付成功队列。
2、Topic Exchange
- 按通配符匹配 routing key。
*匹配一个单词,#匹配多个单词。
- 适合按业务模块、事件类型做灵活分发。
3、Fanout Exchange
- 广播模式,不看 routing key。
- 绑定到交换机的队列都会收到消息。
- 适合系统公告、缓存刷新等广播场景。
云岚到家项目里大量使用 topic 类型,便于按业务模块分发 Canal 数据和交易状态
五、RabbitMQ部署模式
1、单机部署
YAML 保存位置: ~/k8s-manifests/jzo2o/rabbitmq-config.yaml
先在 Kubernetes 管理节点执行:
mkdir -p ~/k8s-manifests/jzo2o
cd ~/k8s-manifests/jzo2o
vi rabbitmq-config.yaml把下方 YAML 全部复制进去;红色字段按当前环境修改;按 Esc 后输入 :wq 保存退出。
apiVersion: v1
kind: ConfigMap
metadata:
name: rabbitmq-config
namespace: jzo2o
data:
rabbitmq.conf: |
management.load_definitions = /etc/rabbitmq/definitions.json
loopback_users.guest = false
definitions.json: |
{
"users": [
{
"name": "xzb",
"password": "xzb",
"tags": ""
},
{
"name": "czri",
"password": "czri1234",
"tags": "administrator"
}
],
"vhosts": [
{
"name": "/"
},
{
"name": "/xzb"
}
],
"permissions": [
{
"user": "xzb",
"vhost": "/xzb",
"configure": ".*",
"write": ".*",
"read": ".*"
},
{
"user": "czri",
"vhost": "/",
"configure": ".*",
"write": ".*",
"read": ".*"
},
{
"user": "czri",
"vhost": "/xzb",
"configure": ".*",
"write": ".*",
"read": ".*"
}
],
"parameters": [],
"policies": [],
"queues": [],
"exchanges": [
{
"name": "exchange.canal-jzo2o",
"vhost": "/xzb",
"type": "topic",
"durable": true,
"auto_delete": false,
"internal": false,
"arguments": {}
}
],
"bindings": []
}配置详解:
| 需要改的项 | 为什么改 |
|---|---|
namespace: jzo2o | 与实际命名空间一致;使用默认命名空间时可不写。 |
配置详解:
| 需要改的项 | 为什么改 |
|---|---|
namespace: jzo2o | 与实际命名空间一致;使用默认命名空间时可不写。 |
注意事项:
- 修改配置前先备份原文件,尤其是 SSH、Nginx、数据库和 Kubernetes 生产配置。
- YAML 缩进不能乱;同级字段要对齐,子字段要多缩进两个空格。
- 资源名称、命名空间、标签选择器要互相对应,否则资源创建了也可能找不到彼此。
YAML 保存位置: ~/k8s-manifests/jzo2o/rabbitmq.yaml
先在 Kubernetes 管理节点执行:
mkdir -p ~/k8s-manifests/jzo2o
cd ~/k8s-manifests/jzo2o
vi rabbitmq.yaml把下方 YAML 全部复制进去;红色字段按当前环境修改;按 Esc 后输入 :wq 保存退出。
apiVersion: v1
kind: Service
metadata:
name: rabbitmq
namespace: jzo2o
spec:
type: NodePort
selector: {app: rabbitmq}
ports:
- name: amqp
port: 5672
targetPort: 5672
nodePort: 30672
- name: management
port: 15672
targetPort: 15672
nodePort: 31672
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: rabbitmq
namespace: jzo2o
spec:
serviceName: rabbitmq
replicas: 1
selector:
matchLabels: {app: rabbitmq}
template:
metadata:
labels: {app: rabbitmq}
spec:
containers:
- name: rabbitmq
image: harbor.lanqicheng.top/jzo2o/rabbitmq:3.9.17-management-delayed
ports:
- containerPort: 5672
name: amqp
- containerPort: 15672
name: management
env:
- name: RABBITMQ_DEFAULT_USER
value: czri
- name: RABBITMQ_DEFAULT_PASS
value: czri1234
volumeMounts:
- name: rabbitmq-data
mountPath: /var/lib/rabbitmq
- name: rabbitmq-config
mountPath: /etc/rabbitmq/rabbitmq.conf
subPath: rabbitmq.conf
- name: rabbitmq-config
mountPath: /etc/rabbitmq/definitions.json
subPath: definitions.json
volumes:
- name: rabbitmq-config
configMap:
name: rabbitmq-config
volumeClaimTemplates:
- metadata:
name: rabbitmq-data
spec:
accessModes: ["ReadWriteMany"]
storageClassName: nfs-storage
resources:
requests:
storage: 10Gi配置详解:
| 需要改的项 | 为什么改 |
|---|---|
namespace: jzo2o | 与实际命名空间一致;使用默认命名空间时可不写。 |
port: 5672 | 对外暴露的 Service 端口;调用方按此端口访问。 |
targetPort: 5672 | 必须与容器实际监听端口一致。 |
nodePort: 30672 | 集群外通过 NodeIP:NodePort 访问时才需要改。 |
port: 15672 | 对外暴露的 Service 端口;调用方按此端口访问。 |
targetPort: 15672 | 必须与容器实际监听端口一致。 |
注意事项:
- 修改配置前先备份原文件,尤其是 SSH、Nginx、数据库和 Kubernetes 生产配置。
- YAML 缩进不能乱;同级字段要对齐,子字段要多缩进两个空格。
- 资源名称、命名空间、标签选择器要互相对应,否则资源创建了也可能找不到彼此。
- 镜像地址、版本号和拉取权限要确认,否则 Pod 会进入
ImagePullBackOff。
2、集群模式
- 创建配置文件
YAML 保存位置: ~/k8s-manifests/jzo2o/rabbitmq-erlang-cookie.yaml
先在 Kubernetes 管理节点执行:
mkdir -p ~/k8s-manifests/jzo2o
cd ~/k8s-manifests/jzo2o
vi rabbitmq-erlang-cookie.yaml把下方 YAML 全部复制进去;红色字段按当前环境修改;按 Esc 后输入 :wq 保存退出。
apiVersion: v1
kind: Secret
metadata:
name: rabbitmq-erlang-cookie
namespace: jzo2o
type: Opaque
stringData:
cookie: "jzo2o-rabbitmq-cookie"
---
apiVersion: v1
kind: ConfigMap
metadata:
name: rabbitmq-config
namespace: jzo2o
data:
enabled_plugins: |
[rabbitmq_management,rabbitmq_peer_discovery_k8s].
rabbitmq.conf: |
management.load_definitions = /etc/rabbitmq/definitions.json
loopback_users.guest = false
cluster_formation.peer_discovery_backend = rabbit_peer_discovery_k8s
cluster_formation.k8s.host = kubernetes.default.svc.cluster.local
cluster_formation.k8s.address_type = hostname
cluster_formation.k8s.service_name = rabbitmq-headless
cluster_formation.k8s.hostname_suffix = .rabbitmq-headless.jzo2o.svc.cluster.local
cluster_partition_handling = pause_minority
queue_master_locator = min-masters
definitions.json: |
{
"users": [
{
"name": "xzb",
"password": "xzb",
"tags": ""
},
{
"name": "czri",
"password": "czri1234",
"tags": "administrator"
}
],
"vhosts": [
{
"name": "/"
},
{
"name": "/xzb"
}
],
"permissions": [
{
"user": "xzb",
"vhost": "/xzb",
"configure": ".*",
"write": ".*",
"read": ".*"
},
{
"user": "czri",
"vhost": "/",
"configure": ".*",
"write": ".*",
"read": ".*"
},
{
"user": "czri",
"vhost": "/xzb",
"configure": ".*",
"write": ".*",
"read": ".*"
}
],
"parameters": [],
"policies": [],
"queues": [],
"exchanges": [],
"bindings": []
}配置详解:
| 需要改的项 | 为什么改 |
|---|---|
namespace: jzo2o | 与实际命名空间一致;使用默认命名空间时可不写。 |
注意事项:
- 修改配置前先备份原文件,尤其是 SSH、Nginx、数据库和 Kubernetes 生产配置。
- YAML 缩进不能乱;同级字段要对齐,子字段要多缩进两个空格。
- 资源名称、命名空间、标签选择器要互相对应,否则资源创建了也可能找不到彼此。
YAML 保存位置: ~/k8s-manifests/jzo2o/rabbitmq-rbac.yaml
先在 Kubernetes 管理节点执行:
mkdir -p ~/k8s-manifests/jzo2o
cd ~/k8s-manifests/jzo2o
vi rabbitmq-rbac.yaml把下方 YAML 全部复制进去;红色字段按当前环境修改;按 Esc 后输入 :wq 保存退出。
apiVersion: v1
kind: ServiceAccount
metadata:
name: rabbitmq
namespace: jzo2o
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: rabbitmq-peer-discovery
namespace: jzo2o
rules:
- apiGroups: [""]
resources: ["endpoints", "pods", "services"]
verbs: ["get", "list", "watch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: rabbitmq-peer-discovery
namespace: jzo2o
subjects:
- kind: ServiceAccount
name: rabbitmq
namespace: jzo2o
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: Role
name: rabbitmq-peer-discovery配置详解:
| 需要改的项 | 为什么改 |
|---|---|
namespace: jzo2o | 与实际命名空间一致;使用默认命名空间时可不写。 |
注意事项:
- 修改配置前先备份原文件,尤其是 SSH、Nginx、数据库和 Kubernetes 生产配置。
- YAML 缩进不能乱;同级字段要对齐,子字段要多缩进两个空格。
- 资源名称、命名空间、标签选择器要互相对应,否则资源创建了也可能找不到彼此。
YAML 保存位置: ~/k8s-manifests/jzo2o/rabbitmq-headless.yaml
先在 Kubernetes 管理节点执行:
mkdir -p ~/k8s-manifests/jzo2o
cd ~/k8s-manifests/jzo2o
vi rabbitmq-headless.yaml把下方 YAML 全部复制进去;红色字段按当前环境修改;按 Esc 后输入 :wq 保存退出。
apiVersion: v1
kind: Service
metadata:
name: rabbitmq-headless
namespace: jzo2o
spec:
clusterIP: None
selector:
app: rabbitmq
ports:
- name: amqp
port: 5672
targetPort: 5672
- name: management
port: 15672
targetPort: 15672
- name: epmd
port: 4369
targetPort: 4369
- name: clustering
port: 25672
targetPort: 25672
---
apiVersion: v1
kind: Service
metadata:
name: rabbitmq
namespace: jzo2o
spec:
type: NodePort
selector:
app: rabbitmq
ports:
- name: amqp
port: 5672
targetPort: 5672
nodePort: 30672
- name: management
port: 15672
targetPort: 15672
nodePort: 31672
---
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: rabbitmq
namespace: jzo2o
spec:
serviceName: rabbitmq-headless
replicas: 3
selector:
matchLabels:
app: rabbitmq
template:
metadata:
labels:
app: rabbitmq
spec:
serviceAccountName: rabbitmq
containers:
- name: rabbitmq
image: harbor.lanqicheng.top/jzo2o/rabbitmq:3.9.17-management-delayed
imagePullPolicy: IfNotPresent
ports:
- name: amqp
containerPort: 5672
- name: management
containerPort: 15672
- name: epmd
containerPort: 4369
- name: clustering
containerPort: 25672
env:
- name: POD_NAME
valueFrom:
fieldRef:
fieldPath: metadata.name
- name: RABBITMQ_USE_LONGNAME
value: "true"
- name: RABBITMQ_NODENAME
value: rabbit@$(POD_NAME).rabbitmq-headless.jzo2o.svc.cluster.local
- name: RABBITMQ_ERLANG_COOKIE
valueFrom:
secretKeyRef:
name: rabbitmq-erlang-cookie
key: cookie
volumeMounts:
- name: rabbitmq-data
mountPath: /var/lib/rabbitmq
- name: rabbitmq-config
mountPath: /etc/rabbitmq/rabbitmq.conf
subPath: rabbitmq.conf
- name: rabbitmq-config
mountPath: /etc/rabbitmq/definitions.json
subPath: definitions.json
- name: rabbitmq-config
mountPath: /etc/rabbitmq/enabled_plugins
subPath: enabled_plugins
volumes:
- name: rabbitmq-config
configMap:
name: rabbitmq-config
volumeClaimTemplates:
- metadata:
name: rabbitmq-data
spec:
accessModes: ["ReadWriteOnce"]
storageClassName: nfs-storage
resources:
requests:
storage: 10Gi配置详解:
| 需要改的项 | 为什么改 |
|---|---|
namespace: jzo2o | 与实际命名空间一致;使用默认命名空间时可不写。 |
port: 5672 | 对外暴露的 Service 端口;调用方按此端口访问。 |
targetPort: 5672 | 必须与容器实际监听端口一致。 |
port: 15672 | 对外暴露的 Service 端口;调用方按此端口访问。 |
targetPort: 15672 | 必须与容器实际监听端口一致。 |
port: 4369 | 对外暴露的 Service 端口;调用方按此端口访问。 |
注意事项:
- 修改配置前先备份原文件,尤其是 SSH、Nginx、数据库和 Kubernetes 生产配置。
- YAML 缩进不能乱;同级字段要对齐,子字段要多缩进两个空格。
- 资源名称、命名空间、标签选择器要互相对应,否则资源创建了也可能找不到彼此。
- 镜像地址、版本号和拉取权限要确认,否则 Pod 会进入
ImagePullBackOff。
同步说明
本文由飞书云文档同步生成。涉及命令、SQL、配置示例时,请以飞书源文档和实际环境执行结果为准。