Kafka發行包里自帶的樣本配置可以用來安裝單機服務,但并不能滿足大多數安裝場景的要求。Kafka有很多配置選項,涉及安裝和調優的方方面面。不過大多數調優選項可以使用默認配置,除非對調優有特別需求。有一些配置選項,在單機安裝時可以直接使用默認配置,下面看一些常規配置。
常規配置
broker.id
每個broker都需要有一個標識符,使用broker.id來表示。它的默認值是0,也可以被設置成其它任意整數,這個值在整個kafka集群里必須是唯一的。這個值可以任意選定,如果出于維護的需要,可以在服務器節點間交換使用這些ID。建議把它們設置成與機器名具有相關性的整數,這樣在進行維護時,將ID號映射到機器名就沒有那么麻煩了。例如,如果機器名包含唯一性的數字(例如name1.example.com,name2.example.com),那么用這些數字來設置broker.id就再好不過了。
port
如果使用配置樣本來啟動Kafka,他會監聽9092端口。修改port配置參數可以把它設置成其他任意可以用的端口。要注意,如果使用1024以下的端口,需要使用root權限啟動kafka,不過不建議這么做。
zookeeper.connect
用于保存broker元數據的zookeeper地址是通過zookeeper.connect來指定的。localhost:2181表示這個zookeeper是運行在本地的2181端口上。該配置參數是用冒號分割的一組hostname:port/path列表,每一部分的含義如下:
hostname是zookeeper服務器的機器名或ip地址
port是zookeeper的客戶端連接端口
/path是可以選的zookeeper路徑,作為kafka集群的chroot環境,如果不指定,默認使用根路徑
如果指定的chroot路徑不存在,broker會在啟動的的時候創建它。
為什么使用chroot路徑?在kafka集群里使用chroot路徑是一種最佳實踐。zookeeper集群可以共享給其他應用程序,即使還有其他kafka集群存在,也不會產生沖突。最好是在配置文件里指定一組zookeeper服務器,用分號把它們分隔開。一旦有一個zookeeper服務器宕機,broker可以連接到zookeeper群組的另一個節點上。
log.dirs
kafka把所有消息都保存在磁盤上,存放這些日志片段的目錄是通過log.dirs指定的。它是一組用逗號分隔的本地文件系統路徑。它是一組用逗號分隔的本地文件系統路徑。如果指定了多個路徑,那么broker會根據“最少使用”原則,把同一個分區的日志片段保存到同一個路徑下。要注意,broker會往擁有最少數目分區的路徑新增分區,而不是往擁有最小磁盤空間的路徑新增分區。
num.recovery.threads.per.data.dir
對于如下3種情況,kafka會使用可以配置的線程池來處理日志片段:
服務器正常啟動,用于打開每個分區的日志片段;
服務器崩潰后重啟,用于檢查和截短每個分區的日志片段;
服務器正常關閉,用于關閉日志片段
默認情況下,每個日志目錄只使用一個線程。因為這些線程只是在服務器啟動和關閉時會用到,所以完全可以設置大量的線程來達到并行操作的目的。特別是對于包含大量分區的服務器來說,一旦發生崩潰,在進行恢復時使用并行操作可能會省下數小時的時間。設置此參數時需要注意,所配置的數字對應的是log.dirs指定的單個日志目錄。也就是說,如果num.recovery.threads.per.data.dir被設置為8,并且log.dir指定了3個路徑,那么總共需要24個線程。
auto.create.topics.enable
默認情況下,kafka會在如下幾種情況下自動創建主題:
當一個生產者開始往主題寫入消息時
當一個消費者開始從主題讀取消息時
當任意一個客戶端向主題發送元數據請求時
很多時候,這種行為都是非預期的。而且,根據kafka協議,如果一個主題不先被創建,根本無法知道它是否已經存在。如果顯式的創建主題,不管是手動創建還是通過其他系統創建,都可以把auto.create.topics.enable設置為false。
主題配置
kafka為新創建的主題提供了很多默認配置參數。可以通過管理工具為每個主題單獨配置一部分數據,比如分區個數和數據保留策略。服務器提供的默認配置可以作為基準,它們適用于大部分主題。
注意,新版本要對主題配置進行覆蓋,需要使用管理工具。
num.partitions
num.partitions參數指定了新創建的主題將包含多少個分區。如果啟用了主題自動創建功能(該功能默認啟動),主題分區個數就該參數指定的值。該參數的默認值是1。要注意,主題分區的個數可以增加,但是不能減少。所以,如果要讓一個主題的分區個數小于num.partitions制定的值,需要手動創建該主題。
前面提過Kafka集群通過分區對主題進行橫向擴展,所以當有新的broker加入集群時,可以通過分區個數來實現集群的負載均衡。當然這并不是說,在存在多個主題的情況下(它們分布在多個broker上),為了能讓分區分布到所有broker上,主題分區的個數必須要大于broker個數。不過,擁有大量消息的主題如果要進行負載分散,就需要大量的分區。
如何選定分區的數量?為主題選定分區數量并不是一件可有可無的事,在進行數量選擇時需要考慮以下幾個因素:
1? 主題需要達到多大的吞吐量?是希望每秒寫入100K還是1G?
2? 從單個分區讀取數據的最大吞吐量是多少?每個分區一般都會有一個消費者,如果消費者寫入數據庫的速度不會超過每秒50M,那么從一個分區讀取數據的吞吐量也不需要超過每秒50M
3? 生產者的速度一般比消費者快得多,所以最好為生產者多估算一些吞吐量
4? 每個broker包含的分區數,可用的磁盤空間,網絡帶寬都是考慮因素
5? 如果消息按照不同的鍵寫入分區的,那么為已有的主題新增分區就會很困難
6? 單個broker對分區個數是有限制的,因為分區越多占用內存越多,完成首領選舉需要的時間也越長
綜合以上考慮,分區個數要適量的多,但是不能太多,可以根據估算出的吞吐量,用主題吞吐量除以消費者吞吐量算出分區的個數。也就是說,如果每秒要從主題上讀寫1G數據,并且每個消費者每秒可以處理50M,那么至少需要20個分區,這樣就可以讓20個消費者同時讀取這些分區,從而達到預期吞吐量。如果信息不明,根據經驗,把分區的大小限制在25GB以內可以得到比較理想的效果。
log.retention.ms
kafka通常根據時間來決定數據被保留多久。默認使用log.retention.hours參數來配置時間,默認值為168小時,也就是一周。此外,還有兩個參數log.retention.minutes和log.retentions.ms。這三個參數的作用是一樣的,都是設置消息多久后被刪除,推薦使用log.retentions.ms。如果制定了多個,會優先使用單位最小的。
根據時間保留數據是通過檢查磁盤上日志片段文件的最后修改時間來實現的。通常最后修改時間指的就是日志片段的關閉時間。也即是文件里最后一個消息的時間戳。不過,如果使用管理工具在服務器間移動分區,最后修改時間就不準確了,時間誤差可能導致這些分區過多的保留數據。
log.retention.bytes
通過保留的消息字節數也可以判斷消息是否過期,它的值通過參數log.retention.bytes來指定,作用在每一個分區上,也就是說,如果有一個包含8個分區的主題,并且log.retention.bytes被指定為1G,那么此主題最多可保留8G數據,所以,當主題的分區數增加時,整個主題可以保留的數據也隨之增加。同時指定了保留時間和保留字節數,只要任意 一個條件達到指定值,消息就會被刪除。
log.segment.bytes
以上設置都是作用在日志片段而不是單個消息上。當消息到達broker時,它們被追加到分區的當前日志片段上。當日志片段大小到達log.segment.bytes指定的上限(默認1G)時,當前日志片段就會被關閉,一個新的日志片段就會被打開。如果一個日志片段被關閉,就開始等待過期。這個參數值越小,就會越頻繁的關閉和分配新文件,會降低磁盤寫入的整體效率。
如果主題的消息量不大,那么如何調整這個參數大小尤為重要。例如一個主題每天只接收100M消息,而log.segment.bytes使用默認值,那么需要10天才能寫滿一個日志片段,因為日志片段被關閉前消息是不會過期的,所以如果log.retention.ms被設置為604 800 000(也就是一周),那么日志片段最多需要17天才會過期,這是因為關閉需要10天,過期需要七天。
還可以使用時間戳獲取偏移量。日志片段的大小會影響使用時間戳獲取偏移量。在使用時間戳獲取日志偏移量時,Kafka會檢查分區里最后修改時間大于指定時間戳的日志片段(已經被關閉的),該日志片段的前一個文件的最后修改時間小于指定時間戳,然后Kafka返回該日志片段(也就是文件名)開頭的偏移量。對于使用時間戳獲取偏移量來說,日志片段越小結果越準確。
log.segment.ms
另一個可以控制日志片段關閉的參數是log.segment.ms。它指定了多長時間之后,日志片段會被關閉。就像log.retention.bytes和log.retention.ms這兩個參數一樣,log.segment.bytes和log.segment.ms也可以共同使用,日志片段會在大小或時間達到上限時被關閉,就看哪個條件先滿足。默認log.segment.ms沒有默認值,所以只根據大小關閉。
在使用基于時間的日志片段時,要著重考慮并行關閉多個日志片段對磁盤性能的影響,如果多個分區的日志片段永遠不能達到大小上限,就會發生這種情況,因為broker在啟動之后就開始計算日志片段的過期時間,對于那些數據量小的分區來說,日志片段的關閉操作總是同時發生。
message.max.bytes
broker通過設置message.max.bytes參數限定單個消息的大小,默認值時1 000 000。也即是1M。如果生產者嘗試發送的消息超過這個大小,不僅消息不會被接收,還會收到broker返回的錯誤信息。跟其他與字節相關的配置參數一樣,該參數指定的時壓縮后的消息大小,也就是說,只要壓縮后消息小于message.max.bytes指定的值,消息的實際大小可以遠大于這個值。
這個值對性能有顯著的影響。值越大,負責處理網絡連接和請求的線程就需要花更多的時間處理。還會增加磁盤寫入塊的大小,從而影響IO吞吐量。
消費者客戶端設置的fetch.message.max.bytes必須與服務端設置的消息大小進行協調。如果這個值比message.max.bytes小,消費者就無法讀取比較大的消息,導致出現消費者被阻塞的情況。在為集群里的broker設置replica.fetch.max.bytes時,也遵循同樣原則。