概述
RabbitMQ 用 Erlang 写的,手动安装要先把 Erlang 运行时和它的版本对应关系搞对------这是新手踩的第一个坑。
本文用 Docker 一条命令把单机版跑起来,理清 5672 / 15672 两个端口分别干什么,并给出一套可复制的"安装是否真的成功"验证流程。
纲要
- 关键概念
- RabbitMQ:基于 Erlang 开发、实现 AMQP 协议的开源消息中间件
- 五种角色:
publisher、consumer、exchange、queue、virtualHost - 两个端口:5672 (AMQP 消息通信)、15672(Management 管理控制台 UI)
- 三种装法:Docker 单机部署 (推荐)、Linux 原生安装 、集群部署
- 核心流程
- 拉取镜像 →
docker run启动容器 → 访问控制台 → 用默认账号登录 → 看节点/交换机/队列 - 建立 Connection → 创建 Channel → 声明队列 → 收发消息
- 拉取镜像 →
- 涉及命令与配置
docker pull rabbitmq:3.8-management/docker load -i mq.tardocker run完整参数:-e、-v、--name、--hostname、-p、-d- 验证:
docker ps、docker logs、ss -tlnp、curl调 HTTP API、rabbitmqctl status - 排障:端口占用、容器退出、控制台打不开、guest 账号无法远程登录
RabbitMQ 是什么
RabbitMQ 是一个开源的消息中间件(Message Queue,事件驱动架构里的 Broker),由 Erlang 语言 开发,实现了 AMQP(Advanced Message Queuing Protocol)协议,同时也支持 STOMP、MQTT 等协议通过插件扩展。
Erlang 是爱立信为电信交换机设计的语言,天生面向并发、面向分布式:轻量进程、Actor 模型、软实时、热更新。RabbitMQ 建在它上面,也就天然带了这些特征------单机吞吐不是它的强项,但消息可靠性和稳定性是。官方文档里给的单机吞吐参考是万级/秒量级,做业务异步解耦、削峰填谷绰绰有余;真要做到百万级吞吐,那是 Kafka / RocketMQ 的战场。
选型时常见的四种 MQ 对比:
| RabbitMQ | ActiveMQ | RocketMQ | Kafka | |
|---|---|---|---|---|
| 公司/社区 | Rabbit(原 Pivotal) | Apache | 阿里 | Apache |
| 开发语言 | Erlang | Java | Java | Scala & Java |
| 协议支持 | AMQP、XMPP、SMTP、STOMP | OpenWire、STOMP、REST、XMPP、AMQP | 自定义协议 | 自定义协议 |
| 可用性 | 高 | 一般 | 高 | 高 |
| 单机吞吐量 | 一般 | 差 | 高 | 非常高 |
| 消息延迟 | 微秒级 | 毫秒级 | 毫秒级 | 毫秒以内 |
| 消息可靠性 | 高 | 一般 | 高 | 一般 |
一句话记法:追求可靠性选 RabbitMQ / RocketMQ,追求吞吐选 Kafka / RocketMQ,追求低延迟选 RabbitMQ / Kafka。
整体结构与核心概念
这是理解后面所有操作的基础,先记住这张图:
#mermaid-svg-He4WGwRjlAzebbWm{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-He4WGwRjlAzebbWm .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-He4WGwRjlAzebbWm .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-He4WGwRjlAzebbWm .error-icon{fill:#552222;}#mermaid-svg-He4WGwRjlAzebbWm .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-He4WGwRjlAzebbWm .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-He4WGwRjlAzebbWm .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-He4WGwRjlAzebbWm .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-He4WGwRjlAzebbWm .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-He4WGwRjlAzebbWm .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-He4WGwRjlAzebbWm .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-He4WGwRjlAzebbWm .marker{fill:#333333;stroke:#333333;}#mermaid-svg-He4WGwRjlAzebbWm .marker.cross{stroke:#333333;}#mermaid-svg-He4WGwRjlAzebbWm svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-He4WGwRjlAzebbWm p{margin:0;}#mermaid-svg-He4WGwRjlAzebbWm .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-He4WGwRjlAzebbWm .cluster-label text{fill:#333;}#mermaid-svg-He4WGwRjlAzebbWm .cluster-label span{color:#333;}#mermaid-svg-He4WGwRjlAzebbWm .cluster-label span p{background-color:transparent;}#mermaid-svg-He4WGwRjlAzebbWm .label text,#mermaid-svg-He4WGwRjlAzebbWm span{fill:#333;color:#333;}#mermaid-svg-He4WGwRjlAzebbWm .node rect,#mermaid-svg-He4WGwRjlAzebbWm .node circle,#mermaid-svg-He4WGwRjlAzebbWm .node ellipse,#mermaid-svg-He4WGwRjlAzebbWm .node polygon,#mermaid-svg-He4WGwRjlAzebbWm .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-He4WGwRjlAzebbWm .rough-node .label text,#mermaid-svg-He4WGwRjlAzebbWm .node .label text,#mermaid-svg-He4WGwRjlAzebbWm .image-shape .label,#mermaid-svg-He4WGwRjlAzebbWm .icon-shape .label{text-anchor:middle;}#mermaid-svg-He4WGwRjlAzebbWm .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-He4WGwRjlAzebbWm .rough-node .label,#mermaid-svg-He4WGwRjlAzebbWm .node .label,#mermaid-svg-He4WGwRjlAzebbWm .image-shape .label,#mermaid-svg-He4WGwRjlAzebbWm .icon-shape .label{text-align:center;}#mermaid-svg-He4WGwRjlAzebbWm .node.clickable{cursor:pointer;}#mermaid-svg-He4WGwRjlAzebbWm .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-He4WGwRjlAzebbWm .arrowheadPath{fill:#333333;}#mermaid-svg-He4WGwRjlAzebbWm .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-He4WGwRjlAzebbWm .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-He4WGwRjlAzebbWm .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-He4WGwRjlAzebbWm .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-He4WGwRjlAzebbWm .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-He4WGwRjlAzebbWm .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-He4WGwRjlAzebbWm .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-He4WGwRjlAzebbWm .cluster text{fill:#333;}#mermaid-svg-He4WGwRjlAzebbWm .cluster span{color:#333;}#mermaid-svg-He4WGwRjlAzebbWm div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-He4WGwRjlAzebbWm .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-He4WGwRjlAzebbWm rect.text{fill:none;stroke-width:0;}#mermaid-svg-He4WGwRjlAzebbWm .icon-shape,#mermaid-svg-He4WGwRjlAzebbWm .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-He4WGwRjlAzebbWm .icon-shape p,#mermaid-svg-He4WGwRjlAzebbWm .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-He4WGwRjlAzebbWm .icon-shape .label rect,#mermaid-svg-He4WGwRjlAzebbWm .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-He4WGwRjlAzebbWm .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-He4WGwRjlAzebbWm .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-He4WGwRjlAzebbWm :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 隔离
virtualHost: /itcast
发布消息
路由 RoutingKey
推送消息
publisher 生产者
exchange 交换机
queue 队列
consumer 消费者
virtualHost: /
发布消息
路由 RoutingKey
推送消息
publisher 生产者
exchange 交换机
queue 队列
consumer 消费者
两个虚拟主机里的交换机可以同名、队列可以同名,互不干扰。这就是多租户隔离。
角色职责:
| 角色 | 作用 | 典型误用 |
|---|---|---|
publisher |
消息生产者,把消息发给 exchange | 以为消息是直接发给 queue 的 |
consumer |
消息消费者,从 queue 取消息处理 | 一个队列挂多个消费者时以为是"每个都收一份",实际是竞争消费 |
exchange |
交换机,消息的路由器,按类型和 RoutingKey 投递到队列 | 忘记绑队列,消息发出去直接被丢弃(RabbitMQ 不报错) |
queue |
队列,缓存消息,是真正存消息的地方 | 生产消费者两边声明的队列参数不一致,启动报 PRECONDITION_FAILED |
virtualHost |
虚拟主机,对 exchange/queue/消息做逻辑分组与隔离 | 所有业务共用一个 /,多项目互相踩 |
channel |
操作 MQ 的句柄,收发消息都要通过它 | 把 Connection 当操作对象用;或者一个线程共用一个非线程安全的 Channel |
connection |
TCP 长连接 | 每条消息新建一次连接,把 MQ 连接打满 |
一次完整的通信过程:
consumer queue Broker(RabbitMQ) publisher consumer queue Broker(RabbitMQ) publisher #mermaid-svg-CZoLYIyQetC8CdkS{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-CZoLYIyQetC8CdkS .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-CZoLYIyQetC8CdkS .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-CZoLYIyQetC8CdkS .error-icon{fill:#552222;}#mermaid-svg-CZoLYIyQetC8CdkS .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-CZoLYIyQetC8CdkS .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-CZoLYIyQetC8CdkS .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-CZoLYIyQetC8CdkS .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-CZoLYIyQetC8CdkS .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-CZoLYIyQetC8CdkS .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-CZoLYIyQetC8CdkS .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-CZoLYIyQetC8CdkS .marker{fill:#333333;stroke:#333333;}#mermaid-svg-CZoLYIyQetC8CdkS .marker.cross{stroke:#333333;}#mermaid-svg-CZoLYIyQetC8CdkS svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-CZoLYIyQetC8CdkS p{margin:0;}#mermaid-svg-CZoLYIyQetC8CdkS .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-CZoLYIyQetC8CdkS text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-CZoLYIyQetC8CdkS .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-CZoLYIyQetC8CdkS .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-CZoLYIyQetC8CdkS .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-CZoLYIyQetC8CdkS .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-CZoLYIyQetC8CdkS #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-CZoLYIyQetC8CdkS .sequenceNumber{fill:white;}#mermaid-svg-CZoLYIyQetC8CdkS #sequencenumber{fill:#333;}#mermaid-svg-CZoLYIyQetC8CdkS #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-CZoLYIyQetC8CdkS .messageText{fill:#333;stroke:none;}#mermaid-svg-CZoLYIyQetC8CdkS .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-CZoLYIyQetC8CdkS .labelText,#mermaid-svg-CZoLYIyQetC8CdkS .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-CZoLYIyQetC8CdkS .loopText,#mermaid-svg-CZoLYIyQetC8CdkS .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-CZoLYIyQetC8CdkS .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-CZoLYIyQetC8CdkS .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-CZoLYIyQetC8CdkS .noteText,#mermaid-svg-CZoLYIyQetC8CdkS .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-CZoLYIyQetC8CdkS .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-CZoLYIyQetC8CdkS .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-CZoLYIyQetC8CdkS .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-CZoLYIyQetC8CdkS .actorPopupMenu{position:absolute;}#mermaid-svg-CZoLYIyQetC8CdkS .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-CZoLYIyQetC8CdkS .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-CZoLYIyQetC8CdkS .actor-man circle,#mermaid-svg-CZoLYIyQetC8CdkS line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-CZoLYIyQetC8CdkS :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 建立 TCP Connection(5672) 在 Connection 上创建 Channel 声明 exchange / queue 并绑定 basicPublish 发送消息 exchange 按 RoutingKey 路由入队 建立 Connection + Channel basicConsume 订阅队列 推送消息 ack 确认 删除已确认的消息
注意 channel 这一层:Connection 是昂贵的 TCP 长连接,Channel 是复用在它上面的轻量逻辑连接。生产环境里一个应用通常只维持少量 Connection,每个线程各自开 Channel------Channel 不是线程安全的,别多线程共享同一个。
为什么用 Docker 装
原生安装 RabbitMQ 的麻烦在于 Erlang 版本强绑定:RabbitMQ 3.8 要求 Erlang 21.3 -- 23.x,3.9 要求 23.2 -- 24.x,装错版本服务直接起不来,报的错还是一句看不懂的 Erlang 栈。Docker 镜像把 Erlang 运行时、RabbitMQ 服务端、management 插件一起打包好了,一条命令解决。
课程环境是 CentOS7 虚拟机,下面的命令都在上面跑过。
Docker 单机部署
获取镜像
两种方式,二选一。有网就在线拉取:
bash
# 拉取带管理控制台的官方镜像(3.8 系列,management 版自带 Web UI)
docker pull rabbitmq:3.8-management
# 想要最新稳定版可以用 3-management 这个滚动标签
docker pull rabbitmq:3-management
# 确认镜像到位
docker images | grep rabbitmq
这里有个坑:镜像标签千万别写成
rabbitmq:3.8或rabbitmq:latest。这两个标签的镜像没有预装 management 插件 ,启动后 15672 端口打不开,控制台根本不存在。认准带-management后缀的。
离线环境就用镜像包导入:
bash
# 把课前资料里的 mq.tar 上传到虚拟机,比如 /tmp 目录
# 进入目录后加载
cd /tmp
docker load -i mq.tar
# 导入成功后再看一眼
docker images
导入成功后能看到 rabbitmq 3-management 这样的镜像记录。
启动容器
bash
docker run \
-e RABBITMQ_DEFAULT_USER=itcast \
-e RABBITMQ_DEFAULT_PASS=123321 \
-v mq-plugins:/plugins \
--name mq \
--hostname mq \
-p 15672:15672 \
-p 5672:5672 \
-d \
rabbitmq:3.8-management
参数逐个说清楚,别当黑盒抄:
| 参数 | 值 | 作用与注意事项 |
|---|---|---|
-e RABBITMQ_DEFAULT_USER |
itcast |
初始化管理员账号。只在首次初始化时生效,数据卷已存在时改这个再重启没用 |
-e RABBITMQ_DEFAULT_PASS |
123321 |
上面账号的密码 |
-v mq-plugins:/plugins |
数据卷挂载 | 插件目录持久化,装了插件重启容器不丢 |
--name mq |
容器名 | 后续 docker logs mq、docker exec -it mq bash 都用它 |
--hostname mq |
主机名 | RabbitMQ 节点名默认取自主机名(rabbit@主机名)。单机不配也能跑,但做集群或保留数据时必须固定,否则容器重建后节点名变了,mnesia 数据对不上,会出现节点起不来或数据丢失 |
-p 15672:15672 |
管理控制台 | Web UI 端口,浏览器访问它 |
-p 5672:5672 |
AMQP | 消息通信端口,客户端收发消息走它 |
-d |
后台运行 | 不加就前台挂着,Ctrl+C 容器就停了 |
| 镜像名 | rabbitmq:3.8-management |
必须与本地已有的镜像 tag 一致 |
如果还要用集群或命令行工具,建议把 25672(节点间通信与 CLI 端口)也映射出来;单机学习用不上,先不映射。
启动后的目录与端口关系:
text
宿主机(CentOS7)
├── Docker
│ └── 容器 mq (hostname=mq, 节点名 rabbit@mq)
│ ├── /var/lib/rabbitmq/mnesia/rabbit@mq # 消息与元数据落盘目录
│ └── /plugins # 挂载到数据卷 mq-plugins
└── 端口映射
├── 5672 -> 容器内 5672 (AMQP 0-9-1,生产者/消费者连接)
└── 15672 -> 容器内 15672 (Management UI / HTTP API)
验证安装成功
别只靠浏览器能打开就下结论,按顺序走一遍这六步:
第一步,容器是否真的 Up
bash
docker ps --filter name=mq --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
正常输出应该能看到 Up 和 0.0.0.0:5672->5672/tcp, 0.0.0.0:15672->15672/tcp。状态是 Exited (1) 就直接跳到排障章节看日志。
第二步,日志里有没有启动完成标志
bash
docker logs -f mq
RabbitMQ 启动比较慢(Erlang 虚拟机 + 建库),通常十几秒到一分钟。看到这几行才算就绪:
text
## ## RabbitMQ 3.8.x
###### ##
########## Copyright (c) 2007-2020 VMware, Inc.
Starting broker... completed with 3 plugins.
日志里出现 completed with N plugins 就是启动完成的信号。看到 ERROR: epmd error 或 unable to connect to node 说明节点名/主机名出了问题。
第三步,端口是否真的在监听
bash
# 宿主机上确认两个端口已被 docker 监听
ss -tlnp | grep -E '5672|15672'
# 没有 ss 就用 netstat 或 lsof
netstat -tlnp | grep -E '5672|15672'
lsof -i:15672
# 从另一台机器探测连通性(换成你的虚拟机 IP)
nc -zv 192.168.150.101 5672
nc -zv 192.168.150.101 15672
第四步,不打开浏览器也能验证:调 HTTP API
bash
# 用刚设置的账号调管理 API,返回一大坨 JSON 说明服务正常
curl -u itcast:123321 http://localhost:15672/api/overview
# 只看关键字段(需要 jq,没装就去掉 | jq 部分)
curl -s -u itcast:123321 http://localhost:15672/api/overview \
| jq '{rabbitmq_version, management_version, node, erlang_version}'
期望输出类似:
json
{
"rabbitmq_version": "3.8.19",
"management_version": "3.8.19",
"node": "rabbit@mq",
"erlang_version": "23.3.4.11"
}
node 字段是 rabbit@mq,正好对上 --hostname mq。
第五步,容器内执行 rabbitmqctl 自检
bash
docker exec -it mq rabbitmqctl status
# 看监听的 AMQP 端口
docker exec -it mq rabbitmqctl status | grep -A 5 "Interfaces"
# 确认 management 插件已启用
docker exec -it mq rabbitmq-plugins list | grep management
rabbitmq-plugins list 里 rabbitmq_management 前面应该是 [E*](E 表示显式启用)。
第六步,浏览器登录控制台
访问 http://192.168.150.101:15672(IP 换成你自己的虚拟机地址),用 itcast / 123321 登录。能进到 Overview 页,安装这一步就彻底完成了。
如果想从代码层面确认 5672 真的能收发消息,用 amqp-client 跑个最小连接测试:
xml
<!-- pom.xml 依赖 -->
<dependency>
<groupId>com.rabbitmq</groupId>
<artifactId>amqp-client</artifactId>
<version>5.14.2</version>
</dependency>
java
package cn.itcast.mq.helloworld;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
/**
* 安装验证:连上 5672,声明队列,发一条消息再收一条,确认 AMQP 链路通畅。
*/
public class MqConnectionTest {
private static final String QUEUE_NAME = "install.check.queue";
public static void main(String[] args) throws Exception {
ConnectionFactory factory = new ConnectionFactory();
// 宿主机 IP + AMQP 端口 5672,不是 15672
factory.setHost("192.168.150.101");
factory.setPort(5672);
factory.setUsername("itcast");
factory.setPassword("123321");
// 虚拟主机,默认是 "/"
factory.setVirtualHost("/");
// try-with-resources 自动关闭连接,避免每次运行都留下一条 Connection
try (Connection connection = factory.newConnection();
Channel channel = connection.createChannel()) {
// 声明队列:持久化=false,独占=false,自动删除=true
channel.queueDeclare(QUEUE_NAME, false, false, true, null);
channel.basicPublish("", QUEUE_NAME, null, "hello rabbitmq".getBytes("UTF-8"));
System.out.println("消息已发送");
// 拉一条消息,autoAck=true 表示拿到即确认
var response = channel.basicGet(QUEUE_NAME, true);
if (response != null) {
System.out.println("收到消息: " + new String(response.getBody(), "UTF-8"));
} else {
System.out.println("队列为空,未收到消息");
}
}
}
}
跑完控制台打印 收到消息: hello rabbitmq,说明 5672 通信正常。再去控制台 Queues 页能看到这个队列(因为设置了 auto-delete,连接断开后会自动消失)。
管理控制台初识
登录后的界面分六个 Tab,各自看什么:
| Tab | 看什么 | 现在应该看到的状态 |
|---|---|---|
| Overview | 节点信息、硬件资源、消息吞吐总览 | 单节点 rabbit@mq,没有集群 |
| Connections | 与 Broker 建立的 TCP 连接 | 刚装好是空的,跑上面 Java 代码后会出现一条 |
| Channels | 连接之上的通道 | 有 Connection 后才有 Channel |
| Exchanges | 交换机列表 | 已有一批内置的 amq.direct、amq.topic、amq.fanout、(AMQP default) 等 |
| Queues | 队列列表 | 空的,队列要自己声明 |
| Admin | 用户、虚拟主机、权限管理 | 只有 itcast 一个用户 |
这里容易困惑的一点:交换机装好就有好几个,队列却一个都没有。因为交换机有内置的默认实例,队列必须显式声明才会创建------生产者或消费者谁先声明都行,参数要一致。
新建用户与虚拟主机
Admin → Add user,填用户名、密码,再选角色(Tags):
| Tag | 权限 |
|---|---|
management |
能登录控制台,看自己有权限的 vhost |
policymaker |
管理策略(镜像队列、TTL 等) |
monitoring |
查看节点和连接的监控数据 |
administrator |
超级管理员,能管用户、vhost、权限 |
新用户建好后默认是 No access------没有分配虚拟主机,什么都干不了。这是设计如此,不是 bug。
Admin → Virtual Hosts → Add a new virtual host,名字填 /itcast(vhost 名必须以 / 开头),建好后再回到用户列表点进该用户,在 Permissions 里选择对应 vhost,Set permission 即可。
推荐的做法是一个业务/一个用户独享一个 vhost ,比如 itcast 只给 / 的权限,新用户只给 /itcast 的权限,两边谁也看不见谁的队列和交换机。这就是 RabbitMQ 的多租户隔离。
#mermaid-svg-NFmHBtFPcEL1RSUA{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-NFmHBtFPcEL1RSUA .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-NFmHBtFPcEL1RSUA .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-NFmHBtFPcEL1RSUA .error-icon{fill:#552222;}#mermaid-svg-NFmHBtFPcEL1RSUA .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-NFmHBtFPcEL1RSUA .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-NFmHBtFPcEL1RSUA .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-NFmHBtFPcEL1RSUA .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-NFmHBtFPcEL1RSUA .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-NFmHBtFPcEL1RSUA .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-NFmHBtFPcEL1RSUA .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-NFmHBtFPcEL1RSUA .marker{fill:#333333;stroke:#333333;}#mermaid-svg-NFmHBtFPcEL1RSUA .marker.cross{stroke:#333333;}#mermaid-svg-NFmHBtFPcEL1RSUA svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-NFmHBtFPcEL1RSUA p{margin:0;}#mermaid-svg-NFmHBtFPcEL1RSUA .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-NFmHBtFPcEL1RSUA .cluster-label text{fill:#333;}#mermaid-svg-NFmHBtFPcEL1RSUA .cluster-label span{color:#333;}#mermaid-svg-NFmHBtFPcEL1RSUA .cluster-label span p{background-color:transparent;}#mermaid-svg-NFmHBtFPcEL1RSUA .label text,#mermaid-svg-NFmHBtFPcEL1RSUA span{fill:#333;color:#333;}#mermaid-svg-NFmHBtFPcEL1RSUA .node rect,#mermaid-svg-NFmHBtFPcEL1RSUA .node circle,#mermaid-svg-NFmHBtFPcEL1RSUA .node ellipse,#mermaid-svg-NFmHBtFPcEL1RSUA .node polygon,#mermaid-svg-NFmHBtFPcEL1RSUA .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-NFmHBtFPcEL1RSUA .rough-node .label text,#mermaid-svg-NFmHBtFPcEL1RSUA .node .label text,#mermaid-svg-NFmHBtFPcEL1RSUA .image-shape .label,#mermaid-svg-NFmHBtFPcEL1RSUA .icon-shape .label{text-anchor:middle;}#mermaid-svg-NFmHBtFPcEL1RSUA .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-NFmHBtFPcEL1RSUA .rough-node .label,#mermaid-svg-NFmHBtFPcEL1RSUA .node .label,#mermaid-svg-NFmHBtFPcEL1RSUA .image-shape .label,#mermaid-svg-NFmHBtFPcEL1RSUA .icon-shape .label{text-align:center;}#mermaid-svg-NFmHBtFPcEL1RSUA .node.clickable{cursor:pointer;}#mermaid-svg-NFmHBtFPcEL1RSUA .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-NFmHBtFPcEL1RSUA .arrowheadPath{fill:#333333;}#mermaid-svg-NFmHBtFPcEL1RSUA .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-NFmHBtFPcEL1RSUA .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-NFmHBtFPcEL1RSUA .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NFmHBtFPcEL1RSUA .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-NFmHBtFPcEL1RSUA .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NFmHBtFPcEL1RSUA .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-NFmHBtFPcEL1RSUA .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-NFmHBtFPcEL1RSUA .cluster text{fill:#333;}#mermaid-svg-NFmHBtFPcEL1RSUA .cluster span{color:#333;}#mermaid-svg-NFmHBtFPcEL1RSUA div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-NFmHBtFPcEL1RSUA .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-NFmHBtFPcEL1RSUA rect.text{fill:none;stroke-width:0;}#mermaid-svg-NFmHBtFPcEL1RSUA .icon-shape,#mermaid-svg-NFmHBtFPcEL1RSUA .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-NFmHBtFPcEL1RSUA .icon-shape p,#mermaid-svg-NFmHBtFPcEL1RSUA .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-NFmHBtFPcEL1RSUA .icon-shape .label rect,#mermaid-svg-NFmHBtFPcEL1RSUA .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-NFmHBtFPcEL1RSUA .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-NFmHBtFPcEL1RSUA .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-NFmHBtFPcEL1RSUA :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Set permission
Set permission
看不见
用户 itcast
virtualHost: /
用户 李四
virtualHost: /itcast
exchange / queue 组 A
exchange / queue 组 B
安装排障
按现象查,别瞎猜:
| 现象 | 常见原因 | 怎么查 / 怎么修 |
|---|---|---|
Bind for 0.0.0.0:15672 failed: port is already allocated |
端口被占用(旧容器没删、宿主机装了 RabbitMQ) | `ss -tlnp |
docker: Conflict. The container name "/mq" is already in use |
同名容器已存在 | docker rm -f mq 删掉旧的,或用新名字 |
容器起来又退出,docker ps -a 显示 Exited (1) |
启动报错 | docker logs mq 看最后几十行,多半是主机名/Erlang cookie/磁盘问题 |
日志报 unable to connect to epmd 或节点名对不上 |
主机名变了或 4369/25672 端口受限 | 固定 --hostname;确认容器内 hostname 命令输出与 /var/lib/rabbitmq/mnesia 下的目录名一致 |
日志报 insufficient free disk space |
磁盘低于阈值(默认 50MB) | df -h 看空间;调低阈值 -e RABBITMQ_DISK_FREE_LIMIT=256MB |
浏览器打不开 15672,但 curl localhost:15672 通 |
防火墙 / 安全组没放 | CentOS7:firewall-cmd --zone=public --add-port=15672/tcp --permanent && firewall-cmd --reload;云服务器还要开安全组 |
| 15672 完全没响应 | 用的镜像不带 management 插件 | docker exec -it mq rabbitmq-plugins enable rabbitmq_management;根治办法是换 -management 镜像 |
用 guest/guest 登录失败 |
RabbitMQ 3.3 起 guest 只允许 localhost 访问 | 远程登录用 RABBITMQ_DEFAULT_USER/PASS 建的账号;非要用 guest,在容器内 echo "loopback_users = none" >> /etc/rabbitmq/rabbitmq.conf 后重启服务(仅限本地环境) |
改了 -e 里的密码重启后不生效 |
默认账号只在初始化时写入,数据卷已存在就跳过 | docker exec -it mq rabbitmqctl change_password itcast 新密码 |
客户端连 5672 报 ACCESS_REFUSED |
账号密码错,或该用户对这个 vhost 没权限 | 控制台 Admin → 用户 → Permissions 检查;代码里 setVirtualHost 是否与权限匹配 |
| 控制台能看到消息,但消费者收不到 | 队列没绑定交换机 / RoutingKey 写错 | Exchanges 页点进交换机看 Bindings;用默认交换机时 RoutingKey 必须等于队列名 |
| 容器重启后队列和消息全没了 | 队列非持久化 + 没挂数据卷 | 队列声明 durable=true、消息 MessageProperties.PERSISTENT_TEXT_PLAIN;数据目录挂 -v mq-data:/var/lib/rabbitmq |
两个高频但容易忽略的点再强调一次:
一是镜像 tag 。不带 -management 的镜像没有 UI,很多人装完发现 15672 打不开,翻半天防火墙,其实是镜像选错了。
二是环境变量只在初始化生效 。RabbitMQ 的数据目录一旦有内容(mnesia 已初始化),重启时不会再读 RABBITMQ_DEFAULT_USER/PASS。改密码、改用户走 rabbitmqctl,别指望改 docker run 命令。
Linux 原生安装简述
不用 Docker 的场景(比如生产环境要求直接装在宿主机),流程是这样:
bash
# 1. 先装 Erlang ------ 版本必须匹配 RabbitMQ 要求,这是最容易翻车的一步
# RabbitMQ 3.8.x 需要 Erlang 21.3 ~ 23.x
# 版本对应表见官方文档 https://www.rabbitmq.com/which-erlang.html
yum install -y erlang
# 2. 导入签名 key、下载 rpm 并安装服务端
# 注意:yum 不会自己去 GitHub 找 rpm,文件必须先落到本地,否则报
# No package rabbitmq-server-3.8.19-1.el7.noarch.rpm available.
rpm --import https://github.com/rabbitmq/signing-keys/releases/download/2.0/rabbitmq-release-signing-key.asc
wget https://github.com/rabbitmq/rabbitmq-server/releases/download/v3.8.19/rabbitmq-server-3.8.19-1.el7.noarch.rpm
yum install -y rabbitmq-server-3.8.19-1.el7.noarch.rpm
# 3. 开机自启 + 启动
systemctl enable rabbitmq-server
systemctl start rabbitmq-server
systemctl status rabbitmq-server
# 4. 开启管理控制台插件(原生安装默认不装 UI)
rabbitmq-plugins enable rabbitmq_management
# 5. 建管理员账号,别用 guest 远程登录
rabbitmqctl add_user itcast 123321
rabbitmqctl set_user_tags itcast administrator
rabbitmqctl set_permissions -p / itcast ".*" ".*" ".*"
# 6. 放通端口
firewall-cmd --zone=public --add-port=5672/tcp --permanent
firewall-cmd --zone=public --add-port=15672/tcp --permanent
firewall-cmd --reload
Ubuntu / Debian 换成 apt-get install -y erlang rabbitmq-server,其余步骤一致。
两种方式对比:
| Docker 部署 | Linux 原生部署 | |
|---|---|---|
| 前置依赖 | 只需要 Docker | 要自己解决 Erlang 版本匹配 |
| 耗时 | 分钟级 | 十分钟到半小时,卡在依赖上更久 |
| 升级/回滚 | 换镜像 tag 重跑 | rpm/apt 升级,回滚麻烦 |
| 多实例/集群 | 起多个容器即可 | 多套目录与配置,端口要改 |
| 适合场景 | 本地开发、学习、测试环境 | 生产环境、对资源有特殊管控要求的场景 |
集群部署在这两者的基础上做:多个节点共享同一个 Erlang Cookie(~/.erlang.cookie 内容必须一致),节点名 rabbit@主机名 要能互相解析,再 rabbitmqctl join_cluster 把节点加进去,最后配镜像队列策略做队列高可用。单机阶段先不用管。
命令速览
| 命令 | 作用 |
|---|---|
docker pull rabbitmq:3.8-management |
拉取带控制台的镜像 |
docker load -i mq.tar |
从本地镜像包导入 |
docker run ... rabbitmq:3.8-management |
启动容器(见上文完整参数) |
docker ps --filter name=mq |
看容器状态与端口映射 |
docker logs -f mq |
看启动日志,找 completed with N plugins |
docker exec -it mq rabbitmqctl status |
容器内查看节点状态 |
docker exec -it mq rabbitmq-plugins list |
查看已启用插件 |
docker exec -it mq rabbitmqctl change_password 用户 新密码 |
改密码 |
docker exec -it mq rabbitmqctl list_queues |
列出队列 |
docker stop mq / docker start mq |
停止 / 重新启动容器 |
docker rm -f mq |
强制删除容器(数据卷不会一起删) |
| `ss -tlnp | grep -E '5672 |
curl -u 用户:密码 http://localhost:15672/api/overview |
用 HTTP API 验证服务可用性 |
官方文档
- RabbitMQ 官方文档
- Downloading and Installing RabbitMQ
- Erlang 版本兼容对照表
- Management Plugin / HTTP API
- Docker Hub: rabbitmq 镜像
- AMQP 0-9-1 协议模型说明
总结
- RabbitMQ 是 Erlang 写的 AMQP 实现,强项是可靠性和低延迟,不是极致吞吐;装它最省事的方式是 Docker +
-management镜像。 - 端口分工必须记住:5672 收发消息,15672 看控制台。连错端口是新手最常见的低级错误。
docker run里的--hostname单机可省,但一旦要保留数据或做集群就必须固定------节点名rabbit@主机名跟着主机名走。- 验证安装别只看浏览器:容器 Up → 日志
completed with N plugins→ 端口监听 → HTTP API 返回 JSON → 控制台登录,五步全过才算数。 - 多用户场景下用 virtualHost 做隔离,一个用户配一个 vhost 并单独授权,别所有人共用
/。