原文鏈接: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,寫一份粗略的調研材料供大家參考。
什么是流計算?流是計算的一個連續計算類型
流計算一般有哪些方式?
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等需要預留集群資源,對開發者也是一種負擔。
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代碼開發:
這里面做了這樣幾件事情:
- 構建了Kafka中數據序列化/反序列化方式
- 構建了2個計算節點
- 分詞(flatMapValues),並將結果根據Key來Map
- Reduce(根據Key來計算結果)
- 將結果寫到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 處理后后結果輸出
兩者的關系如圖:
Kafka Stream如何解決流計算中6個問題:
保序(Ordering)
對Kafka而言,在一個Partition(Shard)下,數據是先進先出嚴格有序的,因此不是問題。
分區與規模(Partition & Scalability)
流計算規模取決於2個因素:數據是否能線性擴容、計算能否線性擴容。
數據
Kafka中的數據通過Partition方式划分,每個Partition嚴格有序,可以做到彈性伸縮(實際上目前版本中彈性伸縮是不完整的,Kafka在0.10版本中能提供完全彈性伸縮的能力)。
計算
Kafka對於消費端提供Consumer Group功能,可以擴展消費Instance達到與Partition同樣的水平擴展能力,過程中保證一個消費Instance只能消費一個Partition。
故障恢復(Fault Tolerance)
Kafka Consumer Group已實現了負載均衡,因此當有消費實例crash時也能保證迅速未完成的任務,過程中數據不丟,可能會重復(取決於消費checkpoint配合)
狀態處理(State)
這個問題相對比較復雜,在流計算場景中,分為兩類計算:
- Stateless(無狀態):例如Filter,Map,Joins,這些只要數據流過一遍即可,不依賴於前后的狀態
- Stateful(有狀態):主要是基於時間Aggregation,例如某段時間的TopK,UV等,當數據達到計算節點時需要根據內存中狀態計算出數值
Kafka Stream 提供了一個抽象概念KTable,KStream來解決狀態存儲和數據變化的問題,見下面的章節解釋。
重放(Reprocessing)
在了解了RedoLog和State后,重放這個概念並不難理解
基於時間窗口計算(Time, Windowsing)
時間是流計算的一個重要熟悉,因為在現實過程中數據采集往往並不是很完美的,歷史數據的到來會打斷我們對計算的假設。時間有兩個概念:
- Event Time: 物理時間中的客觀時間,代表事件發生時的一刻
- Processing Time: 實際處理的時間(到達服務器時間)
雖然Processing Time對處理比較容易,但因歷史數據的影響,采用Event Time更為准確。一個零售業中比較典型的場景是:統計每10分鍾內每個產品的銷量(或網站每個時間點UV、PV的統計)。銷售數據可能會從不同的渠道實時流入,因此我們必須依賴於銷售數據產生的時間點來作為窗口,而不是數據達到計算的點。
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造成
假設有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
當這兩個流中的記錄先后達到情況下,會影響庫存狀態,整個庫存的變化狀態如下:
我們把這兩個流放到Kafka Stream中,就會看到一個Processor節點中的狀態變化如下:
基於狀態數據,我們可以在該節點定義處理的邏輯:
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字段。