《Kafka Stream》調研:一種輕量級流計算模式


原文鏈接:https://yq.aliyun.com/articles/58382

摘要: 流計算,已經有Storm、Spark,Samza,包括最近新起的Flink,Kafka為什么再自己做一套流計算呢?Kafka Stream 與這些框架比有什么優勢?Samza、Consumer Group已經包裝了Kafka輕量級的消費功能,難道不夠嗎?

Confluent Inc(原LinkedIn Kafka作者離職后創業公司)在6月份預告推出Kafka Stream,Kafka Stream會在Kafka 0.10版本中推出。

對於流計算,已經有Storm、Spark,Samza,包括最近新起的Flink,Kafka為什么再自己做一套流計算呢?Kafka Stream 與這些框架比有什么優勢?Samza、Consumer Group已經包裝了Kafka輕量級的消費功能,難道不夠嗎?

花了一些時間閱讀docs 和一些PPT,寫一份粗略的調研材料供大家參考。

什么是流計算?流是計算的一個連續計算類型

  1. Single:例如HTTP,發送一個Request請求、返回一個Response
    96b770e1f8751b7c54e9f8e029cc1294441329e0

  2. Batch:將一組作業提交給計算機,返回一組,優勢是減少IO等待時間
    74aa27c2393cf6a0a3d4b90715c37c6f6f63bd51

  3. Stream:Batch異步過程,任務和任務之間沒有明顯的邊界
    cd7efbb59df6617b5ef3a4470c7717577af99b6e

流計算一般有哪些方式?

DIY 簡單實現

以wordcount來作例子,我們可以啟動一個server,內存中建立一個HashMap,把輸入先分詞,然后根據word視圖更新HashMap。是不是很簡單?但帶來的問題是什么?

  • 如果掛了,數據都被清空,數據重復怎么辦?
  • 如果數據量非常大,一塊內存放不下怎么辦?
  • 如果在多台機器上部署,如何保證分配策略和先后順序?

我們把這些問題做一個分類,主要有這樣幾個:

  • 保序處理
  • 規模和切片
  • 異常恢復
  • 狀態類計算(例如TopK,UV等)
  • 重新計算
  • 時間、窗口等相關問題
利用現有框架

比較成熟度的框架有:Apache Spark, Storm(我們公司開源Jstorm),  Flink, Samza 等。第三方有:Google’s DataFlow,AWS Lambda

現有框架的好處是什么?

強大計算能力,例如Spark Streaming上已經包含Graph Compute,MLLib等適合迭代計算庫,在特定場景中非常好用。

問題是什么?
  • 使用起來比較復雜,例如將業務邏輯遷移到完備的框架中,Spark RDD,Spout等。有一些工作試圖提供SQL等更易使用模式降低了開發門檻,但對於個性化ETL工作(大部分ETL其實是不需要重量級的流計算框架的)需要在SQL中寫UDF,流計算框架就退化為一個純粹的容器或沙箱。
  • 作者認為部署Storm,Spark等需要預留集群資源,對開發者也是一種負擔。

2849aa59f2a85b6653aa61979beecb44fd47f8b7

Kafka Stream定位是輕量級的流計算類庫,簡單體現在什么方面?

  • 所有功能放在Lib中實現,實現的程序不依賴單獨執行環境
    • 可以用Mesos,K8S,Yarn和Ladmda等獨立調度執行Binary,試想可以通過Lamdba+Kafka實現一個按需付費、並能彈性擴展的流計算系統,是不是很cool?
    • 可以在單集成、單線程、多線程進行支持
  • 在一個編程模型中支持Stateless,Stateful兩種類型計算
  • 編程模型比較簡潔,基於Kafka Consumer Lib,及Key-Affinity特性開發,代碼只要處理執行邏輯就可以,Failover和規模等問題由Kafka本身特性幫助解決

個人感覺Kafka Lib是Samza一個增強版(Samza也是Linkedin與Kafka深度集成的流計算框架),將來可以替換Samza,但無法撼動Spark、Flink等語義上比較高級的流計算系統地位,只能做一些輕量級流處理的場景(例如ETL,數據集成,清洗等)。

Kafka Stream 例子

先來看一個例子,通過Kafka Stream代碼開發:

4e4ef5b315958cd38f3804644153031b47aba859

這里面做了這樣幾件事情:

  1. 構建了Kafka中數據序列化/反序列化方式
  2. 構建了2個計算節點
    • 分詞(flatMapValues),並將結果根據Key來Map
    • Reduce(根據Key來計算結果)
  3. 將結果寫到Kafka一個結果Topic中(增量方式)

在2個結算節點中,使用了一個Kafka Topic將計算結果序列化、並反序列化。相當於Map-Reduce中Streamline。

這段程序可以執行在一個Thread中,也可以執行在N台機器上,主要歸結於Kafka Consumer Lib可以幫助對數據與計算解耦分離。

基本概念
Processor:Processor是一個基本的計算節點
public interface Processor<K, V> {
void process (K key, V Value);
void punctuate(long time stampe);
}
Stream: Processor 處理后后結果輸出

兩者的關系如圖:

7e5adb60e72b395819ab8c2f6464f6f1831d50fd

Kafka Stream如何解決流計算中6個問題:

