 Kafka 啟動(dòng)配置與主題收發(fā)排錯(cuò)指南)
1. 先想清楚Windows 上跑 Kafka 到底難在哪本地開(kāi)發(fā)要驗(yàn)一段消息鏈路最省事的做法就是在自己的 Windows 機(jī)器上把一個(gè)單節(jié)點(diǎn) Kafka 拉起來(lái)。聽(tīng)起來(lái)簡(jiǎn)單解壓、改配置、雙擊啟動(dòng)三步的事。但我見(jiàn)過(guò)太多人在這一步上耗掉大半天命令行窗口一閃就沒(méi)了、提示找不到主類(lèi)、topic 建好了卻看不到、生產(chǎn)者能發(fā)消費(fèi)者收不到。這些問(wèn)題單看都很小湊在一起就足夠勸退。這篇內(nèi)容就是把這些零碎的東西一次性講透從選哪個(gè)版本、為什么這么選、配置文件里哪幾行必須改、啟動(dòng)腳本怎么調(diào)、topic 建幾個(gè)分區(qū)、副本數(shù)給多少、出錯(cuò)以后按什么順序排查。適合三類(lèi)人看第一類(lèi)是剛接觸消息隊(duì)列、需要在本地跑通一個(gè)完整收發(fā)鏈路的開(kāi)發(fā)者第二類(lèi)是后端或者數(shù)據(jù)側(cè)的同學(xué)項(xiàng)目里要用 Kafka 做解耦和緩沖想先在本地把環(huán)境摸熟再上測(cè)試環(huán)境第三類(lèi)是已經(jīng)用過(guò) Kafka 但一直在 Linux 上操作、突然換到 Windows 發(fā)現(xiàn)處處別扭的人。我會(huì)把每個(gè)參數(shù)背后的理由說(shuō)清楚不只是告訴你填什么還要告訴你為什么這么填、填錯(cuò)了會(huì)怎樣。全文基于單機(jī)開(kāi)發(fā)場(chǎng)景不涉及集群部署和運(yùn)維體系但原理是相通的后面往多節(jié)點(diǎn)擴(kuò)的時(shí)候不用推翻重來(lái)。1.1 為什么大多數(shù)人第一次在 Windows 啟動(dòng) Kafka 都會(huì)卡住Kafka 本質(zhì)上是個(gè) JVM 應(yīng)用它在任何平臺(tái)上跑起來(lái)的核心邏輯都一樣讀配置、初始化日志存儲(chǔ)目錄、綁定端口、注冊(cè)到協(xié)調(diào)服務(wù)。Windows 上讓人崩潰的地方不在 Kafka 本身而在于三件事。第一件事是腳本形態(tài)。Kafka 發(fā)行包里給 Linux/macOS 準(zhǔn)備的是.sh腳本W(wǎng)indows 用的是bin\windows目錄下那一批.bat。很多人直接在根目錄找kafka-server-start找不到就以為包下錯(cuò)了。更麻煩的是部分較新的發(fā)行版對(duì) Windows 腳本的維護(hù)力度在下降某些版本里bin\windows目錄干脆不存在這時(shí)候你要么退回一個(gè)還帶 Windows 腳本的版本要么在一個(gè) Linux 環(huán)境里跑。搞清楚自己下的包里到底有沒(méi)有這個(gè)目錄是動(dòng)手前的第一件事。第二件事是運(yùn)行模式的變化。早期的 Kafka 強(qiáng)依賴(lài) ZooKeeper必須先起 ZooKeeper 再起 Kafka少一步就報(bào)連接超時(shí)。后來(lái)的版本引入了不需要外部協(xié)調(diào)服務(wù)的模式把元數(shù)據(jù)管理內(nèi)置了啟動(dòng)流程變成了「先生成集群 ID、格式化存儲(chǔ)目錄、再啟動(dòng)」。兩套流程的命令完全不同網(wǎng)上搜到的教程一半是舊的一半是新的照著抄必然互相打架。第三件事是 Windows 的文件系統(tǒng)和終端習(xí)慣。.bat腳本里有大量相對(duì)路徑和%CLASSPATH%拼接路徑里帶空格、帶中文、帶括號(hào)都可能讓腳本解析出錯(cuò)關(guān)閉命令行窗口時(shí)如果直接把窗口叉掉JVM 是被強(qiáng)殺的下次啟動(dòng)就可能遇到日志段沒(méi)正常關(guān)閉的告警。這些坑在 Linux 上一個(gè)nohup加就繞過(guò)去了在 Windows 上得單獨(dú)想辦法。提示動(dòng)手前先確認(rèn)bin\windows目錄是否存在這一步能省掉后面半小時(shí)的無(wú)效搜索。1.2 版本與運(yùn)行模式的選擇邏輯選版本這件事我的建議是以「是否自帶 Windows 啟動(dòng)腳本」和「是否需要額外起 ZooKeeper」兩個(gè)維度來(lái)定。如果你的機(jī)器上只是想快速跑通一個(gè)單機(jī)環(huán)境優(yōu)先選那種元數(shù)據(jù)內(nèi)置、不需要額外起協(xié)調(diào)服務(wù)的版本。這種模式的好處很直接——只起一個(gè)進(jìn)程端口只占一個(gè)關(guān)的時(shí)候也只關(guān)一個(gè)排查問(wèn)題時(shí)變量少一半。代價(jià)是首次啟動(dòng)前必須做一次存儲(chǔ)目錄格式化而且格式化用的集群 ID 一旦變了原來(lái)那個(gè)日志目錄里的數(shù)據(jù)就讀不出來(lái)了等于清庫(kù)。這個(gè)代價(jià)對(duì)本地開(kāi)發(fā)完全可接受反正數(shù)據(jù)隨時(shí)可以重建。如果你手上的項(xiàng)目還在用老版本或者團(tuán)隊(duì)里的測(cè)試環(huán)境是依賴(lài)外部協(xié)調(diào)服務(wù)的那一套那就老老實(shí)實(shí)按兩段式啟動(dòng)來(lái)先起協(xié)調(diào)服務(wù)再起 Kafka。這么做的好處是和你將來(lái)要對(duì)接的環(huán)境保持一致少一層「本地能跑、測(cè)試環(huán)境起不來(lái)」的意外。對(duì)比維度元數(shù)據(jù)內(nèi)置模式依賴(lài)外部協(xié)調(diào)服務(wù)模式啟動(dòng)進(jìn)程數(shù)1 個(gè)2 個(gè)先協(xié)調(diào)服務(wù)后 Kafka首次啟動(dòng)前置動(dòng)作必須格式化存儲(chǔ)目錄無(wú)直接起端口占用只需一個(gè)對(duì)外端口協(xié)調(diào)服務(wù)端口 Kafka 端口數(shù)據(jù)可遷移性與集群 ID 強(qiáng)綁定換 ID 即失效依賴(lài)協(xié)調(diào)服務(wù)中的元數(shù)據(jù)適合場(chǎng)景本地開(kāi)發(fā)、單機(jī)驗(yàn)證與存量環(huán)境保持一致常見(jiàn)坑忘記格式化、重復(fù)格式化丟數(shù)據(jù)協(xié)調(diào)服務(wù)沒(méi)起就起 Kafka連接超時(shí)我自己的習(xí)慣是本地一律用元數(shù)據(jù)內(nèi)置模式因?yàn)榭斓灰@個(gè)環(huán)境將來(lái)要給別人復(fù)現(xiàn)就把啟動(dòng)命令連同版本號(hào)一起寫(xiě)進(jìn)一份說(shuō)明文件里別指望三個(gè)月后自己還記得當(dāng)時(shí)用的是什么模式。1.3 目錄與磁盤(pán)規(guī)劃別等日志寫(xiě)滿(mǎn)了才后悔Kafka 的數(shù)據(jù)目錄日志目錄是它真正放消息的地方默認(rèn)配置往往指向一個(gè)相對(duì)路徑或者系統(tǒng)臨時(shí)目錄。這在開(kāi)發(fā)機(jī)上會(huì)帶來(lái)兩個(gè)后患一是重裝或者清理臨時(shí)文件時(shí)數(shù)據(jù)莫名其妙沒(méi)了二是系統(tǒng)盤(pán)被撐爆機(jī)器整體變卡你還以為是 Kafka 內(nèi)存泄漏。我的做法是固定一個(gè)獨(dú)立目錄比如D:\dev\kafka-data并且和安裝目錄分開(kāi)。安裝目錄可能因?yàn)閾Q版本被整個(gè)替換數(shù)據(jù)目錄不能跟著一起沒(méi)。目錄名全用英文小寫(xiě)加連字符不要有空格、中文、括號(hào)、這類(lèi)字符。原因很實(shí)在.bat腳本在拼接路徑時(shí)對(duì)空格的處理很粗糙一旦路徑里有空格很多時(shí)候不是報(bào)個(gè)明確的錯(cuò)而是給你一個(gè)莫名其妙的「找不到類(lèi)」或者「無(wú)法加載主類(lèi)」。另外要留意 Windows 的路徑長(zhǎng)度限制。Kafka 的日志目錄下會(huì)生成「主題名-分區(qū)號(hào)」這樣的子目錄主題名本身可能就不短再加上時(shí)間戳命名的日志段文件整條路徑很容易突破傳統(tǒng)長(zhǎng)度上限。規(guī)避辦法很簡(jiǎn)單把數(shù)據(jù)目錄放在盤(pán)的根附近層級(jí)壓到兩級(jí)以?xún)?nèi)主題名控制在 30 個(gè)字符以?xún)?nèi)。磁盤(pán)空間也得有個(gè)心理預(yù)期。開(kāi)發(fā)機(jī)上調(diào)小保留時(shí)間比如只留 24 小時(shí)或者按大小限制到 1GB 到 2GB別用默認(rèn)的一周。我踩過(guò)一次壓測(cè)腳本忘了關(guān)第二天早上發(fā)現(xiàn)磁盤(pán)只剩幾百兆系統(tǒng)開(kāi)始各種異常清完數(shù)據(jù)目錄才恢復(fù)。從那以后我本地配置里都會(huì)顯式寫(xiě)上保留策略這算是用代價(jià)換來(lái)的習(xí)慣。2. 環(huán)境準(zhǔn)備與配置文件逐項(xiàng)拆解2.1 JDK 與目錄結(jié)構(gòu)先把地基鋪平Kafka 跑在 JVM 上所以第一步是確認(rèn) Java 可用。在命令行里執(zhí)行java -version能打印出版本號(hào)就說(shuō)明環(huán)境變量沒(méi)問(wèn)題。這里有個(gè)容易被忽略的點(diǎn)Windows 上經(jīng)常同時(shí)裝了好幾個(gè) JDK命令行里java指向的那個(gè)和JAVA_HOME指向的可能不是同一個(gè)。啟動(dòng)腳本通常優(yōu)先讀JAVA_HOME所以你如果發(fā)現(xiàn)「明明 java 命令能用但腳本就是起不來(lái)」八成是這個(gè)不一致導(dǎo)致的。我的處理方式是先把JAVA_HOME打印出來(lái)看一眼再確認(rèn)%JAVA_HOME%\bin\java.exe這個(gè)文件真實(shí)存在。兩個(gè)都對(duì)上了再往下走。版本方面用發(fā)行方長(zhǎng)期支持的版本就行不必追求最新Kafka 對(duì) JDK 版本有明確的下限要求太老的 JDK 會(huì)在啟動(dòng)時(shí)直接拋不支持的類(lèi)版本錯(cuò)誤。目錄規(guī)劃我一般這樣組織D:\dev\ ├─ kafka\ 安裝目錄解壓即用 │ ├─ bin\windows\ 啟動(dòng)腳本都在這里 │ ├─ config\ 配置文件 │ ├─ libs\ 依賴(lài) jar │ └─ logs\ 服務(wù)端運(yùn)行日志不是消息數(shù)據(jù) └─ kafka-data\ 消息數(shù)據(jù)目錄單獨(dú)放 └─ kraft-logs\注意kafka\logs和kafka-data是兩個(gè)完全不同的東西。前者是 Kafka 服務(wù)自身打的應(yīng)用日志用來(lái)排查啟動(dòng)失敗、連接拒絕這類(lèi)問(wèn)題后者才是消息真正落盤(pán)的地方。很多人把這兩個(gè)搞混一看數(shù)據(jù)目錄里全是文本文件就以為消息沒(méi)存進(jìn)去其實(shí)消息在另一套以數(shù)字和.log結(jié)尾的段文件里。解壓的時(shí)候還有一個(gè)細(xì)節(jié)不要用那種需要在系統(tǒng)里注冊(cè)的壓縮工具去解用系統(tǒng)自帶的或者命令行解壓避免解出來(lái)的文件帶上了奇怪的權(quán)限標(biāo)記或者被改動(dòng)過(guò)的換行符。.bat腳本對(duì)換行符不敏感但配置文件如果被轉(zhuǎn)成別的編碼讀到中文注釋就會(huì)解析異常。所以我把所有配置文件統(tǒng)一保持 UTF-8 無(wú) BOM改配置用支持編碼選擇的編輯器別用記事本。2.2 server.properties 關(guān)鍵參數(shù)逐個(gè)說(shuō)透配置文件是整個(gè)啟動(dòng)過(guò)程的核心我把必須理解的幾組參數(shù)拆開(kāi)說(shuō)。下面這些是單機(jī)開(kāi)發(fā)場(chǎng)景下的典型配置字段名以發(fā)行包里的實(shí)際文件為準(zhǔn)不同版本可能略有增刪。# 節(jié)點(diǎn)標(biāo)識(shí)單機(jī)隨便給個(gè)不重復(fù)的數(shù)字 node.id1 # 對(duì)外監(jiān)聽(tīng)地址前面的名字是監(jiān)聽(tīng)器名稱(chēng) listenersPLAINTEXT://:9092 # 客戶(hù)端真正連的地址本地開(kāi)發(fā)寫(xiě) 127.0.0.1 最穩(wěn) advertised.listenersPLAINTEXT://127.0.0.1:9092 # 消息數(shù)據(jù)目錄指向獨(dú)立盤(pán) log.dirsD:/dev/kafka-data/kraft-logs # 關(guān)閉自動(dòng)建主題逼自己顯式創(chuàng)建 auto.create.topics.enablefalse # 新建主題的默認(rèn)分區(qū)數(shù)和副本數(shù) num.partitions3 default.replication.factor1 # 內(nèi)部主題的副本數(shù)單機(jī)必須是 1 offsets.topic.replication.factor1 transaction.state.log.replication.factor1 transaction.state.log.min.isr1 # 保留策略本地開(kāi)發(fā)調(diào)小 log.retention.hours24 log.segment.bytes268435456先說(shuō)listeners和advertised.listeners這一對(duì)這是最容易出錯(cuò)的地方。listeners決定 Kafka 在哪個(gè)網(wǎng)卡和端口上等客戶(hù)端連接寫(xiě)成PLAINTEXT://:9092表示監(jiān)聽(tīng)本機(jī)所有網(wǎng)卡的 9092 端口。advertised.listeners決定 Kafka 告訴客戶(hù)端「你應(yīng)該連我哪個(gè)地址」。這兩者的區(qū)別在于客戶(hù)端第一次連上之后服務(wù)端會(huì)返回一批元數(shù)據(jù)里面寫(xiě)的就是advertised.listeners里的地址客戶(hù)端后續(xù)按這個(gè)地址去拉數(shù)據(jù)。如果這里填的是一個(gè)客戶(hù)端訪(fǎng)問(wèn)不到的地址癥狀就是「能連上但發(fā)不出消息」或者「一直重連」。本地開(kāi)發(fā)最穩(wěn)妥的寫(xiě)法是127.0.0.1。有人圖省事寫(xiě)成主機(jī)名結(jié)果換了網(wǎng)絡(luò)環(huán)境主機(jī)名解析變了就連不上了。也有人直接不配這一項(xiàng)某些版本下會(huì)退化成用listeners的值一旦listeners寫(xiě)的是0.0.0.0客戶(hù)端拿到這個(gè)地址就會(huì)去連0.0.0.0:9092然后失敗。所以這一項(xiàng)我建議永遠(yuǎn)顯式寫(xiě)死。再講log.dirs。路徑分隔符在配置文件里用正斜杠/是最省心的反斜杠在某些解析路徑下會(huì)被當(dāng)成轉(zhuǎn)義符。盤(pán)符加冒號(hào)沒(méi)問(wèn)題但路徑里不要有空格這一點(diǎn)前面已經(jīng)強(qiáng)調(diào)過(guò)。auto.create.topics.enable我強(qiáng)烈建議關(guān)掉。默認(rèn)開(kāi)啟的時(shí)候生產(chǎn)者往一個(gè)不存在的主題發(fā)消息服務(wù)端會(huì)自動(dòng)創(chuàng)建一個(gè)分區(qū)數(shù)和副本數(shù)都取默認(rèn)值的主題出來(lái)。開(kāi)發(fā)階段看著方便實(shí)際上是災(zāi)難拼錯(cuò)一個(gè)字母就多出一個(gè)垃圾主題而且自動(dòng)創(chuàng)建的主題參數(shù)往往不是你想要的事后還得挨個(gè)刪。關(guān)掉之后主題不存在就是明確報(bào)錯(cuò)問(wèn)題暴露得早。內(nèi)部主題那幾個(gè)副本數(shù)參數(shù)offsets.topic.replication.factor等在單機(jī)上必須設(shè)成 1。原因很直接單機(jī)只有一個(gè)節(jié)點(diǎn)副本數(shù)配成 3 的話(huà)創(chuàng)建內(nèi)部主題時(shí)會(huì)因?yàn)闇惒积R副本而一直卡在創(chuàng)建中表現(xiàn)出來(lái)就是消費(fèi)位點(diǎn)提交失敗、事務(wù)相關(guān)功能不可用而錯(cuò)誤信息往往很隱晦不直接告訴你副本不夠。這一條我吃過(guò)虧當(dāng)時(shí)找了很久才發(fā)現(xiàn)是副本數(shù)的問(wèn)題。2.3 Windows 下必須改的默認(rèn)項(xiàng)清單除了上面這些通用參數(shù)Windows 場(chǎng)景下有幾項(xiàng)我每次都會(huì)額外檢查一遍列成一張清單方便對(duì)照。配置項(xiàng)默認(rèn)情況建議值不改會(huì)怎樣數(shù)據(jù)目錄路徑相對(duì)路徑或臨時(shí)目錄獨(dú)立盤(pán)絕對(duì)路徑數(shù)據(jù)易丟、系統(tǒng)盤(pán)被撐滿(mǎn)監(jiān)聽(tīng)地址可能綁定所有網(wǎng)卡顯式寫(xiě) 127.0.0.1客戶(hù)端拿到不可達(dá)地址連不上自動(dòng)建主題通常開(kāi)啟關(guān)閉拼錯(cuò)主題名靜默產(chǎn)生垃圾主題內(nèi)部主題副本數(shù)可能為 31內(nèi)部主題創(chuàng)建卡住位點(diǎn)提交異常日志保留時(shí)長(zhǎng)168 小時(shí)24 小時(shí)壓測(cè)時(shí)磁盤(pán)迅速寫(xiě)滿(mǎn)單個(gè)日志段大小1GB256MB小數(shù)據(jù)量下產(chǎn)生大量小文件清理不靈活還有一項(xiàng)容易漏服務(wù)自身的日志輸出目錄。發(fā)行包里一般有個(gè)config\log4j.properties或者同類(lèi)的日志配置文件里面寫(xiě)著應(yīng)用日志往哪寫(xiě)。默認(rèn)往往是個(gè)相對(duì)路徑啟動(dòng)腳本從哪個(gè)目錄被調(diào)用日志就落在哪里。如果你是從資源管理器雙擊.bat啟動(dòng)的當(dāng)前工作目錄可能是腳本所在目錄也可能不是這取決于腳本內(nèi)部有沒(méi)有自己切目錄。表現(xiàn)就是「明明啟動(dòng)了但找不到日志文件」。處理方式是把這個(gè)路徑改成絕對(duì)路徑一勞永逸。3. 啟動(dòng) Kafka 的完整實(shí)操流程3.1 元數(shù)據(jù)內(nèi)置模式的首次啟動(dòng)先格式化再啟動(dòng)這套模式下第一次啟動(dòng)必須做一次存儲(chǔ)目錄格式化本質(zhì)是給這個(gè)數(shù)據(jù)目錄打上集群身份標(biāo)識(shí)并在里面初始化元數(shù)據(jù)。第一步生成一個(gè)集群 IDcd /d D:\dev\kafka bin\windows\kafka-storage.bat random-uuid命令會(huì)輸出一串形如AbCdEfGhIjKlMnOpQrStUv的字符串長(zhǎng)度固定字符集是大小寫(xiě)字母加數(shù)字。把這串東西復(fù)制下來(lái)后面要用。第二步用這個(gè) ID 格式化數(shù)據(jù)目錄bin\windows\kafka-storage.bat format -t AbCdEfGhIjKlMnOpQrStUv -c config\kraft\server.properties這里-t后面跟集群 ID-c后面跟配置文件路徑。執(zhí)行成功會(huì)打印類(lèi)似「Formatting metadata directory ... with metadata.version ...」的信息。如果提示數(shù)據(jù)目錄已存在且非空說(shuō)明你重復(fù)格式化了這時(shí)候要么換一個(gè)空目錄要么先把原目錄清掉——但清掉就意味著原來(lái)的數(shù)據(jù)全沒(méi)了這一步一定要看清楚再回車(chē)。第三步啟動(dòng)服務(wù)bin\windows\kafka-server-start.bat config\kraft\server.properties窗口里會(huì)滾出一大堆日志最后停在類(lèi)似「Kafka Server started」或者「started (kafka.server.KafkaRaftServer)」這樣的行上。看到這行才算真的起來(lái)了之前的所有輸出都可能是初始化過(guò)程。注意格式化這一步只做一次。后續(xù)每次啟動(dòng)都直接執(zhí)行第三步不要再格式化。養(yǎng)成習(xí)慣把格式化命令單獨(dú)記在一個(gè)地方別和日常啟動(dòng)命令放一起避免手滑。3.2 判斷「到底起沒(méi)起來(lái)」的三個(gè)硬指標(biāo)日志刷得飛快光看最后一行不保險(xiǎn)。我一般用三個(gè)指標(biāo)交叉驗(yàn)證。指標(biāo)一是端口。開(kāi)一個(gè)新窗口執(zhí)行netstat -ano | findstr :9092能看到LISTENING狀態(tài)的記錄說(shuō)明端口已經(jīng)在監(jiān)聽(tīng)。如果什么都沒(méi)有說(shuō)明進(jìn)程根本沒(méi)綁上端口去看啟動(dòng)窗口里的報(bào)錯(cuò)。指標(biāo)二是進(jìn)程。執(zhí)行tasklist | findstr java能看到 java 進(jìn)程。Windows 上可能還有別的 Java 程序在跑所以這一條只能作為輔助配合端口一起看更準(zhǔn)。指標(biāo)三是功能驗(yàn)證。用自帶的命令行工具列一下主題bin\windows\kafka-topics.bat --bootstrap-server 127.0.0.1:9092 --list這條命令能正常返回哪怕返回空列表說(shuō)明服務(wù)端不僅能接受連接還能處理元數(shù)據(jù)請(qǐng)求這才是真正的「起來(lái)了」。如果這里報(bào)連接被拒絕那就是服務(wù)沒(méi)起或者端口不對(duì)如果報(bào)超時(shí)可能是防火墻攔了或者advertised.listeners配錯(cuò)了。我踩過(guò)的一個(gè)坑是啟動(dòng)窗口里明明打印了 started但客戶(hù)端連不上。原因是啟動(dòng)過(guò)程中某一步失敗后又重試成功了另一個(gè)組件日志里既有 started 又有后續(xù)的警告粗看以為是好的。后來(lái)我固定用第三條命令做驗(yàn)收再?zèng)]誤判過(guò)。3.3 讓服務(wù)在后臺(tái)穩(wěn)定跑著三種可行做法命令行窗口一直開(kāi)著一不小心點(diǎn)到關(guān)閉按鈕服務(wù)就沒(méi)了。這在開(kāi)發(fā)機(jī)上很常見(jiàn)尤其是需要長(zhǎng)期掛著做聯(lián)調(diào)的時(shí)候。我試過(guò)幾種辦法。第一種是用start命令開(kāi)一個(gè)新窗口跑腳本內(nèi)容如下echo off cd /d D:\dev\kafka start kafka-broker /min bin\windows\kafka-server-start.bat config\kraft\server.properties/min讓新窗口最小化眼不見(jiàn)心不煩。缺點(diǎn)是窗口還是存在誤關(guān)的風(fēng)險(xiǎn)依然有。第二種是用javaw直接起 JVM。javaw和java的區(qū)別是不帶控制臺(tái)窗口進(jìn)程在后臺(tái)。但這條路要自己拼完整的 classpath 和啟動(dòng)參數(shù)比較麻煩而且一旦出問(wèn)題沒(méi)有任何輸出可看排查成本高。我不推薦在還沒(méi)跑通階段就用這個(gè)。第三種是把啟動(dòng)腳本注冊(cè)成一個(gè) Windows 服務(wù)用系統(tǒng)服務(wù)管理器管理。好處是開(kāi)機(jī)自啟、狀態(tài)可查、異常退出可以配自動(dòng)重啟。代價(jià)是配置步驟多需要額外的工具把普通程序包裝成服務(wù)而且調(diào)試時(shí)看日志不如直接看窗口方便。我個(gè)人的選擇是調(diào)試階段就用普通窗口跑看得見(jiàn)日志最重要跑通之后需要長(zhǎng)期掛著再考慮包裝成服務(wù)。中間態(tài)可以用第一種start /min應(yīng)付一下成本最低。3.4 關(guān)閉與重啟的正確姿勢(shì)關(guān)閉這件事有講究。直接在啟動(dòng)窗口按CtrlCKafka 會(huì)收到中斷信號(hào)走正常的關(guān)閉流程刷盤(pán)、注銷(xiāo)、釋放端口。窗口里會(huì)打印一串關(guān)閉日志最后進(jìn)程退出。這是推薦做法。直接在任務(wù)管理器里結(jié)束進(jìn)程或者點(diǎn)窗口右上角的叉屬于強(qiáng)殺。強(qiáng)殺之后下次啟動(dòng)時(shí)日志里可能出現(xiàn)關(guān)于未正常關(guān)閉的段文件的提示正常情況下會(huì)自動(dòng)修復(fù)但如果你正好在壓測(cè)可能出現(xiàn)少量的數(shù)據(jù)不一致。開(kāi)發(fā)環(huán)境無(wú)所謂但養(yǎng)成好習(xí)慣總沒(méi)錯(cuò)。重啟的時(shí)候有個(gè)順序問(wèn)題如果是元數(shù)據(jù)內(nèi)置模式直接停掉再起就行不用重新格式化。如果用的是依賴(lài)外部協(xié)調(diào)服務(wù)的模式關(guān)的時(shí)候先關(guān) Kafka 再關(guān)協(xié)調(diào)服務(wù)起的時(shí)候反過(guò)來(lái)。順序反了協(xié)調(diào)服務(wù)里會(huì)殘留舊的注冊(cè)信息新起來(lái)的 Kafka 可能以為自己還是舊會(huì)話(huà)出現(xiàn)一些奇怪的選主行為。還有一個(gè)細(xì)節(jié)停掉之后端口不會(huì)立刻釋放可能需要幾秒。如果你寫(xiě)了個(gè)腳本「停掉馬上重啟」有可能撞上端口占用。加一個(gè)等待或者重啟前先netstat檢查一下端口是否已釋放能避開(kāi)這個(gè)偶發(fā)問(wèn)題。4. 創(chuàng)建主題隊(duì)列的參數(shù)與實(shí)操4.1 命令結(jié)構(gòu)與最常用的幾個(gè)參數(shù)主題也就是大家口中常說(shuō)的隊(duì)列是 Kafka 里組織和隔離消息的基本單位。創(chuàng)建命令的骨架是這樣bin\windows\kafka-topics.bat --bootstrap-server 127.0.0.1:9092 ^ --create ^ --topic order-events ^ --partitions 3 ^ --replication-factor 1Windows 命令行里換行用^這是.bat體系的寫(xiě)法。如果你嫌麻煩整條命令寫(xiě)成一行也完全可以。幾個(gè)參數(shù)的含義--bootstrap-server任意一個(gè)可連的節(jié)點(diǎn)地址用來(lái)拿集群元數(shù)據(jù)。本地就寫(xiě)127.0.0.1:9092。--create動(dòng)作創(chuàng)建。--topic主題名。只能用字母、數(shù)字、點(diǎn)、下劃線(xiàn)、連字符長(zhǎng)度別太夸張。--partitions分區(qū)數(shù)。決定這個(gè)主題能并行處理到多大程度。--replication-factor副本數(shù)。單機(jī)只能填 1填大了會(huì)直接失敗。主題命名我有個(gè)習(xí)慣用「業(yè)務(wù)域-事件類(lèi)型」這種兩段式比如order-events、user-profile-changes。別用中文別用大寫(xiě)別用點(diǎn)號(hào)開(kāi)頭。點(diǎn)號(hào)在內(nèi)部會(huì)被轉(zhuǎn)換成下劃線(xiàn)參與指標(biāo)命名容易和監(jiān)控系統(tǒng)里的名字對(duì)不上。還有個(gè)常用參數(shù)--config用來(lái)覆蓋單個(gè)主題的配置。比如給某個(gè)主題單獨(dú)設(shè)保留時(shí)間bin\windows\kafka-topics.bat --bootstrap-server 127.0.0.1:9092 ^ --create --topic debug-trace ^ --partitions 1 --replication-factor 1 ^ --config retention.ms3600000這條命令創(chuàng)建的主題只保留一小時(shí)數(shù)據(jù)適合那種臨時(shí)排查用的調(diào)試主題不用了就自動(dòng)清掉省得手動(dòng)刪。4.2 分區(qū)數(shù)怎么算副本數(shù)怎么定分區(qū)數(shù)是最容易被隨意填的參數(shù)但它直接決定了這個(gè)主題的并行上限。核心邏輯是一個(gè)分區(qū)在同一時(shí)刻只能被同一個(gè)消費(fèi)組里的一個(gè)消費(fèi)者消費(fèi)。所以如果消費(fèi)端有 6 個(gè)實(shí)例主題只有 3 個(gè)分區(qū)那么有 3 個(gè)實(shí)例會(huì)分不到分區(qū)一直空閑。反過(guò)來(lái)算也一樣。假設(shè)單分區(qū)能穩(wěn)定處理 5MB/s業(yè)務(wù)峰值寫(xiě)入是 30MB/s那么至少要 6 個(gè)分區(qū)。公式很簡(jiǎn)單最小分區(qū)數(shù) 峰值寫(xiě)入吞吐 ÷ 單分區(qū)可持續(xù)吞吐但這個(gè)公式里最難確定的是「單分區(qū)可持續(xù)吞吐」。生產(chǎn)環(huán)境里我一般按 2MB/s 到 5MB/s 這個(gè)區(qū)間保守估本地開(kāi)發(fā)完全不用算因?yàn)闆](méi)壓力。真正需要認(rèn)真算的是那種上線(xiàn)后不好改的場(chǎng)景——分區(qū)數(shù)雖然可以增加但減少是不支持的而且增加分區(qū)會(huì)打亂原有的按 key 分區(qū)規(guī)則導(dǎo)致同一個(gè) key 的消息在新舊分區(qū)之間分布不一致。副本數(shù)在單機(jī)上只能是 1因?yàn)橹挥幸粋€(gè)節(jié)點(diǎn)副本要落在不同節(jié)點(diǎn)上。本地開(kāi)發(fā)填 1 就對(duì)了。上了多節(jié)點(diǎn)環(huán)境副本數(shù)一般給 2 或 3含義是每條消息存幾份。副本越多越不容易丟數(shù)據(jù)但磁盤(pán)占用和網(wǎng)絡(luò)開(kāi)銷(xiāo)也成倍漲。這里沒(méi)有標(biāo)準(zhǔn)答案看你能接受多大的數(shù)據(jù)丟失風(fēng)險(xiǎn)。場(chǎng)景建議分區(qū)數(shù)建議副本數(shù)理由本地開(kāi)發(fā)驗(yàn)證1 到 31無(wú)并發(fā)壓力單節(jié)點(diǎn)只能 1 副本單機(jī)小流量服務(wù)3 到 61留出消費(fèi)端并行空間多節(jié)點(diǎn)常規(guī)業(yè)務(wù)按吞吐算取 2 的冪附近2 或 3兼顧并行與容錯(cuò)高吞吐核心鏈路按峰值算完再上浮 50%3給突發(fā)流量留余量「取 2 的冪附近」這個(gè)說(shuō)法來(lái)自實(shí)踐分區(qū)數(shù)在 6、12、24 這類(lèi)數(shù)字上配合消費(fèi)端實(shí)例數(shù)擴(kuò)縮容時(shí)分布更均勻。不是硬規(guī)定但能減少一些湊不整的尷尬。4.3 主題的查詢(xún)、修改與刪除創(chuàng)建完之后要確認(rèn)用--describebin\windows\kafka-topics.bat --bootstrap-server 127.0.0.1:9092 --describe --topic order-events輸出里會(huì)列出每個(gè)分區(qū)的信息分區(qū)號(hào)、 Leader 在哪個(gè)節(jié)點(diǎn)、副本分布、同步中的副本。單機(jī)環(huán)境下 Leader 都是 0 號(hào)節(jié)點(diǎn)副本也是它自己??吹矫總€(gè)分區(qū)都有 Leader說(shuō)明主題狀態(tài)正常。如果 Leader 那一列是none或者-1說(shuō)明分區(qū)還沒(méi)完成選主通常是節(jié)點(diǎn)剛起來(lái)還沒(méi)穩(wěn)定等幾秒再看。加分區(qū)bin\windows\kafka-topics.bat --bootstrap-server 127.0.0.1:9092 ^ --alter --topic order-events --partitions 6只能加不能減。減分區(qū)是不支持的需要重建主題并遷移數(shù)據(jù)成本很高。這也是為什么前面強(qiáng)調(diào)分區(qū)數(shù)寧可按估算偏大一點(diǎn)別事后加。刪除主題bin\windows\kafka-topics.bat --bootstrap-server 127.0.0.1:9092 --delete --topic order-events刪除是異步的命令返回成功不代表文件立刻清掉了。執(zhí)行完再--list一次如果還在等一會(huì)兒或者重啟一下服務(wù)再看。另外要注意如果配置里把刪除功能禁用了這條命令會(huì)失敗需要先改配置。還有一個(gè)常用動(dòng)作是把所有主題列出來(lái)并看詳情bin\windows\kafka-topics.bat --bootstrap-server 127.0.0.1:9092 --describe不加--topic就是全量輸出可能很長(zhǎng)配合重定向到文件再看更舒服。4.4 把常用命令封裝成腳本每次敲這么長(zhǎng)的命令很煩我一般會(huì)在kafka目錄下放兩個(gè)批處理文件一個(gè)建主題一個(gè)刪主題參數(shù)從命令行傳進(jìn)去echo off setlocal if %~1 ( echo 用法: create-topic.bat ^主題名^ [分區(qū)數(shù)] [副本數(shù)] exit /b 1 ) set TOPIC%~1 set PARTS%~2 set REPLICAS%~3 if %PARTS% set PARTS3 if %REPLICAS% set REPLICAS1 cd /d %~dp0.. call bin\windows\kafka-topics.bat --bootstrap-server 127.0.0.1:9092 ^ --create --topic %TOPIC% --partitions %PARTS% --replication-factor %REPLICAS% endlocal這段腳本的關(guān)鍵點(diǎn)有三個(gè)cd /d %~dp0..先把工作目錄切到安裝目錄避免從別的目錄調(diào)用時(shí)相對(duì)路徑失效參數(shù)做了默認(rèn)值處理不傳就用默認(rèn)call是必須的直接調(diào)用.bat會(huì)導(dǎo)致控制權(quán)不返回腳本后面的代碼不執(zhí)行。封裝之后建主題就是一行create-topic.bat order-events 6這種小工具看著不起眼但一天建十幾個(gè)主題的時(shí)候省下來(lái)的時(shí)間和少犯的錯(cuò)是實(shí)打?qū)嵉摹?. 生產(chǎn)消費(fèi)閉環(huán)驗(yàn)證5.1 用命令行工具跑通收發(fā)主題建好了得驗(yàn)證真能收到消息。Kafka 自帶控制臺(tái)生產(chǎn)者bin\windows\kafka-console-producer.bat --bootstrap-server 127.0.0.1:9092 --topic order-events回車(chē)之后會(huì)進(jìn)入一個(gè)可以逐行輸入的模式輸入一行回車(chē)就發(fā)一條。發(fā)幾條像helloworld這樣的內(nèi)容進(jìn)去然后用消費(fèi)者讀bin\windows\kafka-console-consumer.bat --bootstrap-server 127.0.0.1:9092 --topic order-events --from-beginning--from-beginning表示從最早的消息開(kāi)始讀。不加這個(gè)參數(shù)默認(rèn)從最新的開(kāi)始會(huì)出現(xiàn)「生產(chǎn)者發(fā)了但消費(fèi)者什么都沒(méi)看到」的錯(cuò)覺(jué)——其實(shí)是消費(fèi)者啟動(dòng)時(shí)機(jī)晚于消息寫(xiě)入跳過(guò)了舊消息。5.2 三個(gè)容易誤判的現(xiàn)象第一個(gè)現(xiàn)象消費(fèi)者起來(lái)了但一條都沒(méi)讀到。除了上面說(shuō)的--from-beginning還有一種可能是消費(fèi)者用了另一個(gè)消費(fèi)組而那個(gè)組的位點(diǎn)已經(jīng)提交到末尾了。控制臺(tái)消費(fèi)者每次啟動(dòng)默認(rèn)用隨機(jī)組名理論上不會(huì)撞但如果你手動(dòng)指定了--group就要注意這一點(diǎn)。第二個(gè)現(xiàn)象生產(chǎn)者輸入了內(nèi)容按回車(chē)卻卡住不動(dòng)。多半是主題不存在而自動(dòng)建主題又被關(guān)掉了客戶(hù)端在等元數(shù)據(jù)刷新。確認(rèn)主題名拼寫(xiě)或者先--list看一眼。第三個(gè)現(xiàn)象命令行里中文顯示成亂碼。這是 Windows 控制臺(tái)編碼的問(wèn)題不影響實(shí)際存儲(chǔ)但看日志很難受??梢栽趩?dòng)腳本開(kāi)頭加一行chcp 65001切到 UTF-8或者干脆測(cè)試時(shí)用英文內(nèi)容別和自己較勁。6. 常見(jiàn)問(wèn)題排查實(shí)錄6.1 啟動(dòng)類(lèi)問(wèn)題速查現(xiàn)象可能原因處理方式窗口一閃就關(guān)腳本內(nèi)部報(bào)錯(cuò)窗口關(guān)閉太快看不到從命令行里調(diào)用腳本別雙擊提示找不到或無(wú)法加載主類(lèi)classpath 拼接出錯(cuò)常見(jiàn)于路徑有空格或中文換到無(wú)空格無(wú)中文的短路徑提示 JAVA_HOME 不正確環(huán)境變量指向了不存在的目錄核對(duì) JAVA_HOME 與 java 命令的一致性端口被占用上一次的進(jìn)程沒(méi)退干凈或別的程序占了 9092用 netstat 定位進(jìn)程號(hào)后結(jié)束啟動(dòng)日志出現(xiàn) meta.properties 相關(guān)錯(cuò)誤存儲(chǔ)目錄不是用當(dāng)前集群 ID 格式化的換空目錄重新格式化或清空后重來(lái)卡在「等待初始化」不再往下走內(nèi)部主題副本數(shù)配得比節(jié)點(diǎn)數(shù)多把內(nèi)部主題副本數(shù)改成 1排查順序我固定成一個(gè)套路先看窗口日志里第一條 ERROR而不是最后一條。第一條錯(cuò)誤往往才是根因后面的大多是連鎖反應(yīng)。找不到 ERROR 就用端口和進(jìn)程兩個(gè)指標(biāo)反推。6.2 主題與消費(fèi)類(lèi)問(wèn)題速查現(xiàn)象可能原因處理方式創(chuàng)建主題報(bào)副本數(shù)過(guò)大單節(jié)點(diǎn)填了大于 1 的副本改成 1創(chuàng)建主題報(bào)已存在主題名重復(fù)或者自動(dòng)建主題曾經(jīng)建過(guò)先 describe 確認(rèn)需要的話(huà)刪了重建生產(chǎn)者發(fā)消息超時(shí)advertised.listeners 地址客戶(hù)端不可達(dá)改成 127.0.0.1 并重啟消費(fèi)者讀不到歷史消息沒(méi)加 --from-beginning加上該參數(shù)或改用新組名describe 顯示 Leader 為 none分區(qū)還在選主或節(jié)點(diǎn)狀態(tài)異常等幾秒重試仍不行就看服務(wù)端日志刪除主題后列表里還在刪除是異步的或刪除功能被禁用等待后重試必要時(shí)改配置這些表我建議存在自己的筆記里出問(wèn)題時(shí)對(duì)著掃一遍比漫無(wú)目的搜要快得多。6.3 我踩過(guò)的兩個(gè)典型坑第一個(gè)坑是「路徑里有個(gè)空格」。當(dāng)時(shí)圖方便把整個(gè)包解壓到了類(lèi)似D:\我的工具\(yùn)kafka 安裝這樣的目錄啟動(dòng)時(shí)窗口閃一下就沒(méi)了。從命令行調(diào)用終于看到報(bào)錯(cuò)大意是找不到某個(gè) jar。查了很久才意識(shí)到是腳本里-cp參數(shù)后面直接接了含空格的路徑?jīng)]加引號(hào)被拆成了兩段。解決辦法就是把目錄改成D:\dev\kafka改完立刻就通了。這個(gè)坑的教訓(xùn)是涉及腳本拼接路徑的工具一律放在無(wú)空格的英文短路徑下不要挑戰(zhàn)它的健壯性。第二個(gè)坑是「防火墻靜默攔截」。有一次換了臺(tái)機(jī)器服務(wù)起來(lái)了一切正常命令行工具也能連但從另一個(gè)工具連就超時(shí)。檢查半天發(fā)現(xiàn)那臺(tái)機(jī)器上有一條入站規(guī)則攔住了 9092而命令行工具走的是本機(jī)回環(huán)不受影響。這個(gè)問(wèn)題的隱蔽性在于「本機(jī)測(cè)都正常」很容易誤判成客戶(hù)端配置問(wèn)題。判斷方法很簡(jiǎn)單換一個(gè)地址連一下如果你從本機(jī)連回環(huán)能通、從別的機(jī)器連本機(jī) IP 不通基本就是網(wǎng)絡(luò)層攔了。7. 我在實(shí)際項(xiàng)目里踩出來(lái)的經(jīng)驗(yàn)7.1 幾個(gè)反直覺(jué)但很管用的細(xì)節(jié)細(xì)節(jié)一不要迷信「啟動(dòng)日志最后一行」。日志是多個(gè)組件并發(fā)打的最后一行可能是某個(gè)后臺(tái)線(xiàn)程的常規(guī)輸出跟啟動(dòng)成功沒(méi)有必然關(guān)系。真正的驗(yàn)收標(biāo)準(zhǔn)永遠(yuǎn)是「能不能列主題」「能不能收發(fā)消息」。細(xì)節(jié)二改完配置必須重啟才生效。我見(jiàn)過(guò)有人在窗口跑著的時(shí)候改server.properties然后疑惑為什么行為沒(méi)變。絕大多數(shù)參數(shù)都是啟動(dòng)時(shí)讀取一次運(yùn)行期不重讀。改配置和重啟要綁成一個(gè)動(dòng)作記在腦子里。細(xì)節(jié)三單機(jī)的副本數(shù)配大了報(bào)錯(cuò)位置往往和原因無(wú)關(guān)。你配了 3 副本報(bào)的可能是位點(diǎn)提交失敗、事務(wù)超時(shí)而不是「副本不足」。這類(lèi)間接報(bào)錯(cuò)最耗時(shí)間所以單機(jī)配置里所有和副本數(shù)相關(guān)的項(xiàng)掃一遍確認(rèn)全是 1。細(xì)節(jié)四主題名不要用點(diǎn)號(hào)開(kāi)頭。有些命名習(xí)慣是從別的系統(tǒng)帶過(guò)來(lái)的但在 Kafka 里點(diǎn)號(hào)開(kāi)頭有特殊用途內(nèi)部主題的命名空間自己的業(yè)務(wù)主題別往里擠免得和內(nèi)部主題混在一起清理的時(shí)候誤刪。7.2 跑通之后可以往哪擴(kuò)單機(jī)跑通只是起點(diǎn)。接下來(lái)最自然的一步是把消息的序列化方式換掉從純字符串換成結(jié)構(gòu)化數(shù)據(jù)這會(huì)讓消息體更可控也方便后續(xù)做 schema 管理。再往后可以考慮多節(jié)點(diǎn)把node.id分散開(kāi)每個(gè)節(jié)點(diǎn)一份配置調(diào)好監(jiān)聽(tīng)地址就能組一個(gè)小集群副本數(shù)終于能設(shè)成 2 了容錯(cuò)這一塊才真正開(kāi)始有意義。如果是要做業(yè)務(wù)落地還有兩件事值得提前布局。一是給主題定好保留策略和清理策略別等磁盤(pán)報(bào)警了再手忙腳亂二是把消費(fèi)端的位點(diǎn)提交方式想清楚自動(dòng)提交省事但可能重復(fù)消費(fèi)手動(dòng)提交可靠但代碼復(fù)雜這個(gè)取舍在寫(xiě)第一行業(yè)務(wù)代碼之前就該定好。最后一個(gè)建議把這套本地環(huán)境的搭建步驟寫(xiě)成一份帶版本號(hào)和環(huán)境信息的短文檔放在項(xiàng)目倉(cāng)庫(kù)的角落里。三個(gè)月后你換電腦、或者同事要復(fù)現(xiàn)你的環(huán)境這份文檔能省掉的溝通和試錯(cuò)遠(yuǎn)比寫(xiě)它的時(shí)間多。我自己那份已經(jīng)改到第七版了每一版都是在踩坑之后補(bǔ)上去的現(xiàn)在拿出來(lái)基本照著走一遍就能起來(lái)。