Kafka學習之路 (二)Kafka的架構

一、Kafka的集羣架構

Kafka學習之路 (二)Kafka的架構
如上圖所示,一個典型的Kafka集羣中包含若干Producer(可以是web前端產生的Page View,或者是服務器日誌,系統CPU、Memory等),若干broker(Kafka支持水平擴展,一般broker數量越多,集羣吞吐率越高),若干Consumer Group,以及一個Zookeeper集羣。Kafka通過Zookeeper管理集羣配置,選舉leader,以及在Consumer Group發生變化時進行rebalance。Producer使用push模式將消息發佈到broker,Consumer使用pull模式從broker訂閱並消費消息。

ZooKeeper的作用
Apache Kafka的一個關鍵依賴是Apache Zookeeper,它是一個分佈式配置和同步服務。 Zookeeper是Kafka代理和消費者之間的協調接口。 Kafka服務器通過Zookeeper集羣共享信息。 Kafka在Zookeeper中存儲基本元數據,例如關於主題,代理,消費者偏移(隊列讀取器)等的信息。

由於所有關鍵信息存儲在Zookeeper中,並且它通常在其整體上覆制此數據,因此Kafka代理/ Zookeeper的故障不會影響Kafka集羣的狀態。 Kafka將恢復狀態,一旦Zookeeper重新啓動。 這爲Kafka帶來了零停機時間。 Kafka代理之間的領導者選舉也通過使用Zookeeper在領導者失敗的情況下完成。

二、Topics和Partition

Topic在邏輯上可以被認爲是一個queue,每條消費都必須指定它的Topic,可以簡單理解爲必須指明把這條消息放進哪個queue裏。爲了使得Kafka的吞吐率可以線性提高,物理上把Topic分成一個或多個Partition,每個Partition在物理上對應一個文件夾,該文件夾下存儲這個Partition的所有消息和索引文件。創建一個topic時,同時可以指定分區數目,分區數越多,其吞吐量也越大,但是需要的資源也越多,同時也會導致更高的不可用性,kafka在接收到生產者發送的消息之後,會根據均衡策略將消息存儲到不同的分區中。因爲每條消息都被append到該Partition中,屬於順序寫磁盤,因此效率非常高(經驗證,順序寫磁盤效率比隨機寫內存還要高,這是Kafka高吞吐率的一個很重要的保證)。
Kafka學習之路 (二)Kafka的架構
官方地址: http://kafka.apache.org/documentation/#connect_dynamicio

對於傳統的message queue而言,一般會刪除已經被消費的消息,而Kafka集羣會保留所有的消息,無論其被消費與否。當然,因爲磁盤限制,不可能永久保留所有數據(實際上也沒必要),因此Kafka提供兩種策略刪除舊數據。一是基於時間,二是基於Partition文件大小。例如可以通過配置$KAFKA_HOME/config/server.properties,讓Kafka刪除一週前的數據,也可在Partition文件超過1GB時刪除舊數據,配置如下所示:

[root@kafka ~]# cat /opt/kafka/config/server.properties
# The minimum age of a log file to be eligible for deletion due to age
log.retention.hours=168

# A size-based retention policy for logs. Segments are pruned from the log unless the remaining
# segments drop below log.retention.bytes. Functions independently of log.retention.hours.
#log.retention.bytes=1073741824

# The maximum size of a log segment file. When this size is reached a new log segment will be created.
log.segment.bytes=1073741824

# The interval at which log segments are checked to see if they can be deleted according
# to the retention policies
log.retention.check.interval.ms=300000

因爲Kafka讀取特定消息的時間複雜度爲O(1),即與文件大小無關,所以這裏刪除過期文件與提高Kafka性能無關。選擇怎樣的刪除策略只與磁盤以及具體的需求有關。另外,Kafka會爲每一個Consumer Group保留一些metadata信息——當前消費的消息的position,也即offset。這個offset由Consumer控制。正常情況下Consumer會在消費完一條消息後遞增該offset。當然,Consumer也可將offset設成一個較小的值,重新消費一些消息。因爲offet由Consumer控制,所以Kafka broker是無狀態的,它不需要標記哪些消息被哪些消費過,也不需要通過broker去保證同一個Consumer Group只有一個Consumer能消費某一條消息,因此也就不需要鎖機制,這也爲Kafka的高吞吐率提供了有力保障。

三、Producer消息路由

