天天看點

Kafka學習之路 (二)Kafka的架構一、Kafka的叢集架構二、Topics和Partition三、Producer消息路由四、Consumer Group五、Push vs Pull六、Kafka delivery guarantee

Kafka學習之路 (二)Kafka的架構一、Kafka的叢集架構二、Topics和Partition三、Producer消息路由四、Consumer Group五、Push vs Pull六、Kafka delivery guarantee

如上圖所示,一個典型的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在上司者失敗的情況下完成。

Topic在邏輯上可以被認為是一個queue,每條消費都必須指定它的Topic,可以簡單了解為必須指明把這條消息放進哪個queue裡。為了使得Kafka的吞吐率可以線性提高,實體上把Topic分成一個或多個Partition,每個Partition在實體上對應一個檔案夾,該檔案夾下存儲這個Partition的所有消息和索引檔案。建立一個topic時,同時可以指定分區數目,分區數越多,其吞吐量也越大,但是需要的資源也越多,同時也會導緻更高的不可用性,kafka在接收到生産者發送的消息之後,會根據均衡政策将消息存儲到不同的分區中。因為每條消息都被append到該Partition中,屬于順序寫磁盤,是以效率非常高(經驗證,順序寫磁盤效率比随機寫記憶體還要高,這是Kafka高吞吐率的一個很重要的保證)。

Kafka學習之路 (二)Kafka的架構一、Kafka的叢集架構二、Topics和Partition三、Producer消息路由四、Consumer Group五、Push vs Pull六、Kafka delivery guarantee

官方位址: http://kafka.apache.org/documentation/#connect_dynamicio

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

因為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發送消息到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 high level API時,同一Topic的一條消息隻能被同一個Consumer Group内的一個Consumer消費,但多個Consumer Group可同時消費這一消息。

Kafka學習之路 (二)Kafka的架構一、Kafka的叢集架構二、Topics和Partition三、Producer消息路由四、Consumer Group五、Push vs Pull六、Kafka delivery guarantee

這是Kafka用來實作一個Topic消息的廣播(發給所有的Consumer)和單點傳播(發給某一個Consumer)的手段。一個Topic可以對應多個Consumer Group。如果需要實作廣播,隻要每個Consumer有一個獨立的Group就可以了。要實作單點傳播隻要所有的Consumer在同一個Group裡。用Consumer Group還可以将Consumer進行自由的分組而不需要多次發送消息到不同的Topic。

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

作為一個消息系統,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可以自己控制消費方式——即可批量消費也可逐條消費,同時還能選擇不同的送出方式進而實作不同的傳輸語義。

有這麼幾種可能的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可以非常直接非常容易得使用這種方式。