、Docker容器化與集群搭建實(shí)戰(zhàn))
1. 為什么消息隊(duì)列的部署方式值得單獨(dú)拿出來講消息隊(duì)列這東西很多團(tuán)隊(duì)都是用起來再說結(jié)果上線前三天才開始裝環(huán)境然后被各種端口、權(quán)限、網(wǎng)絡(luò)配置折騰到凌晨。RabbitMQ 作為最主流的消息中間件之一部署方式其實(shí)直接決定了后續(xù)的運(yùn)維成本和踩坑概率。我見過太多項(xiàng)目開發(fā)環(huán)境用單機(jī)跑得好好的一上生產(chǎn)集群就各種腦裂、隊(duì)列丟失、連接超時(shí)根子往往不在代碼而在最初部署方案的選擇上。這篇內(nèi)容面向的是需要自己動(dòng)手把 RabbitMQ 跑起來的開發(fā)和運(yùn)維同學(xué)不管你是本地 Windows 想快速驗(yàn)證一個(gè) Demo還是要在服務(wù)器上用 Docker 起一個(gè)干凈實(shí)例又或者是需要搭一套能扛住生產(chǎn)流量的集群這里都會(huì)把每種方式的完整步驟、參數(shù)含義、以及我實(shí)際踩過的坑講清楚。核心關(guān)鍵詞就四個(gè)Windows 部署、單機(jī)模式、Docker 容器化、集群搭建。這四個(gè)場(chǎng)景覆蓋了從個(gè)人開發(fā)到企業(yè)生產(chǎn)的完整鏈路我會(huì)按什么場(chǎng)景選什么方案的邏輯來組織而不是干巴巴地羅列命令。先說一個(gè)基本認(rèn)知RabbitMQ 是基于 Erlang 開發(fā)的這決定了它的部署和普通 Java 應(yīng)用不太一樣。Erlang 有自己的節(jié)點(diǎn)通信機(jī)制集群節(jié)點(diǎn)之間要靠Erlang Cookie來互相認(rèn)證這個(gè) Cookie 文件如果集群內(nèi)不一致節(jié)點(diǎn)根本連不上。另外 RabbitMQ 的集群分兩種模式——普通集群和鏡像集群前者只同步元數(shù)據(jù)不同步消息后者才會(huì)真正復(fù)制隊(duì)列內(nèi)容。很多人搭完集群發(fā)現(xiàn)消息還是會(huì)丟就是因?yàn)闆]搞明白這個(gè)區(qū)別。這些底層邏輯我會(huì)在對(duì)應(yīng)章節(jié)里展開先把四種部署方式的適用場(chǎng)景用一張表說清楚。部署方式適用場(chǎng)景優(yōu)點(diǎn)主要坑點(diǎn)Windows 單機(jī)本地開發(fā)、功能驗(yàn)證圖形化安裝上手快服務(wù)自啟、Erlang 版本匹配Linux 單機(jī)測(cè)試環(huán)境、小型生產(chǎn)資源占用低可控性強(qiáng)依賴包、防火墻、SELinuxDocker 單機(jī)快速交付、環(huán)境隔離一條命令起服務(wù)干凈數(shù)據(jù)持久化、端口映射集群生產(chǎn)高可用負(fù)載均衡、故障轉(zhuǎn)移Cookie 一致、鏡像策略、網(wǎng)絡(luò)分區(qū)選型這件事沒有絕對(duì)的對(duì)錯(cuò)只有匹配不匹配。本地寫代碼驗(yàn)證邏輯Windows 單機(jī)十分鐘搞定要給測(cè)試同學(xué)一個(gè)穩(wěn)定環(huán)境Docker 最省心真到了生產(chǎn)集群幾乎是必選項(xiàng)但集群的復(fù)雜度也是指數(shù)級(jí)上升的。下面我按從簡到繁的順序把每種方式拆開講。2. Windows 單機(jī)部署十分鐘跑起來但有兩個(gè)隱藏細(xì)節(jié)Windows 上裝 RabbitMQ 是很多人的第一次接觸因?yàn)閳D形化安裝確實(shí)友好。但我要先潑一盆冷水RabbitMQ 不能單獨(dú)安裝它依賴 Erlang 運(yùn)行時(shí)而且兩者版本必須匹配。我見過有人裝了最新版 RabbitMQ 配了個(gè)老 Erlang結(jié)果服務(wù)死活起不來日志里全是init terminating in do_boot這種讓人頭大的報(bào)錯(cuò)。2.1 Erlang 與 RabbitMQ 的版本對(duì)應(yīng)關(guān)系在動(dòng)手之前先去 RabbitMQ 官方文檔查一下版本兼容表。這個(gè)表會(huì)明確告訴你哪個(gè) RabbitMQ 版本對(duì)應(yīng)哪個(gè) Erlang 版本范圍。比如 RabbitMQ 3.12.x 通常要求 Erlang 25.x 或 26.x而 3.13.x 可能要求 26.x 以上。不要憑感覺裝最新版最新版 RabbitMQ 配最新版 Erlang 有時(shí)候反而會(huì)因?yàn)閯偘l(fā)布還沒磨合好而出問題選一個(gè)穩(wěn)定組合更靠譜。安裝 Erlang 的時(shí)候有個(gè)細(xì)節(jié)安裝路徑不要帶空格和中文。默認(rèn)路徑C:\Program Files\erl-26.0里的空格在某些腳本調(diào)用時(shí)會(huì)出問題我一般會(huì)改成C:\erl26這種干凈路徑。安裝完成后需要把 Erlang 的bin目錄加到系統(tǒng)環(huán)境變量PATH里然后在命令行敲erl -version驗(yàn)證能輸出版本號(hào)才算成功。2.2 安裝 RabbitMQ 與開啟管理插件RabbitMQ 的 Windows 安裝包是個(gè) exe雙擊一路下一步就行。裝完之后它默認(rèn)會(huì)注冊(cè)成 Windows 服務(wù)并自動(dòng)啟動(dòng)。但這時(shí)候你只能通過命令行操作要看圖形界面還得手動(dòng)開啟管理插件。打開命令行切到 RabbitMQ 的 sbin 目錄執(zhí)行rabbitmq-plugins enable rabbitmq_management這條命令會(huì)啟用rabbitmq_management插件它內(nèi)置了一個(gè) Web 管理界面默認(rèn)監(jiān)聽15672端口。啟用后重啟一下服務(wù)瀏覽器訪問http://localhost:15672用默認(rèn)賬號(hào)guest/guest就能登錄。這里有個(gè)安全限制要注意guest 賬號(hào)默認(rèn)只允許本機(jī)登錄遠(yuǎn)程訪問會(huì)被拒絕。這是 RabbitMQ 的安全設(shè)計(jì)生產(chǎn)環(huán)境一定要新建獨(dú)立賬號(hào)并改掉 guest 密碼。2.3 服務(wù)自啟與日志排查Windows 服務(wù)默認(rèn)是自動(dòng)啟動(dòng)的但如果你改了配置或者插件最好手動(dòng)重啟一次。重啟命令是net stop RabbitMQ net start RabbitMQ如果服務(wù)啟動(dòng)失敗別急著重裝先去看日志。日志位置一般在%APPDATA%\RabbitMQ\log目錄下里面會(huì)有詳細(xì)的啟動(dòng)過程記錄。常見的啟動(dòng)失敗原因有三個(gè)Erlang 版本不匹配、端口被占用5672 是 AMQP 端口15672 是管理端口、Cookie 文件權(quán)限問題。我遇到過一次是 5672 端口被另一個(gè)服務(wù)占了日志里會(huì)明確寫eaddrinuse換個(gè)端口或者關(guān)掉占用進(jìn)程就行。提示W(wǎng)indows 單機(jī)部署適合本地開發(fā)驗(yàn)證但不要用它做生產(chǎn)環(huán)境。Windows 版的 RabbitMQ 在性能和穩(wěn)定性上都不如 Linux而且很多集群特性在 Windows 上支持有限。3. Docker 部署一條命令背后的持久化與網(wǎng)絡(luò)考量Docker 部署 RabbitMQ 是我現(xiàn)在最推薦的方式尤其是給團(tuán)隊(duì)搭測(cè)試環(huán)境。它的好處是環(huán)境干凈、可復(fù)制、刪了重來毫無心理負(fù)擔(dān)。但一條命令起服務(wù)的背后有幾個(gè)參數(shù)如果沒配對(duì)數(shù)據(jù)丟了都不知道怎么丟的。3.1 拉取鏡像與選擇版本標(biāo)簽先拉鏡像但別直接docker pull rabbitmq那樣拉的是latest標(biāo)簽版本不確定。生產(chǎn)或測(cè)試環(huán)境一定要指定版本比如docker pull rabbitmq:3.13-management注意-management這個(gè)后綴它表示鏡像里已經(jīng)預(yù)裝了管理插件省得你進(jìn)去手動(dòng) enable。如果不帶這個(gè)后綴起出來的容器是沒有 Web 界面的還得自己進(jìn)容器執(zhí)行插件命令。版本號(hào)我建議選一個(gè)穩(wěn)定的次新版本太新的可能文檔還沒跟上太舊的又缺安全補(bǔ)丁。3.2 啟動(dòng)命令的每個(gè)參數(shù)都要有理由一條完整的啟動(dòng)命令大概長這樣docker run -d \ --name rabbitmq \ --hostname my-rabbit \ -p 5672:5672 \ -p 15672:15672 \ -e RABBITMQ_DEFAULT_USERadmin \ -e RABBITMQ_DEFAULT_PASSadmin123 \ -v /data/rabbitmq:/var/lib/rabbitmq \ --restartalways \ rabbitmq:3.13-management我逐個(gè)解釋為什么這么寫。--hostname很重要RabbitMQ 用主機(jī)名來標(biāo)識(shí)節(jié)點(diǎn)如果不指定Docker 會(huì)隨機(jī)生成一個(gè)容器 ID 作為主機(jī)名重啟后主機(jī)名變了節(jié)點(diǎn)數(shù)據(jù)可能就對(duì)不上了。-p映射兩個(gè)端口5672 是程序連接用的15672 是管理界面用的。-e設(shè)置默認(rèn)賬號(hào)密碼一定要改掉默認(rèn)的 guest否則等于把門敞開。-v做數(shù)據(jù)持久化把容器內(nèi)的/var/lib/rabbitmq掛到宿主機(jī)這樣容器刪了數(shù)據(jù)還在。--restartalways保證宿主機(jī)重啟后容器自動(dòng)拉起。3.3 數(shù)據(jù)持久化的坑掛載目錄權(quán)限掛載目錄這一步最容易出問題。如果你在宿主機(jī)上手動(dòng)創(chuàng)建了/data/rabbitmq目錄它的屬主是 root而容器內(nèi) RabbitMQ 進(jìn)程是以某個(gè)非 root 用戶運(yùn)行的結(jié)果就是容器啟動(dòng)時(shí)報(bào)權(quán)限拒絕或者數(shù)據(jù)寫不進(jìn)去。解決辦法有兩個(gè)一是啟動(dòng)前把目錄權(quán)限放開二是讓 Docker 自動(dòng)創(chuàng)建目錄不手動(dòng) mkdir。我一般用后者省事。如果非要手動(dòng)創(chuàng)建記得chmod 777或者改成容器內(nèi)對(duì)應(yīng)的 UID。另外Erlang Cookie 文件也在/var/lib/rabbitmq下持久化之后集群部署時(shí)可以直接復(fù)用這個(gè) Cookie省得再手動(dòng)同步。3.4 驗(yàn)證容器狀態(tài)與常見啟動(dòng)失敗啟動(dòng)后用docker ps看容器狀態(tài)如果是Up就說明進(jìn)程活著。但進(jìn)程活著不代表服務(wù)可用還得看日志docker logs -f rabbitmq日志里出現(xiàn)Server startup complete才算真正就緒。常見的失敗情況我整理成表現(xiàn)象原因解決容器反復(fù)重啟內(nèi)存不足或配置錯(cuò)誤看日志調(diào)大內(nèi)存限制管理界面打不開15672 端口沒映射或插件沒啟檢查 -p 參數(shù)和鏡像標(biāo)簽程序連不上5672 端口被占或賬號(hào)錯(cuò)誤換端口核對(duì)賬號(hào)密碼數(shù)據(jù)丟失沒做 volume 掛載加 -v 參數(shù)重新起Docker 方式跑單機(jī)非常舒服但要注意它本質(zhì)上還是單節(jié)點(diǎn)沒有高可用。如果你的業(yè)務(wù)不能接受消息隊(duì)列宕機(jī)那就得往下看集群方案了。4. 集群搭建普通集群和鏡像集群差在哪集群是 RabbitMQ 部署里最復(fù)雜也最容易出問題的部分。很多人以為搭了集群就高枕無憂了結(jié)果一臺(tái)機(jī)器掛了消息全丟原因就是沒搞清楚普通集群和鏡像集群的本質(zhì)區(qū)別。這一節(jié)我會(huì)把集群的原理講透再給出可操作的搭建步驟。4.1 集群的元數(shù)據(jù)同步機(jī)制RabbitMQ 集群里所有節(jié)點(diǎn)會(huì)同步四類東西交換機(jī)、隊(duì)列的元數(shù)據(jù)、綁定關(guān)系、以及 vhost。注意隊(duì)列的元數(shù)據(jù)同步了但隊(duì)列里的消息默認(rèn)不同步。這意味著什么假設(shè)你有節(jié)點(diǎn) A 和節(jié)點(diǎn) B一個(gè)隊(duì)列建在 A 上你通過 B 去消費(fèi)這個(gè)隊(duì)列B 知道這個(gè)隊(duì)列存在元數(shù)據(jù)同步了但消息實(shí)際存在 A 的磁盤或內(nèi)存里B 會(huì)把請(qǐng)求轉(zhuǎn)發(fā)給 A。如果 A 掛了這個(gè)隊(duì)列的消息就訪問不到了直到 A 恢復(fù)。這就是普通集群的局限它解決了元數(shù)據(jù)的一致性和連接的負(fù)載均衡但沒有解決消息的高可用。要讓消息也具備高可用就得用鏡像隊(duì)列Mirrored Queue把隊(duì)列內(nèi)容復(fù)制到多個(gè)節(jié)點(diǎn)上。鏡像隊(duì)列通過策略Policy來配置可以指定鏡像到幾個(gè)節(jié)點(diǎn)、是否自動(dòng)同步等。4.2 Erlang Cookie 必須完全一致集群節(jié)點(diǎn)之間靠 Erlang Cookie 認(rèn)證這個(gè)文件通常叫.erlang.cookie位置在用戶主目錄下。集群內(nèi)所有節(jié)點(diǎn)的 Cookie 內(nèi)容必須一模一樣否則節(jié)點(diǎn)握手直接失敗日志里會(huì)報(bào)unable to connect to epmd或者cookie mismatch。搭建時(shí)最穩(wěn)妥的做法是先在一個(gè)節(jié)點(diǎn)上找到 Cookie 文件把它復(fù)制到其他所有節(jié)點(diǎn)并確保文件權(quán)限是 400只有屬主可讀。在 Docker 環(huán)境下Cookie 文件在/var/lib/rabbitmq/.erlang.cookie如果你做了 volume 掛載可以直接把第一個(gè)節(jié)點(diǎn)的 Cookie 拷到其他節(jié)點(diǎn)的掛載目錄里。4.3 用 Docker 搭一個(gè)三節(jié)點(diǎn)集群下面是我實(shí)際用過的三節(jié)點(diǎn)集群搭建流程基于 Docker 網(wǎng)絡(luò)。先創(chuàng)建一個(gè)自定義網(wǎng)絡(luò)讓容器之間能通過主機(jī)名互相訪問docker network create rabbitmq-net然后依次啟動(dòng)三個(gè)節(jié)點(diǎn)注意每個(gè)節(jié)點(diǎn)的 hostname 要不同并且都加入同一個(gè)網(wǎng)絡(luò)docker run -d --name rabbit1 --hostname rabbit1 --network rabbitmq-net \ -p 5672:5672 -p 15672:15672 \ -e RABBITMQ_ERLANG_COOKIEmysecretcookie \ rabbitmq:3.13-management docker run -d --name rabbit2 --hostname rabbit2 --network rabbitmq-net \ -e RABBITMQ_ERLANG_COOKIEmysecretcookie \ rabbitmq:3.13-management docker run -d --name rabbit3 --hostname rabbit3 --network rabbitmq-net \ -e RABBITMQ_ERLANG_COOKIEmysecretcookie \ rabbitmq:3.13-management這里用RABBITMQ_ERLANG_COOKIE環(huán)境變量統(tǒng)一設(shè)置了 Cookie省去手動(dòng)拷貝的麻煩。三個(gè)節(jié)點(diǎn)起來后進(jìn)入 rabbit2 和 rabbit3分別執(zhí)行加入集群的命令# 在 rabbit2 容器內(nèi)執(zhí)行 rabbitmqctl stop_app rabbitmqctl reset rabbitmqctl join_cluster rabbitrabbit1 rabbitmqctl start_appstop_app是停掉 RabbitMQ 應(yīng)用但保留 Erlang 節(jié)點(diǎn)reset是清空當(dāng)前節(jié)點(diǎn)的元數(shù)據(jù)新節(jié)點(diǎn)必須做join_cluster指定要加入的集群節(jié)點(diǎn)start_app重新啟動(dòng)應(yīng)用。rabbit3 同理只是 join 的目標(biāo)還是 rabbit1。全部執(zhí)行完后在任意節(jié)點(diǎn)執(zhí)行rabbitmqctl cluster_status能看到三個(gè)節(jié)點(diǎn)都列在 running_nodes 里就說明集群搭好了。4.4 鏡像隊(duì)列策略配置與驗(yàn)證集群搭好只是第一步默認(rèn)還是普通集群模式。要開啟鏡像需要設(shè)置策略。在管理界面或者命令行都可以命令行方式rabbitmqctl set_policy ha-all ^ {ha-mode:all} --apply-to queues這條命令的意思是對(duì)所有隊(duì)列^是正則匹配全部應(yīng)用ha-all策略ha-mode為all表示鏡像到所有節(jié)點(diǎn)。設(shè)置完之后新建的隊(duì)列會(huì)自動(dòng)鏡像。驗(yàn)證方法是創(chuàng)建一個(gè)隊(duì)列然后在管理界面的隊(duì)列詳情里看如果 Nodes 列顯示了多個(gè)節(jié)點(diǎn)就說明鏡像生效了。注意ha-mode: all在節(jié)點(diǎn)數(shù)量多的時(shí)候會(huì)帶來較大的同步開銷生產(chǎn)環(huán)境更推薦用ha-mode: exactly配合ha-params指定鏡像到 2 個(gè)節(jié)點(diǎn)兼顧可用性和性能。4.5 集群的網(wǎng)絡(luò)分區(qū)與恢復(fù)集群最怕的是網(wǎng)絡(luò)分區(qū)Network Partition也就是節(jié)點(diǎn)之間網(wǎng)絡(luò)斷了各自以為對(duì)方掛了。RabbitMQ 默認(rèn)的處理策略是ignore即不自動(dòng)處理需要人工介入。分區(qū)發(fā)生后管理界面會(huì)顯示節(jié)點(diǎn)狀態(tài)異常這時(shí)候要手動(dòng)決定保留哪個(gè)分區(qū)的數(shù)據(jù)然后重啟其他節(jié)點(diǎn)重新加入。預(yù)防網(wǎng)絡(luò)分區(qū)一方面要保證集群節(jié)點(diǎn)之間的網(wǎng)絡(luò)質(zhì)量另一方面可以配置pause_minority策略讓少數(shù)派節(jié)點(diǎn)自動(dòng)暫停避免腦裂。這個(gè)策略在/etc/rabbitmq/rabbitmq.conf里配置cluster_partition_handling pause_minority改完配置需要重啟節(jié)點(diǎn)。這個(gè)參數(shù)的選擇要根據(jù)你的網(wǎng)絡(luò)環(huán)境來網(wǎng)絡(luò)穩(wěn)定就用默認(rèn)網(wǎng)絡(luò)抖動(dòng)頻繁就考慮 pause_minority。5. 四種部署方式的選擇邏輯與遷移路徑講完四種方式最后聊聊怎么選以及從一種方式遷移到另一種時(shí)要注意什么。這部分是我這些年做架構(gòu)選型時(shí)總結(jié)的經(jīng)驗(yàn)不是教科書上的標(biāo)準(zhǔn)答案但應(yīng)該能幫你少走彎路。5.1 按團(tuán)隊(duì)規(guī)模和業(yè)務(wù)階段選型個(gè)人開發(fā)或者小團(tuán)隊(duì)做原型驗(yàn)證Windows 單機(jī)或者 Docker 單機(jī)足夠了別一上來就搞集群那是給自己找麻煩。集群的運(yùn)維成本不低節(jié)點(diǎn)監(jiān)控、Cookie 管理、鏡像策略調(diào)優(yōu)每一項(xiàng)都要花時(shí)間。等到業(yè)務(wù)真的需要高可用比如消息不能丟、服務(wù)不能停再上集群也不遲。中型團(tuán)隊(duì)給測(cè)試環(huán)境用我強(qiáng)烈推薦 Docker 單機(jī)加持久化。一條命令起服務(wù)環(huán)境干凈出問題刪了重來比在物理機(jī)上折騰依賴省心太多。而且 Docker 的鏡像可以版本化今天用 3.13明天想試 3.12改個(gè)標(biāo)簽就行。生產(chǎn)環(huán)境如果預(yù)算允許直接上三節(jié)點(diǎn)鏡像集群。三個(gè)節(jié)點(diǎn)是性價(jià)比最高的配置能容忍一個(gè)節(jié)點(diǎn)故障鏡像開銷也可控。節(jié)點(diǎn)數(shù)再多同步成本上升收益遞減。5.2 從單機(jī)遷移到集群的注意事項(xiàng)如果你現(xiàn)在跑的是單機(jī)想遷移到集群有幾件事必須提前做。第一備份現(xiàn)有數(shù)據(jù)包括隊(duì)列、交換機(jī)、綁定關(guān)系和消息。RabbitMQ 提供了rabbitmqadmin工具可以導(dǎo)出定義但消息本身需要靠消費(fèi)或者底層數(shù)據(jù)文件遷移。第二規(guī)劃好 vhost 和用戶權(quán)限集群環(huán)境下權(quán)限管理更復(fù)雜最好提前統(tǒng)一。第三測(cè)試鏡像策略對(duì)性能的影響鏡像會(huì)帶來額外的網(wǎng)絡(luò)和磁盤開銷上線前壓測(cè)一下。遷移過程中建議先用新集群跑一段時(shí)間雙寫確認(rèn)沒問題再切流量。直接切風(fēng)險(xiǎn)太大萬一集群配置有問題回滾都來不及。5.3 幾個(gè)通用的運(yùn)維經(jīng)驗(yàn)不管用哪種部署方式有幾個(gè)經(jīng)驗(yàn)是通用的。監(jiān)控一定要做RabbitMQ 暴露了很多指標(biāo)隊(duì)列長度、消息速率、連接數(shù)、內(nèi)存使用這些都要盯著。隊(duì)列積壓往往是消費(fèi)能力不足或者消費(fèi)者掛了的信號(hào)。日志要集中管理單機(jī)看本地日志還行集群環(huán)境必須把日志收集起來統(tǒng)一分析否則排查問題要登好幾臺(tái)機(jī)器。定期演練故障恢復(fù)別等真出事了才發(fā)現(xiàn)恢復(fù)流程走不通平時(shí)找個(gè)低峰期手動(dòng)停一個(gè)節(jié)點(diǎn)看看集群能不能正常切換。還有一個(gè)容易被忽略的點(diǎn)Erlang Cookie 的備份。集群的 Cookie 如果丟了節(jié)點(diǎn)就加不回去了。我一般會(huì)把 Cookie 文件單獨(dú)存一份到安全的地方跟數(shù)據(jù)庫密碼一個(gè)級(jí)別對(duì)待。6. 我踩過的幾個(gè)真實(shí)坑與排查思路最后分享幾個(gè)我在實(shí)際部署中踩過的坑都是那種文檔里不會(huì)寫、但遇到了能折騰你半天的。每個(gè)坑我都會(huì)給出排查思路你可以照著復(fù)現(xiàn)。6.1 容器重啟后節(jié)點(diǎn)名變化導(dǎo)致集群異常有一次用 Docker 搭集群容器重啟后發(fā)現(xiàn)節(jié)點(diǎn)加不回集群了。排查了半天發(fā)現(xiàn)是容器重啟后 hostname 變了。Docker 容器如果不指定--hostname每次重啟都會(huì)生成新的隨機(jī)主機(jī)名而 RabbitMQ 的節(jié)點(diǎn)名是基于主機(jī)名的節(jié)點(diǎn)名一變集群里記錄的舊節(jié)點(diǎn)就找不到了。解決辦法就是啟動(dòng)時(shí)固定--hostname這個(gè)參數(shù)在集群環(huán)境里是必須的單機(jī)也建議加上避免不必要的麻煩。6.2 磁盤水位告警導(dǎo)致生產(chǎn)者被阻塞RabbitMQ 有個(gè)磁盤空閑空間檢查機(jī)制默認(rèn)當(dāng)磁盤剩余空間低于 50MB 時(shí)會(huì)阻塞所有生產(chǎn)者防止磁盤寫滿導(dǎo)致崩潰。這個(gè)機(jī)制本身是保護(hù)性的但如果你沒注意到會(huì)以為是程序出了問題。表現(xiàn)是生產(chǎn)者發(fā)送消息一直卡住日志里會(huì)有disk_free_limit相關(guān)的告警。解決辦法要么清理磁盤要么調(diào)整disk_free_limit參數(shù)。我一般會(huì)把閾值設(shè)成固定值比如 1GB而不是用默認(rèn)的相對(duì)值這樣更可控。6.3 內(nèi)存高水位觸發(fā)流控跟磁盤類似RabbitMQ 還有內(nèi)存高水位機(jī)制。當(dāng)內(nèi)存使用超過總內(nèi)存的 40%默認(rèn)值時(shí)會(huì)觸發(fā)流控阻塞生產(chǎn)者。這個(gè)在容器環(huán)境里特別容易觸發(fā)因?yàn)槿萜骺吹降膬?nèi)存可能跟宿主機(jī)不一樣。如果容器沒設(shè)置內(nèi)存限制RabbitMQ 可能讀到的是宿主機(jī)的總內(nèi)存算出來的水位線就不對(duì)。解決辦法是在容器啟動(dòng)時(shí)用--memory限制內(nèi)存或者顯式配置vm_memory_high_watermark參數(shù)。6.4 排查問題的通用思路遇到 RabbitMQ 問題我的排查順序是先看服務(wù)狀態(tài)rabbitmqctl status再看日志docker logs或本地日志文件然后看管理界面的 Overview 和 Connections 頁面最后才是翻代碼。大部分問題在日志里都有明確提示關(guān)鍵是你要知道去哪個(gè)目錄找日志、日志里的關(guān)鍵詞是什么意思。我建議把常見的錯(cuò)誤關(guān)鍵詞整理成一個(gè)速查表比如eaddrinuse是端口占用cookie mismatch是 Cookie 不一致disk_free_limit是磁盤告警memory_high_watermark是內(nèi)存告警。有了這個(gè)表排查效率能提升一大截。部署這件事說到底就是細(xì)節(jié)的堆砌。每一個(gè)參數(shù)背后都有它的設(shè)計(jì)意圖理解了意圖你才知道什么時(shí)候該改、改成什么。希望這篇內(nèi)容能幫你在下次部署 RabbitMQ 的時(shí)候少熬一個(gè)夜。