Producer發送消息到broker時,會根據Paritition機制選擇將其存儲到哪一個Partition。如果Partition機制設置合理,所有消息可以均勻分佈到不同的Partition裏,這樣就實現了負載均衡。如果一個Topic對應一個文件,那這個文件所在的機器I/O將會成爲這個Topic的性能瓶頸,而有了Partition後,不同的消息可以並行寫入不同broker的不同Partition裏,極大的提高了吞吐率。可以在$KAFKA_HOME/config/server.properties中通過配置項num.partitions來指定新建Topic的默認Partition數量,也可在創建Topic時通過參數指定,同時也可以在Topic創建之後通過Kafka提供的工具修改。

在發送一條消息時,可以指定這條消息的key,Producer根據這個key和Partition機制來判斷應該將這條消息發送到哪個Parition。Paritition機制可以通過指定Producer的paritition. class這一參數來指定,該class必須實現kafka.producer.Partitioner接口。

四、Consumer Group

使用Consumer high level API時,同一Topic的一條消息只能被同一個Consumer Group內的一個Consumer消費,但多個Consumer Group可同時消費這一消息。
Kafka學習之路 (二)Kafka的架構
這是Kafka用來實現一個Topic消息的廣播(發給所有的Consumer)和單播(發給某一個Consumer)的手段。一個Topic可以對應多個Consumer Group。如果需要實現廣播,只要每個Consumer有一個獨立的Group就可以了。要實現單播只要所有的Consumer在同一個Group裏。用Consumer Group還可以將Consumer進行自由的分組而不需要多次發送消息到不同的Topic。

實際上,Kafka的設計理念之一就是同時提供離線處理和實時處理。根據這一特性,可以使用Storm這種實時流處理系統對消息進行實時在線處理,同時使用Hadoop這種批處理系統進行離線處理,還可以同時將數據實時備份到另一個數據中心,只需要保證這三個操作所使用的Consumer屬於不同的Consumer Group即可。

五、Push vs Pull

作爲一個消息系統,Kafka遵循了傳統的方式,選擇由Producer向broker push消息並由Consumer從broker pull消息。一些logging-centric system,比如Facebook的Scribe和Cloudera的Flume,採用push模式。事實上,push模式和pull模式各有優劣。

push模式很難適應消費速率不同的消費者,因爲消息發送速率是由broker決定的。push模式的目標是儘可能以最快速度傳遞消息,但是這樣很容易造成Consumer來不及處理消息,典型的表現就是拒絕服務以及網絡擁塞。而pull模式則可以根據Consumer的消費能力以適當的速率消費消息。

對於Kafka而言,pull模式更合適。pull模式可簡化broker的設計,Consumer可自主控制消費消息的速率,同時Consumer可以自己控制消費方式——即可批量消費也可逐條消費,同時還能選擇不同的提交方式從而實現不同的傳輸語義。

六、Kafka delivery guarantee

有這麼幾種可能的delivery guarantee:

  • At most once   消息可能會丟,但絕不會重複傳輸
  • At least one    消息絕不會丟,但可能會重複傳輸
  • Exactly once   每條消息肯定會被傳輸一次且僅傳輸一次,很多時候這是用戶所想要的。

當Producer向broker發送消息時,一旦這條消息被commit,因數replication的存在,它就不會丟。但是如果Producer發送數據給broker後,遇到網絡問題而造成通信中斷,那Producer就無法判斷該條消息是否已經commit。雖然Kafka無法確定網絡故障期間發生了什麼,但是Producer可以生成一種類似於主鍵的東西,發生故障時冪等性的重試多次,這樣就做到了Exactly once。

接下來討論的是消息從broker到Consumer的delivery guarantee語義。(僅針對Kafka consumer high level API)。Consumer在從broker讀取消息後,可以選擇commit,該操作會在Zookeeper中保存該Consumer在該Partition中讀取的消息的offset。該Consumer下一次再讀該Partition時會從下一條開始讀取。如未commit,下一次讀取的開始位置會跟上一次commit之後的開始位置相同。當然可以將Consumer設置爲autocommit,即Consumer一旦讀到數據立即自動commit。如果只討論這一讀取消息的過程,那Kafka是確保了Exactly once。但實際使用中應用程序並非在Consumer讀取完數據就結束了,而是要進行進一步處理,而數據處理與commit的順序在很大程度上決定了消息從broker和consumer的delivery guarantee semantic。

Kafka默認保證At least once,並且允許通過設置Producer異步提交來實現At most once。而Exactly once要求與外部存儲系統協作,幸運的是Kafka提供的offset可以非常直接非常容易得使用這種方式。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章