
1. 项目背景大促前两周推广中台被点名做「订单状态触达」用户支付成功后要在 3 秒内把短信、站内信、积分到账、广告归因四件事推出去。产品经理的原话是「别再让用户刷新订单页干等了」。现网做法是订单服务用 HTTP 同步调用四个下游。平时 QPS 只有 200勉强能扛。压测一拉到 3000链路立刻变形短信网关偶发 800ms 超时订单接口 P99 被拖到 1.2s收银台开始掉单。超时后订单服务重试短信网关其实已经发出去了用户收到两条「支付成功」。积分服务发布窗口宕机 2 分钟订单接口直接 502客服电话被打爆。测试同学无法单独演练「只失败站内信、其它照发」四个 HTTP 绑死在一个事务里。把同步调用画出来痛点一目了然用户支付 │ ▼ 订单服务 ──HTTP──► 短信网关 慢、超时、重复 ──HTTP──► 站内信 ──HTTP──► 积分服务 挂了就拖垮下单 ──HTTP──► 广告归因运维希望下游挂了订单仍能提交测试希望能对「支付成功」这条事件单独做契约测试开发希望一套术语能和 Erlang 进程对上而不是各说各的「队列」「通道」「交换机」。本章不讲如何声明 Topic也不讲 Confirm。先把Broker 是什么、一张图里每个框对应哪个进程、术语如何对齐 Management UI钉死。没有统一语系后面 39 章会在「Channel 和 Connection 搞混」「把 Exchange 当成 Queue」上反复踩坑。选型约束也很明确公司已有 Java / Python 服务IoT 侧还有 MQTT 设备心跳不能为每种协议再买一套中间件。RabbitMQ 4.x 的卖点正是多协议进同一套队列内核。测试同学还卡在契约怎么写HTTP 用例可以断言状态码消息用例却不知道该断言「连接数」「通道数」还是「队列名」。运维则把 5672 端口通了当成 Broker 健康完全没看过节点是否已经serving。架构评审甚至出现两张互相矛盾的图——一张把 Exchange 画成队列一张把 Channel 画成 TCP。本章必须先把词典和架构钉死否则后面的 Confirm、死信、仲裁队列都会在错误的词上讨论正确的技术。2. 项目设计会议室白板左边画着订单 HTTP 调用图右边还空着。小胖咬着鸡翅根先开球。小胖这不就是食堂打饭吗窗口慢队伍就堵到门口。咱们加几个窗口不就完了为啥非要搞个 Broker听着跟证券公司似的。大师加窗口是扩容解决不了「窗口关门时门口是不是必须一起关门」。订单是门口短信是窗口。我们要的是门口把号牌一撕人就可以走窗口回头慢慢炒菜。号牌就是消息收号牌的柜台就是 Broker。技术映射同步 HTTP 你站在窗口等到菜出锅Broker 先取号离开出餐后由窗口叫号。小白取号之后号牌放哪丢了怎么办四个窗口会不会抢同一张号牌还有测试怎么断言「号牌一定送到了短信窗口」大师号牌先放到Queue里谁来取谁是Consumer。一张号牌默认只被一个消费者拿走竞争消费若要短信和站内信都收到就复制成两份绑到两个队列——这是Exchange Binding的事第 6 章展开。丢不丢取决于你有没有Ack / Confirm第 8、9 章再打。测试今天先学会在 UI 里看到「有没有队列、有没有消费者」不要一上来抓包。小胖那客户端连 Broker是连一根网线还是连很多根我看文档又是 Connection 又是 Channel跟银行柜台一样绕。大师一根 TCP 是Connection好比你进银行大门。大门里开多个窗口叫Channel窗口 1 专门办支付通知窗口 2 专门办积分。窗口办砸了Channel 异常只关那一扇窗你在大厅打架Connection 异常保安直接请出大门。技术映射Connection 一条 TCP 一个rabbit_reader进程Channel 这条连接上的逻辑复用Broker 侧一个 Channel 一个rabbit_channel进程。小白那如果某个 Channel 进程崩溃会不会把整个节点打挂Erlang 不是号称让进程随便死吗另外 MQTT 设备和 AMQP 应用能不能共用后面的队列我担心协议一多内核要写两套。大师崩溃隔离靠 OTP 监督树Channel 挂了监督者重启或关闭该通道Connection 还可以活。节点挂不挂取决于是不是把危险工作做成了brutal_kill的核心进程——应用协议处理故意做成可死可重启。多协议方面4.x 把「协议解码」和「队列类型」拆开AMQP 0-9-1、AMQP 1.0、MQTT、STOMP、Stream 都在门口换鞋进门后走同一套rabbit_queue_type。所以 IoT 心跳和订单通知可以进同一套存储引擎只是接入模块不同。小胖我还听运维念叨 VHost、Plugin、Feature Flag、Khepri这四个又是啥食堂设施大师VHost是食堂分区订单区和营销区的锅碗瓢盆不能混用权限也分开。Plugin是可选档口管理台、Prometheus、Shovel 都是插件不装也能炒菜。Feature Flag是升级时的阀门集群没全体就位不能开新菜谱。Khepri是 4.x 默认的元数据账本谁开了哪口锅、哪张菜单交换机/队列/绑定/用户记在这本共识账上真正的菜消息体不在这本账里而在队列自己的磁盘结构里。技术映射元数据Khepri≠ 消息存储Classic CQv2 / Quorum Ra log / Stream Osiris。搞混这两者就会把「声明队列失败」理解成「消息丢了」。小白那 Ack 和 Confirm 要不要画进今天的架构图不画的话测试会不会以为「进了队列就算业务成功」另外 Prefetch 是消费者的还是 Channel 的我怕一张图塞爆但漏画又会误导验收。大师今天只在图上留两个「可靠性闸门」虚线框发布侧 Confirm、消费侧 Ack框里写「第 8/9 章」不要画内部箭头。Prefetch 是 Channel/Consumer 上的 QoS属于消费侧闸门的旋钮同样点到为止。架构图的职责是定位不是把后续 39 章叠罗汉。技术映射架构图回答「消息经过哪些进程」Ack/Confirm 回答「经过了是否算数」。两者不要抢同一张图的墨。小白架构图上还要画 Node 吗单机是不是就一个 BrokerCluster 和第 1 章有什么关系大师Broker是逻辑上的「消息服务」Node是一个 Erlang 节点通常对应一个 OS 进程。单机 一个 Node 的 Broker。集群是多个 Node 组成一个 Broker 对外服务第 17 章才组网。今天图上先画单 Node但把「以后这里会变成三个方块」的位置留出来避免测试环境用单机语义去验收支付的多副本。三人在白板上落下第一张必须进 Wiki 的图客户端、协议入口、Channel、Exchange、Queue、Consumer、管理面、元数据、插件。小胖负责明天对着 UI 把每个词圈出来小白负责在源码里找到rabbit_reader和rabbit_channel的模块注释大师负责卡住「不准把 Channel 叫成连接」。3. 项目实战实战原则先把 Broker 跑起来用眼睛在 UI 里对术语再用 20 行客户端证明 Connection / Channel 真实存在最后打开源码确认进程模型不是口头禅。3.1 环境准备项目版本 / 说明Docker Desktop能跑rabbitmq:4-management即可镜像rabbitmq:4-management4.x 最新管理台版本含 15672Python3.11仅本节验证建连用依赖pika1.3.2源码树本专栏仓库rabbitmq-server/只读不强制编译工作目录建议promo-mq/ch01/。docker-compose.yml# promo-mq/ch01/docker-compose.ymlservices:rabbitmq:image:rabbitmq:4-managementcontainer_name:rabbit-promo-1hostname:rabbit-promo-1ports:-5672:5672# AMQP 0-9-1-15672:15672# Management UI / HTTP APIenvironment:RABBITMQ_DEFAULT_USER:promoRABBITMQ_DEFAULT_PASS:promo_dev_2026RABBITMQ_DEFAULT_VHOST:promovolumes:-rabbit-promo-1-data:/var/lib/rabbitmqhealthcheck:test:[CMD,rabbitmq-diagnostics,-q,ping]interval:10stimeout:5sretries:10volumes:rabbit-promo-1-data:启动dockercompose up-ddockercompose logs-frabbitmq期望日志出现类似不同 4.x 小版本插件数可能略多但必须有 management2026-08-28 12:01:03.20100000:00 [info] 0.234.0 Running boot step database defined by app rabbit 2026-08-28 12:01:04.88000000:00 [info] 0.234.0 Running boot step networking defined by app rabbit 2026-08-28 12:01:05.10200000:00 [info] 0.234.0 started TCP listener on [::]:5672 2026-08-28 12:01:05.44800000:00 [info] 0.234.0 Server startup complete; 4 plugins started. * rabbitmq_prometheus * rabbitmq_management * rabbitmq_web_dispatch * rabbitmq_management_agentrabbitmq-diagnostics ping在 startup complete 之前可能失败这是正常窗口不是镜像损坏。坑 1端口 5672 / 15672 被本机旧 RabbitMQ 或云客户端占用。Windows 上用netstat -ano | findstr 5672查 PID换端口映射5673:5672时客户端也要改端口。坑 2默认镜像里guest只能从 localhost 登录我们改了默认用户不要再用 guest/guest。坑 3RABBITMQ_DEFAULT_VHOSTpromo只在数据卷为空的第一次启动生效。若你改环境变量却沿用旧 volumeVHost 仍是/。处理docker compose down -v后重建开发机可以生产严禁。3.2 步骤一用 CLI 把「Node / Broker / 插件」看见步骤目标确认单节点 Broker 已 serving并记下节点名、监听端口、插件列表。dockerexecrabbit-promo-1 rabbitmq-diagnosticspingdockerexecrabbit-promo-1 rabbitmq-diagnostics statusdockerexecrabbit-promo-1 rabbitmq-diagnostics listenersdockerexecrabbit-promo-1 rabbitmq-plugins list-eping应返回Ping succeeded。status中重点看RuntimeErlang 版本、OS PIDCluster节点名形如rabbitrabbit-promo-1这就是NodeListenersamqp5672、http15672Alarms应为空坑在容器刚起来的 510 秒内status可能报 node not running。等 healthcheck 变 healthy或循环ping。3.3 步骤二Management UI 对照术语词典浏览器打开http://localhost:15672用户promo/promo_dev_2026。按下面清单点一遍把 UI 标签和术语写成对照表贴进 Wiki这就是本章的交付物之一UI 入口术语你现在应该看到什么OverviewBroker / Node1 个 noderabbitrabbit-promo-1Overview → Global countsConnection / Channel / Queue尚未有业务连接时 Connections0ConnectionsConnection下一步跑 Python 后出现 1 条 TCPChannelsChannel一条连接上会出现 1N 个 ChannelExchangesExchange默认已有amq.direct/amq.topic/amq.fanout/amq.headers以及空字符串默认交换器Queues and StreamsQueue当前为空第 7 章再声明Admin → Virtual HostsVHostpromoAdmin → UsersUser tagspromo通常带 administratorAdmin → Feature FlagsFeature Flag4.x 一长串已启用标志Admin → PoliciesPolicy空第 13 章再用坑有人登录后在 Overview 看不到队列是因为右上角 VHost 过滤器选成了/而不是promo。把下面这张定义表与 UI 对照表一起入库。面试和值班都可以用同一份术语一句话定义本章怎么亲眼看到Broker对外提供消息服务的逻辑整体整个 Docker 容器 / 一个集群Node一个 Erlang 节点通常一个 OS 进程rabbitrabbit-promo-1VHost权限与对象的命名空间Admin → Virtual Hosts →promoConnection一条 TCP可叠加 TLS及连接进程Connections 页一个rabbit_readerChannel连接上的逻辑复用窗口Channels 页一个rabbit_channelExchange按规则把消息拷到零个或多个队列Exchanges 页Queue消息的容器消费者从这里取Queues 页BindingExchange 到 Queue 的订阅规则某 Exchange 的 Bindings 页Routing Key发布时带的路由标签发布对话框 / 客户端参数Publisher发消息的应用角色本机 Python 脚本Consumer取消息的应用角色本章暂无第 9 章Message属性 报文体队列 Get MessageAck消费侧确认处理完第 9 章Confirm发布侧确认 Broker 已受理第 8 章Prefetch未确认投递的窗口大小第 9、24 章Policy运维给对象批量打的参数补丁Admin → PoliciesPlugin可选 OTP 应用管理台、Shovel…rabbitmq-plugins listFeature Flag集群升级的能力阀门Admin → Feature Flags运行结果文字登录后 Overview 的Global counts在未跑脚本时应 Connections≈0、Channels≈0Exchanges 数量大于 0内置amq.*。若 Exchanges 为 0说明你看错了 VHost 或连错了节点。3.4 步骤三用客户端证明 Connection ≠ Channel步骤目标一条 TCP 上打开两个 Channel在 UI 里数出 1 个 Connection、2 个 Channel。pipinstallpika1.3.2# promo-mq/ch01/open_two_channels.pyimporttimeimportpika paramspika.ConnectionParameters(host127.0.0.1,port5672,virtual_hostpromo,credentialspika.PlainCredentials(promo,promo_dev_2026),client_properties{connection_name:ch01-wiki-tour},heartbeat30,)connpika.BlockingConnection(params)ch1conn.channel()# Channel 1以后给订单通知用ch2conn.channel()# Channel 2以后给积分用print(connection_open:,conn.is_open)print(channel1:,ch1.channel_number,open,ch1.is_open)print(channel2:,ch2.channel_number,open,ch2.is_open)print(keep 60s, open Management UI → Connections / Channels)time.sleep(60)ch1.close()ch2.close()conn.close()运行python open_two_channels.py期望输出connection_open: True channel1: 1 open True channel2: 2 open True keep 60s, open Management UI → Connections / Channels这 60 秒内刷新 UIConnections 里应出现ch01-wiki-tourChannels 里两条channel编号为 1 和 2归属于同一connection。坑virtual_host写成/会鉴权失败ACCESS_REFUSED或 403。坑公司代理把 5672 当 HTTP 探活会出现连上立刻被复位。用Test-NetConnection 127.0.0.1 -Port 5672排除。3.5 步骤四把架构图写进 Wiki可讲解版本把下面这张图原样贴到团队 Wiki「推广中台 / 消息基础设施 / 01-架构与术语」。讲解时按箭头从左到右不要从插件讲起。Node Erlang VMrabbitrabbit-promo-1ClientsTCP 5672观测观测声明/权限声明/权限Publisher订单服务Consumer短信/积分MQTT 设备协议入口AMQP 0-9-1 / 1.0 / MQTT / STOMP / Streamrabbit_reader一条 Connection 一个进程rabbit_channel一个 Channel 一个进程Exchange按 Binding 路由rabbit_queue_typeclassic / quorum / streamQueue 存储Management HTTP APIKhepri 元数据Plugins对照源码入口只要求认识文件不要求读懂每一行启动与 Boot Step 注册在deps/rabbit/src/rabbit.erl-rabbit_boot_step({pre_boot, [{description, rabbit boot start}]}). %% ... -rabbit_boot_step({database, [{mfa, {rabbit_db, init, []}}, {requires, rabbit_registry}, {enables, external_infrastructure}]}).监督者在deps/rabbit/src/rabbit_sup.erl子进程以transient方式挂到根监督树上单个 worker 挂了按 OTP 策略重启这就是「Channel 死不等于节点死」的根。网络监听在deps/rabbit/src/rabbit_networking.erl注释写明该模块启动 AMQP 0-9-1 监听STOMP/MQTT 等插件会复用其中的 TCP 工具函数。连接实现的模块头注释已经是最好的教材%% This is an AMQP 0-9-1 connection implementation. %% Every connection (as in, a process using this module) %% is a controlling process for a server socket. %% * Performing protocol handshake %% * Parsing incoming data and dispatching protocol methods %% * Channel management%% rabbit_channel processes represent an AMQP 0-9-1 channels. %% Channels are responsible for implementing the logic behind %% the various protocol methods: %% * Routing messages to queue processes %% * Keeping track of publisher confirms坑在 IDE 里搜索Connection会命中几百处 HTTP 连接。术语对齐时请搜模块名rabbit_reader/rabbit_channel不要搜英语单词。3.6 完整代码清单本章交付目录promo-mq/ch01/ docker-compose.yml open_two_channels.py README.md # 粘贴术语表 mermaid 图仓库路径专栏后续统一column/samples/ch01/若你们把示例推进 Git按此放置。3.7 测试验证开发自测dockerexecrabbit-promo-1 rabbitmqctl list_connections name user vhostdockerexecrabbit-promo-1 rabbitmqctl list_channels pid connection number在 Python 脚本 sleep 期间应看到 1 行 connection、2 行 channel。测试同学用 HTTP API第 5 章会系统讲这里先当验收探针curl-s-upromo:promo_dev_2026 http://127.0.0.1:15672/api/overviewcurl-s-upromo:promo_dev_2026 http://127.0.0.1:15672/api/vhostsoverview的object_totals.connections在脚本运行时应 ≥ 1vhosts中必须出现promo。把这两条写成测试用例「TC-CH01-01 默认 VHost 存在」「TC-CH01-02 可建立 AMQP 连接」。4. 项目总结优点与缺点对比对象优点缺点RabbitMQ Broker 模型Connection/Channel 复用降低 TCP 成本协议与队列类型解耦Erlang 进程隔离故障术语比 Redis List / Kafka Topic 多新人前两周容易混同步 HTTP 编排放大促触达强一致、调试直观下游故障耦合、超时放大、难以单独重试Kafka 作为对照日志回放、超高吞吐运维与分区模型更重本专栏业务更需要队列语义竞争消费、Ack云厂商简单 MQ托管省事无法对着源码讲进程模型后续自定义插件、仲裁队列排障会盲Broker 架构的优点1一张图能同时服务开发怎么发、运维听哪个端口、测试在哪断言。2多协议同核避免 MQTT 和 AMQP 两套积压。3元数据与消息存储分离排障时先问「声明失败还是投递失败」。缺点1单机语义不能代表集群。2Management UI 本身不是数据面高并发时不要用 UI 当压测入口。3默认交换器、默认用户容易让人跳过 VHost 设计。适用场景需要统一术语的新团队接入 RabbitMQ。大促类「一事件多下游」且下游 SLA 不一致。已有 AMQP计划再接 MQTT/STOMP想确认能否共用 Broker。架构评审要一张可讲解的单节点图。不适用已经确定用 Kafka 做无限保留事件湖那是第 20 章 Stream 也只能部分覆盖的场景需要本第 1 章就给出集群脑裂方案那是第 17、38 章。注意事项4.x 默认元数据是 Khepri不要拿 3.8 的 Mnesia 分区故事套上来。guest仅 loopback容器互连请自建用户。Docker 环境变量只在空数据目录首次启动生效。讲解架构时把 Feature Flag 和 Plugin 分开插件没装是功能没有Flag 没开是集群协议没对齐。常见踩坑生产「连接数」和「通道数」当同一个容量指标。某促销把 Tomcat 线程开到 400每个请求newConnection()节点文件描述符打满。根因把 Channel 级复用做成了 Connection 级滥用。正确进程内长连接 少量 Channel。在/VHost 声明了订单队列应用连的是promo。现象是 NOT_FOUND开发以为 Broker 坏了。根因UI 过滤器与客户端virtual_host不一致。把 Overview 上的「消息入队速率」当成业务成功。路由到无消费者的队列也算入队。根因没有把 Consumer 数、Unacked、死信纳入同一张验收表。思考题一条 Connection 上 100 个 Channel和 100 条 Connection 各 1 个 Channel对 Broker 进程数、TCP 状态、心跳流量分别意味着什么提示对着rabbit_reader与rabbit_channel的进程模型答。为什么「Khepri 里能看到队列声明」不能推出「磁盘上一定还有那条支付成功消息」第 2 章附录会给出思考题 1 的进程数估算思考题 2 在第 7、19、38 章从存储引擎分别回答。推广计划提示部门本章怎么用协作开发必读。把术语表贴进代码评审清单PR 描述禁止把 Channel 写成 Connection提供open_two_channels.py作为新手环境冒烟测试必读。把 TC-CH01-01/02 纳入环境冒烟学会看 UI 的 VHost 过滤器后续第 8–10 章语义用例都依赖本章术语运维必读 Overview / listeners / 插件列表暂不必深究rabbit_queue_type负责镜像版本钉死与 5672/15672 防火墙架构确认 Wiki 架构图作为后续章节的唯一封面图禁止各小组再画互相矛盾的「MQ 示意图」下一章我们钻进deps/目录用源码树和一次真实启动把「插件是怎么挂到 Broker 上的」变成可复述的路径。附录 A完整代码与仓库位置专栏示例约定仓库rabbitmq-server/column/samples/ch01/与正文promo-mq/ch01/内容相同便于 Git 评审。若团队独立 Git请把docker-compose.yml、open_two_channels.py放在同一目录并附本附录。open_two_channels.py已在 3.4 节给全量可运行代码无需再拼。补充requirements.txtpika1.3.2附录 B思考题说明本章两道思考题的参考答案写在第 2 章附录 C避免开篇剧透进程数估算。请先自己在纸上画「100 Channel vs 100 Connection」再翻下一章。延伸阅读与资源Dify 从入门到进阶LLM 应用平台实战修炼Java 工程师进阶从 JVM 生产排障到OpenJDK原理NumPy 从入门到生产落地全链路实战指南科学计算/向量化Redis 8 实战精讲从 CRUD 到源码构建高可用缓存系统Redis 实战修炼与原理进阶Python 3实战精进从脚本到高并发订单引擎python入门Rquests从菜鸟脚本到企业级SDK的网络实战圣经Milvus向量数据库实战修炼从 0 到 1精通向量检索与生产落地MongoDB 实战进阶与内核修炼后端工程师的 AI 转型第一课Ollama 与私有化大模型实战10倍开发者的 Dify 魔法书从零构建全栈 AI 应用后端工程师转型AI第一课-Ollama 与私有化大模型实战大型语言模型(LLM) vLLM 高性能推理落地实战Agent开发之LlamaIndex 实战修炼与源码进阶大语言模型Transformers 实战修炼与源码剖析