1. 程式人生 > 遊戲 >Steam測試版遊戲安裝頁面可顯示所需儲存空間

Steam測試版遊戲安裝頁面可顯示所需儲存空間

架構

一個典型的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訂閱並消費訊息。

Topics和Partition

Topic在邏輯上可以被認為是一個queue

,每條消費都必須指定它的Topic,可以簡單理解為必須指明把這條訊息放進哪個queue裡。為了使得Kafka的吞吐率可以線性提高,物理上把Topic分成一個或多個Partition,每個Partition在物理上對應一個資料夾,該資料夾下儲存這個Partition的所有訊息和索引檔案。建立一個topic時,同時可以指定分割槽數目,分割槽數越多,其吞吐量也越大,但是需要的資源也越多,同時也會導致更高的不可用性,kafka在接收到生產者傳送的訊息之後,會根據均衡策略將訊息儲存到不同的分割槽中。因為每條訊息都被append到該Partition中,屬於順序寫磁碟,因此效率非常高(順序寫磁碟效率比隨機寫記憶體還要高,這是Kafka高吞吐率的一個很重要的保證,關於
磁碟順序寫入
)。

對於傳統的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設成一個較小的值,重新消費一些訊息。因為offset由Consumer控制,所以Kafka broker是無狀態的

,它不需要標記哪些訊息被哪些消費過,也不需要通過broker去保證同一個Consumer Group只有一個Consumer能消費某一條訊息,因此也就不需要鎖機制,這也為Kafka的高吞吐率提供了有力保障

Kafka 中的訊息以主題為單位進行歸類,生產者負責將訊息傳送到特定的主題(傳送到 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用來實現一個Topic訊息的廣播(發給所有的Consumer)和單播(發給某一個Consumer)的手段。一個Topic可以對應多個Consumer Group。如果需要實現廣播,只要每個Consumer有一個獨立的Group就可以了。要實現單播只要所有的Consumer在同一個Group裡。

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

Push vs. Pull

作為一個訊息系統,Kafka遵循了傳統的方式,選擇由Producer向broker push訊息並由Consumer從broker 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可以非常直接非常容易得使用這種方式。

日誌

不考慮多副本的情況,一個分割槽對應一個日誌(Log)。為了防止 Log 過大,Kafka 又引入了日誌分段(LogSegment)的概念,將 Log 切分為多個 LogSegment,相當於一個巨型檔案被平均分配為多個相對較小的檔案,這樣也便於訊息的維護和清理。

Log 在物理上只以資料夾的形式儲存,而每個 LogSegment 對應於磁碟上的一個日誌檔案和兩個索引檔案,以及可能的其他檔案(比如以“.txnindex”為字尾的事務索引檔案)。下圖描繪了主題、分割槽、副本、Log 以及 LogSegment 之間的關係。