Twitter把Kafka當作存儲系統使用,使用kafka的公司Twitter把Kafka當作存儲系統使用當開發者通過API消費Twitter的公共數據時,他們需要獲得可靠性、速度和穩定性方面的保證。因此,在不久前,我們推出了Account Activity Replay API幫助開發者們提升他們系統的穩定性。這個A......
當開發者通過API消費Twitter的公共數據時,他們需要獲得可靠性、速度和穩定性方面的保證。因此,在不久前,我們推出了Account Activity Replay API幫助開發者們提升他們系統的穩定性。這個API是一個數據恢復工具,開發者可以用它來檢索最早發生在5天前的事件,恢復由于各種原因(包括在實時傳遞時突然發生的服務器中斷)沒有被傳遞的事件。
除了構建API來提升開發者體驗,我們還做了一些優化:
·提高Twitter內部工程師的生產力。
·保持系統的可維護性。具體來說,就是盡量減少開發人員、站點可靠性工程師和其他與系統交互的人員的上下文切換。
基于這些原因,在構建這個API所依賴的回放系統時,我們利用了Account Activity API現有的實時系統設計。這有助于我們重用現有的工作,并最小化上下文切換負擔和培訓工作。
實時系統采用了發布和訂閱架構。為了保持架構的一致性,構建一個可以讀取數據的存儲層,我們想到了傳統的流式技術——Kafka。
1
背景
兩個數據中心產生實時事件,事件被寫入到跨數據中心的主題上,實現數據冗余。
但并不是所有的事件都需要被傳遞,所以會有一個內部應用程序負責對事件進行篩選。這個應用程序消費來自這些主題的事件,根據保存在鍵值存儲中的一組規則來檢查每一個事件,并決定是否應該通過我們的公共API將消息傳遞給特定的開發者。事件是通過Webhook傳遞的,每個Webhook URL都有一個開發人員負責維護,并有唯一的ID標識。
圖1:數據生成管道
2
存儲和分區
通常,在構建一個需要存儲層的回放系統時,人們可能會使用基于Hadoop和HDFS的架構。但我們選擇了Kafka,主要基于以下兩個原因:
·已有的實時系統采用了發布和訂閱架構;
·回放系統存儲的事件量不是PB級的,我們存儲的數據不會超過幾天。此外,執行Hadoop的MapReduce作業比在Kafka上消費數據成本更高、速度更慢,達不到開發者的期望。
要利用實時管道來構建回放管道,首先要確保事件被存儲在Kafka中。我們把Kafka主題叫作deliverylog,每個數據中心都有一個這樣的主題。然后,這些主題被交叉復制,實現數據冗余,以便支持來自數據中心之外的重放請求。事件在被傳遞之前經過去重操作。
在這個Kafka主題上,我們使用默認的分區機制創建了多個分區,分區與WebhookId的散列值(事件記錄的鍵)一一對應。我們考慮過使用靜態分區,但最終決定不使用它,因為如果其中一個開發人員生成的事件多于其他開發人員,那么這個分區包含的數據將多于其他分區,造成了分區的不均衡。相反,我們選擇固定數量的分區,然后使用默認分區策略來分布數據,這樣就降低了分區不均衡的風險,并且不需要讀取Kafka主題的所有分區。重放服務基于請求的WebhookId來確定要讀取哪個分區,并為該分區啟動一個新的Kafka消費者。主題的分區數量不會發生變化,因為這會影響鍵的散列和事件的分布。
我們使用了固態磁盤,根據每個時間段讀取的事件數量來分配空間。我們選擇這種磁盤而不是傳統的硬盤驅動器,以此來獲得更快的處理速度,并減少與查找和訪問操作相關的開銷。因為我們需要訪問低頻數據,無法獲得頁面緩存優化的好處,所以最好是使用固態磁盤。
為了最小化存儲空間,我們使用了snappy壓縮算法。我們知道大部分處理工作都在消費端,之所以選擇snappy,是因為它在解壓時比其他Kafka所支持的壓縮算法(如gzip和lz4)更快。
3
請求和處理
在我們設計的這個系統中,通過API調用來發快遞重放請求。我們從請求消息體中獲取WebhookId和要重放的事件的日期范圍。這些請求被持久化到MySQL中,相當于進入了隊列,直到它們被重放服務讀取。請求中的日期范圍用于確定要讀取的分區的偏移量。消費者對象的offsetForTimes函數用于獲取偏移量。
圖2:重放系統接收請求,并將請求發快遞給配置服務(數據訪問層),然后被持久化到數據庫中。
重放服務處理每個重放請求,它們通過MySQL相互協調,處理數據庫中的下一個需要重放的記錄。重放進程定期輪詢MySQL,獲取需要被處理的掛起作業。一個請求會在各種狀態之間轉換。等待被處理的請求處于開放狀態(OPEN STATE),剛退出隊列的請求處于啟動狀態(STARTED STATE),正在被處理的請求處于進行中狀態(ONGOING STATE),已處理完成的請求將轉換到已完成狀態(COMPLETED STATE)。一個重放進程只會選擇一個尚未啟動的請求(即處于打開狀態的請求)。
每隔一段時間,當一個工作進程將一個請求退出隊列后,它會在MySQL表中寫入時間戳,表示正在處理當前的重放作業。如果重放進程在處理請求時死掉了,相應的作業將被重新啟動。因此,除了將處于打開狀態的請求退出隊列之外,重放操作還將讀取處于已開始或正在進行中的、在預定義的分鐘數內沒有心跳的作業。
圖3:數據傳遞層:重放服務通過輪詢MySQL來讀取作業,消費來自Kafka的消息,并通過Webhook服務傳遞事件。
在讀取事件時會進行去重操作,然后事件被發布到消費者端的Webhook URL上。去重是通過維護被讀取事件的散列值緩存來實現的。如果遇到具有相同散列值的事件,就不傳遞這個事件。
總的來說,我們的解決方案與傳統的將Kafka作為實時、流式系統的用法不一樣。我們成功地將Kafka作為存儲系統,構建了一個API,在進行事件恢復時提升了用戶體驗和數據訪問能力。我們利用已有的實時系統設計讓系統的維護變得更加容易。此外,客戶數據的恢復速度達到了我們的預期。
原文鏈接
https://blog.twitter.com/engineering/enus/topics/infrastructure/2020/kafkaasastoragesystem.html
特別聲明:以上文章內容僅代表作者本人觀點,不代表ESG跨境電商觀點或立場。如有關于作品內容、版權或其它問題請于作品發表后的30日內與ESG跨境電商聯系。
二維碼加載中...
使用微信掃一掃登錄
使用賬號密碼登錄
平臺顧問
微信掃一掃
馬上聯系在線顧問
小程序
ESG跨境小程序
手機入駐更便捷
返回頂部