保序(Ordering)

對Kafka而言,在一個Partition(Shard)下,數據是先進先出嚴格有序的,因此不是問題。
33517985dc7f1c125a8396794a6e7acfad95ff60

分區與規模(Partition & Scalability)

流計算規模取決於2個因素:數據是否能線性擴容、計算能否線性擴容。

數據

Kafka中的數據通過Partition方式划分,每個Partition嚴格有序,可以做到彈性伸縮(實際上目前版本中彈性伸縮是不完整的,Kafka在0.10版本中能提供完全彈性伸縮的能力)。

22e9049a01221be9570e038fbd5d8b81c6e2a4ac

計算

Kafka對於消費端提供Consumer Group功能,可以擴展消費Instance達到與Partition同樣的水平擴展能力,過程中保證一個消費Instance只能消費一個Partition。

6a6cfeb56b40d750a48943f3aedfdb358b935b4e

故障恢復(Fault Tolerance)

Kafka Consumer Group已實現了負載均衡,因此當有消費實例crash時也能保證迅速未完成的任務,過程中數據不丟,可能會重復(取決於消費checkpoint配合)

10eec1810a4e151c3205393278de446fd485659f

狀態處理(State)

這個問題相對比較復雜,在流計算場景中,分為兩類計算:

  • Stateless(無狀態):例如Filter,Map,Joins,這些只要數據流過一遍即可,不依賴於前后的狀態
  • Stateful(有狀態):主要是基於時間Aggregation,例如某段時間的TopK,UV等,當數據達到計算節點時需要根據內存中狀態計算出數值

Kafka Stream 提供了一個抽象概念KTable,KStream來解決狀態存儲和數據變化的問題,見下面的章節解釋。

重放(Reprocessing)

在了解了RedoLog和State后,重放這個概念並不難理解

9e0a1c63280a6e274fc113d29282c8e4cfa46802

基於時間窗口計算(Time, Windowsing)

時間是流計算的一個重要熟悉,因為在現實過程中數據采集往往並不是很完美的,歷史數據的到來會打斷我們對計算的假設。時間有兩個概念:

  • Event Time: 物理時間中的客觀時間,代表事件發生時的一刻
  • Processing Time: 實際處理的時間(到達服務器時間)

雖然Processing Time對處理比較容易,但因歷史數據的影響,采用Event Time更為准確。一個零售業中比較典型的場景是:統計每10分鍾內每個產品的銷量(或網站每個時間點UV、PV的統計)。銷售數據可能會從不同的渠道實時流入,因此我們必須依賴於銷售數據產生的時間點來作為窗口,而不是數據達到計算的點。

cd8728cd59d68c743a765d2105774692af0b7c68

Kafka Stream用一種比較簡單粗暴方式來解決這個問題,他會給每個windows一個狀態,這個狀態只是代表當前時刻的數值,當有新數據達到該窗口時,狀態就被改變了。對於windows based aggregation,Kafka Stream做法是:

Table (狀態數據) + Library = Stateful Service

Stream & Table

為了實現狀態的概念,Kafka 抽象了兩種實體Kstream, KTable

  • Stream 等同於數據庫中Change log
  • Table 等同於數據庫在一個時間點Snapshot,兩個不同的Snapshot之間通過1個或多個changelog造成

8ff4418ccc0f13f398a7649661b85cddbb552a11

假設有2個流,一個流是送貨,另外一個流是銷售,我們對着兩個流進行Join,獲得當前的庫存狀態:

shipment stream:

item ID
store code
count

10
CA
200

23
NY
50

23
CA
101

54
WA
1000

sale stream:

item ID
store code
count

10
CA
20

23
NY
10

當這兩個流中的記錄先后達到情況下,會影響庫存狀態,整個庫存的變化狀態如下:

5efbd77086183c9d98ae8d113d399b84fa93b6b6

我們把這兩個流放到Kafka Stream中,就會看到一個Processor節點中的狀態變化如下:

1a9d3b95bebdf05209790c763f27349e21b10664

基於狀態數據,我們可以在該節點定義處理的邏輯:

if (state.inventory[item].size < 10)
{
notify the manager;
}
else if (state.inventory[item] > 100)
{
on sale;
}

KTable,KStream可能比較抽象,KafkaStream包裝了high-level DSL,直接提供了filter, map, join等算子,當然如果有個性化需求可以使用更低抽象程度API來完成。

粗淺的看法

流計算場景中,是否會有兩個極端:復雜內存操作+迭代計算,輕量級數據加工與ETL。這兩個比例分別占據多少?在我們常用的ETL場景里,大部分其實是輕量級Filter,LookUP,Write Storage等操作,有時候我們為了對數據做加工,不得不借助一個執行容器去選擇流計算的框架。Docker,Lamdba可以解決這類問題,但需要有一定流計算的開發量。

我覺得對輕量級ETL場景,一個而理想的架構是Kafka Stream這樣的輕量級計算庫+Lamdba,這樣就能做到安全按需使用的流計算模式。

Kafka Stream有一些關鍵東西沒有解決,例如在join場景中,需要保證來源2個Topic數據Shard個數必須是一定的,因為本身做不到MapJoin等技術。在之前的版本中,也沒有提供EventTime等Meta字段。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM