某網際網路大廠kafka最佳實踐
上手kafka已有2年的時間,我們的資料處理量也從最初的300g/day發展到今天的T量級在這個過程中也踩了不少坑,在這裡分享出來和大家共勉。
一、硬體考量
1.1、記憶體
不建議為kafka分配超過5g的heap,因為會消耗28-30g的檔案系統快取,而是考慮為kafka的讀寫預留充足的buffer。Buffer大小的快速計算方法是平均磁碟寫入數量的30倍。推薦使用64GB及以上記憶體的伺服器,低於32GB記憶體的機器可能會適得其反,導致不斷依賴堆積機器來應付需求增長。(我們在生產環境使用的機器是64G記憶體的,但也看到LinkedIn用了大量28G記憶體的伺服器。)
1.2、CPU
kafka不算是CPU消耗型服務,在主頻和CPU核數之間,選擇後者,將會獲得多核帶來的更好的併發處理效能。
1.3、磁碟
毋庸置疑,RAID是優先推薦的,它在底層實現了物理磁碟的負載均衡和冗餘,雖然會犧牲一些磁碟空間和寫入效能。更進一步,我們推薦在配置中使用多目錄,每個目錄掛在在不同的磁碟(或者RAID)上。需要注意的是,NAS是不可取的,無論供應商如何吹噓他們的NAS的卓越效能。另外,我們經常看到使用者諮詢kafka是否一定要採用SSD,答案是沒有必要。
1.4網路
分散式系統中,網路的速度和可靠性異常重要,千兆甚至萬兆網路現在應該成為資料中心的標配。避免kafka叢集出現跨資料中心的情況,更要避免有很大的物理空間跨度,尤其中國還有詭異的聯通電信問題。kafka叢集中各個節點地位均等,一旦因為延時導致分散式叢集不穩定,排錯尤其困難。
1.5、檔案系統
ext4是最佳選擇。
1.6、其它
在硬體越來越強大和雲端計算如火如荼的今天,你需要在超高配置的伺服器和IaaS供應商的數百個虛擬機器之間做取捨。我們建議使用中等配置的伺服器,因為叢集規模越大,單臺超高配置的伺服器執行的節點越多,叢集穩定性隨之下降,複雜度則會上升。
二、JVM的考慮
對於java應用,jvm和gc是繞不過去的坎。實踐表明,官方JDK1.7u51會是一個不錯的選擇,配合以G1來做垃圾回收。推薦配置可參考:
-Xms4g -Xmx4g -XX:PermSize=48m -XX:MaxPermSize=48m -XX:+UseG1GC-XX:MaxGCPauseMillis=20 -XX:InitiatingHeapOccupancyPercent=35
注:kafka版本更新會帶來變化。請隨時關注。我們現在的版本是kafka_2.11-0.8.2.1
三、File descriptors
kafka會使用大量檔案和網路socket,所以,我們需要把file descriptors的預設配置改為100000。修改方法如下:
#vi /etc/sysctl.conf
fs.file-max = 32000
#vi /etc/security/limits.conf
yourusersoftnofile10000
youruserhardnofile30000
四、關鍵配置項解讀
儘管預設配置已經很不錯,但出於效能和實際叢集部署情況,我們還是需要講解一些重要的配置項。除此之外,如果對某個預設引數存在質疑,在詳細瞭解改引數的作用前,建議採用預設配置。
4.1、zookeeper.connect
必配引數,建議在kafka叢集的每天機器都配置所有zk。
4.2、broker.id
必配引數。叢集節點的標示符,不得重複。取值範圍0~n。
4.3、log.dirs
建議參照文章前面描述的磁碟配置,不要使用預設的“/tmp/kafka-logs”
4.4、advertised.host.name
註冊到zk供使用者使用的主機名。內網環境通常無需配置,而IaaS一般需要配置為公網地址。預設為“host.name”,可以通過java.net.InetAddress.getCanonicalHostName()介面獲取該值。
4.5、advertised.port
註冊到zk供使用者使用的服務埠,通常在IaaS環境需要額外配置。
4.6、num.partitions
自動建立topic的預設partition數量。預設是1,為了獲得更好的效能,建議修改為更大。最優取值參考後文。
4.7、default.replication.factor
自動建立topic的預設副本數量,官方建議修改為2;但通常一個副本就足夠了。
4.8、min.insync.replicas
ISR提交生成者請求的最小副本數。
4.9、unclean.leader.election.enable
是否允許不具備ISR資格的replicas選舉為leader作為不得已的措施,甚至不惜犧牲部分資料。預設允許。建議允許。資料異常重要的情況例外。
4.10、controlled.shutdown.enable
在kafka收到stop命令或者異常終止時,允許自動同步資料。建議開啟。
五、動態調整配置
大部分kafka配置是寫死在properties檔案裡的。然而,許多關於topic的引數我們可以動態調配,kafka-topic.sh工具提供了該功能,更改將一直生效直到伺服器重啟。可以調整的引數如下:
unclean.leader.election.enable:不嚴格的leader選舉,有助於叢集健壯,但是存在資料丟失風險。
min.insync.replicas:如果同步狀態的副本小於該值,伺服器將不再接受request.required.acks為-1或all的寫入請求。
max.message.bytes:單條訊息的最大長度。如果修改了該值,那麼replica.fetch.max.bytes和消費者的fetch.message.max.bytes也要跟著修改。
cleanup.policy:生命週期終結資料的處理,預設刪除。
flush.messages:強制重新整理寫入的最大快取訊息數。
flust.ms:強制重新整理寫入的最大等待時長。
還有segment.bytes、segment.ms、retention.bytes、retention.ms和segment.jitter.ms。詳見官方解釋。
六、效能優化技巧
6.1、配置合適的partitons數量。
這似乎是kafka新手必問得問題。首先,我們必須理解,partiton是kafka的並行單元。從producer和broker的視角看,向不同的partition寫入是完全並行的;而對於consumer,併發數完全取決於partition的數量,即,如果consumer數量大於partition數量,則必有consumer閒置。所以,我們可以認為kafka的吞吐與partition時線性關係。partition的數量要根據吞吐來推斷,假定p代表生產者寫入單個partition的最大吞吐,c代表消費者從單個partition消費的最大吞吐,我們的目標吞吐是t,那麼partition的數量應該是t/p和t/c中較大的那一個。實際情況中,p的影響因素有批處理的規模,壓縮演算法,確認機制和副本數等,然而,多次benchmark的結果表明,單個partition的最大寫入吞吐在10MB/sec左右;c的影響因素是邏輯演算法,需要在不同場景下實測得出。
這個結論似乎太書生氣和不實用。我們通常建議partition的數量一定要大於等於消費者的數量來實現最大併發。官方曾測試過1萬個partition的情況,所以不需要太擔心partition過多的問題。下面的知識會有助於讀者在生產環境做出最佳的選擇:
a、一個partition就是一個儲存kafka-log的目錄。
b、一個partition只能寄宿在一個broker上。
c、單個partition是可以實現訊息的順序寫入的。
d、單個partition只能被單個消費者程序消費,與該消費者所屬於的消費組無關。這樣做,有助於實現順序消費。
e、單個消費者程序可同時消費多個partition,即partition限制了消費端的併發能力。
f、partition越多則file和memory消耗越大,要在伺服器承受伺服器設定。
g、每個partition資訊都存在所有的zk節點中。
h、partition越多則失敗選舉耗時越長。
k、offset是對每個partition而言的,partition越多,查詢offset就越耗時。
i、partition的數量是可以動態增加的(只能加不能減)。
我們建議的做法是,如果是3個broker的叢集,有5個消費者,那麼建議partition的數量是15,也就是broker和consumer數量的最小公倍數。當然,也可以是一個大於消費者的broker數量的倍數,比如6或者9,還請讀者自行根據實際環境裁定。