ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

第14章:RabbitMQ 内存磁盘告警与连接限流入门

2026/9/10 9:48:59 拓冰建站 浏览量
第14章:RabbitMQ 内存磁盘告警与连接限流入门 1. 项目背景大促预演第三轮发布器 Confirm 突然全部超时。开发说 MQ 挂了。运维一看 Overview 顶上大红Memory alarm。消费者还在慢慢 Ack队列深度并不夸张但发布已被 Broker 主动掐住。没人训练过「红色报警时该停发布还是该加机器」值班把节点重启了报警消失几分钟又回来——因为消费者仍慢、水位仍相对宿主机算错第 3 章 cgroup 故事。磁盘报警更狠剩余空间低于disk_free_limit同样阻塞发布。有人把日志打满磁盘看起来像「业务堆积」其实是日志策略。连接上限则是另一类应用漏关 Connectionconnection_max一到新收银台建连失败旧连接还占着。闸门逻辑要先建立直觉内存/磁盘告警 → 集群内多节点时发布侧节流 → 消费仍希望继续好把水排出去 连接/通道过多 → 新握手或 channel.open 失败 credit flow → 更快的发布被更细的信用证堵住第 24 章第 3 章基线已经写了绝对内存水位。本章用可逆的 ctl 命令在实验室把水位拧到极低让全员看见 blocked 连接而不是只在 Wiki 上看名词。做完必须恢复水位否则开发机再也发不出支付消息。源码里rabbit_alarm写明任一节点资源告警整个集群都应停止发布直到告警清除。单机实验也能看到连接进入 blocked。2. 项目设计小胖把水库泄洪照片甩出来。小胖这不就是水库水位线吗超过就关闸门。那为啥还让消费者继续放水直接整个 Broker 暂停不更干净连接数限制是不是多此一举现代服务器一万连接算什么大师关全部闸门等于不让下游把库存消化掉水库永远满。所以设计是只停进水发布不停出水消费。连接限制不是因为服务器怕一万条 TCP是因为每条连接一个 reader 进程泄漏时先把应用打死比把整机 OOM 更可控。一万个泄漏的收银台连接会先吃光内存再触发本章告警那时已经晚了。技术映射Memory/Disk alarm 关进水消费 泄洪connection_max 进闸安检人数上限。小白相对水位 0.4 在容器里为啥不准blocked 时客户端会收到什么方法Confirm 会挂起还是 nack告警是节点级还是队列级磁盘监控文件在哪网络盘会误报吗channel_max和channel_max_per_node什么区别恢复后要不要重启应用page 到磁盘的 paging_ratio 还在吗大师相对值看 Erlang 眼中的「总内存」cgroup 限制经常看不见所以第 3 章用 absolute。blocked 时 AMQP 会connection.blocked客户端应停发Confirm 不会变成成功会卡住直到解除或超时。告警是节点资源不是某条队列的私人报警多节点时还会传播一个满了大家停写。磁盘看节点数据目录所在文件系统网络盘抖动会闪报要有抑制。channel_max是连接协商上限channel_max_per_node是节点上通道总数帽。恢复后不必重启应用但要确认没有把超时当成 SENT。经典队列 paging 历史参数不要当 4.x 主手段CQv2/水位才是正道。小胖实验把水位拧到「一杯水」灌一点就红灯再拧回来看绿灯。别在生产节点玩。大师加两枪把connection_max调到很小看新连接失败SOP 写成「谁宣布、谁泄洪、谁恢复水位、谁通知开发停发」。没有 SOP重启节点会变成默认动作。技术映射set_vm_memory_high_watermark是运行时旋钮重启若 conf 仍是旧值会覆盖回来——实验前想清楚持久化配置。小白实验室灌消息会不会把磁盘真写满如何安全恢复消费者不在时只停发布有用吗大师用很小的 max-length第 13 章 Operator限制实验室深度或灌完就消费。恢复set_vm_memory_high_watermark回到绝对字节或 0.4容器仍推荐绝对。没有消费者时告警只能等 TTL/丢弃/人为 Purge所以 SOP 第一条是看消费者是否还在。只停发布救不了「没人干活」。小胖SOP 贴机房墙上红灯停发、查消费者、查磁盘日志、恢复水位、再开发。测试把红灯当用例不要当「环境坏了重装 Docker」。3. 项目实战3.1 环境准备实验室节点。记录当前水位以便恢复dockerexecrabbit-promo-1 rabbitmq-diagnostics status|rg-iwatermark|alarm|diskdockerexecrabbit-promo-1 rabbitmqctlevalvm_memory_monitor:get_vm_memory_high_watermark().准备一个能灌的队列注意 Operator max-length20 会让你灌不满内存——先clear_operator_policy或换不匹配的队列名q.lab.mem。# promo-mq/ch14/prep.pyimportpika cpika.BlockingConnection(pika.ConnectionParameters(127.0.0.1,5672,order,pika.PlainCredentials(app_order,ord_dev_2026)))chc.channel()ch.queue_declare(q.lab.mem,durableTrue)print(lab queue)c.close()若 app_order 正则不允许q.lab.改用管理员账号或把实验放在promoVHost。权限与告警实验冲突时用ops_admin走 HTTP 发布。3.2 步骤一拧低内存水位步骤目标运行时设置极低水位使后续发布触发告警。# 相对值极小仅实验室dockerexecrabbit-promo-1 rabbitmqctl set_vm_memory_high_watermark0.01dockerexecrabbit-promo-1 rabbitmqctlevalrabbit_alarm:get_alarms().运行结果若当前占用已超过 1%可能立刻告警。Overview 出现红色 Memory。rabbitmqctl status含 Alarms。源码注释解释为何不能让 VM 吃满%% In practice Erlang shouldnt be allowed to grow to more than a half %% of available memory. ... during garbage collection, Erlang tries to %% allocate a huge chunk of continuous memory告警传播语义%% * per-node resource (disk, memory) alarms for the whole cluster. If any node %% has an alarm, then all publishing should be disabled across the %% cluster until all alarms clear.坑0.01 在空闲节点可能不够触发再降或灌大消息。坑做完不恢复所有人以为 Broker 坏了。恢复命令写在 SOP 第一步的反面。3.3 步骤二观察发布阻塞步骤目标发布端进入 blocked消费仍可进行。# promo-mq/ch14/publish_until_blocked.pyimportpika,time blocked[]defon_blocked(conn,reason):blocked.append(reason)print(BLOCKED,reason)defon_unblocked(conn):print(UNBLOCKED)paramspika.ConnectionParameters(127.0.0.1,5672,order,pika.PlainCredentials(app_order,ord_dev_2026),blocked_connection_timeout120)connpika.BlockingConnection(params)conn.add_on_connection_blocked_callback(on_blocked)conn.add_on_connection_unblocked_callback(on_unblocked)chconn.channel()ch.confirm_delivery()n0try:whilen5000:ch.basic_publish(,q.lab.mem,bx*256,propertiespika.BasicProperties(delivery_mode2))n1ifn%2000:print(sent,n,blocked_events,blocked)conn.process_data_events(time_limit0.1)exceptExceptionase:print(publish stopped,type(e).__name__,e,n,n)conn.close()运行结果控制台出现BLOCKEDConfirm 卡住或超时。另开消费者 Ack水位下降后UNBLOCKED。UI 连接列表可见 blocked 状态。坑blocked_connection_timeout到期会关连接测试要与告警 SOP 一致超时失败不是 SENT。坑HTTP API 发布也可能 5xx/阻塞不要用 UI 灌内存。3.4 步骤三恢复水位# 与第 3 章基线对齐绝对水位示例dockerexecrabbit-promo-1 rabbitmqctl set_vm_memory_high_watermark absolute 1200MBdockerexecrabbit-promo-1 rabbitmqctlevalrabbit_alarm:get_alarms().运行结果Alarms 变空发布恢复。若 conf 里仍是相对值下次重启会回到 conf。要把实验室临时值与 Git 基线对齐。磁盘告警实验可选、危险更低的做法不要真把盘写满改为阅读disk_free_limit与rabbitmq-diagnostics status中的 Free disk。SOP 写磁盘报警先查日志与 tracing 文件再查消息 store。3.5 步骤四连接上限直觉dockerexecrabbit-promo-1 rabbitmqctlevalapplication:get_env(rabbit, connection_max).实验室可在 conf.d 临时加max_connections 3后重启会踢多余连接仅实验机。验证第四条连接失败。做完删掉该行。# 仅实验室片段禁止带去预发 max_connections 3 channel_max 16schema 中max_connections映射connection_maxmax_channels_per_connection映射channel_max。坑上限含管理插件自身连接3 可能连 UI 都挤掉。实验用 20 更稳或接受 UI 暂时进不去。3.6 告警 SOP本章交付物1. Overview 是否红色 Alarm记下 memory / disk。 2. 宣布「暂停新发布」——开发停扫 outbox已超时标 FAILED。 3. 看 consumers为 0 则先拉起消费不重启 Broker。 4. 内存查 unacked、队列深度、是否泄漏连接list_connections。 5. 磁盘df、日志目录、tracing、消息 store。 6. 水位是否被实验命令改过与 Git conf 对照。 7. 恢复后确认 Unblocked再允许 outbox drain。 8. 复盘相对水位max-length消费者 prefetch3.7 完整代码清单column/samples/ch14/prep.py column/samples/ch14/publish_until_blocked.py column/samples/ch14/SOP.md3.8 测试验证编号操作期望TC-CH14-01低水位 灌消息Alarm BLOCKEDTC-CH14-02消费排水 恢复水位UNBLOCKEDTC-CH14-03恢复后 get_alarms空TC-CH14-04实验结束水位基线1200MB 或 Git 值TC-CH14-05无消费者时 SOP 第一步文档含「先看 consumers」值班检查单每次演练有人专门负责「恢复水位」演练结束把get_alarms()截图进工单。禁止把重启节点写在 SOP 第一条。测试环境的 0.01 水位若被误提交到 Git预发会永远 blocked代码评审要扫set_vm_memory_high_watermark。连接泄漏用list_connections按 user 聚合先踢再扩上限。告警期间 Confirm 超时与第 8 章五个分桶的blocked对齐。实验室灌消息前看 Operator max-length 是否把队列钉死在 20 条——钉死就触发不了内存告警。告警实验与策略实验不要叠在同一条业务队列上。专用q.lab.mem做完 Purge 或删队列。演练时三个人分角一人拧水位、一人盯 UI 红灯、一人负责恢复命令。恢复人不得参与「再灌一点看看」避免玩嗨忘记拧回。恢复后立刻跑一条第 8 章 Confirm 成功用例证明发布路径活了再宣布演练结束。若 Confirm 仍超时查是否 Operator 上限、是否连接仍 blocked、是否消费者把通道耗尽。把时间线写下T0 拧低、T1 首次 BLOCKED、T2 恢复命令、T3 UNBLOCKED用于以后对照「正常恢复要多久」。容器里看内存不要只看 Overview 百分比要对docker stats与 watermark 绝对值。百分比漂亮可能只是相对值骗了你。磁盘告警 SOP 单独一页先停 tracing 与 debug 日志再看消息目录最后才是扩盘。扩盘不改 watermark 的绝对磁盘阈值的话可能仍立刻报警。连接上限误伤 UI 时用rabbitmqctl在容器网络内操作不要依赖 15672。文档写清「UI 进不去时 ctl 仍可用」否则值班在红灯时束手。应用侧在收到 blocked 后应停止 outbox 扫描并打 P1 事件而不是加大重试把 Broker 打得更死。解除后 backoff 再扫。把这点写进第 8 章发布器本章 SOP 与之交叉引用。测试混沌时允许失败但不允许演练结束水位与 Git 不一致——用断言比较get_vm_memory_high_watermark与基线文件。恢复命令做成一键脚本restore_watermark.sh只允许从 Git 读取目标值禁止人口头报数字。演练结束跑脚本是门禁。若节点加入集群第 17 章以后记住一节点告警会停全集群写入实验室即使单机也要在 SOP 里写上这句话避免到了多节点有人只重启「红的那台」以为其它节点还能写。当前基础篇单机但口令要先种下。磁盘监控对容器要确认数据目录挂载点不要看 overlay 层剩多少。日志与消息不同盘时日志满不应误判为消息满SOP 分项检查。文件描述符上限与连接泄漏一起看先泄漏再告警是常见顺序list_connections 按 user 排序能发现异常客户端。踢连接前通知踢后观察告警是否下降。下降则根因是连接不是队列深度。这能少一次无意义的 Purge。4. 项目总结优点与缺点机制优点缺点内存/磁盘告警避免硬 OOM留泄洪窗口像「MQ 卡死」不会用的人只会重启绝对水位容器可预期要随 limit 改 Git相对水位物理机省事cgroup 误判连接上限限制泄漏爆炸半径含内部连接调太小误伤优点1故障模式可演练。2发布/消费不对称停。3与 Confirm 超时分桶衔接。缺点1红灯会被当成宕机。2临时 ctl 与 conf 不一致。3无消费者时泄洪无出口。适用场景容器/物理机容量保护。大促值班演练。连接泄漏应急。不适用用告警代替 max-length用重启清除告警当日常在生产拧 0.01 做「压测」。注意事项集群一节点告警发布全局停多节点时。4.x 基线用绝对内存。恢复水位后检查 conf。安全谁能 ctl 改水位要审计改低等于拒绝服务。blocked_connection_timeout与心跳一起配。常见踩坑生产告警时重启节点消费者没拉起重启后立刻再红。根因SOP 第一条是重启。相对水位看宿主机 64G容器 2G永不报警直到 OOM Killer。根因第 3 章没遵守。实验 0.01 写进镜像全环境 blocked。根因临时命令持久化进错误层。思考题为什么告警要在集群范围停写而不是只停告警节点的队列 Leader经典队列与 quorum 答案是否相同消费速度为零时只停发布能否自动解除内存告警还缺哪一步附录 C第 13 章思考题参考答案题 1声明 5000 与 Policy 60000。以effective为准通常按合并规则取策略与参数的组合不要猜谁覆盖谁。用rabbitmqctl list_queues name arguments与 UI effective、以及实际过期时间证明。不同键规则不同测试应打印三处再下结论。题 2Operator 白名单。任意 x-args 会让运维策略变成「开发后门」应用可声明危险参数再靠 Operator 名称伪装成治理。白名单把封顶限制在容量与寿命等运维职责自定义业务头仍归应用。安全模型是职责分离不是功能越多越好。延伸阅读与资源SQLAlchemy 2.0从入门到进阶的实战之旅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 实战修炼与源码剖析