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,寫一份粗略的調研材料供你們參考。windows
Single:例如HTTP,發送一個Request請求、返回一個Response安全
Batch:將一組做業提交給計算機,返回一組,優點是減小IO等待時間服務器
Stream:Batch異步過程,任務和任務之間沒有明顯的邊界多線程
以wordcount來做例子,咱們能夠啓動一個server,內存中創建一個HashMap,把輸入先分詞,而後根據word視圖更新HashMap。是否是很簡單?但帶來的問題是什麼?架構
若是掛了,數據都被清空,數據重複怎麼辦?
若是數據量很是大,一塊內存放不下怎麼辦?
若是在多臺機器上部署,如何保證分配策略和前後順序?
咱們把這些問題作一個分類,主要有這樣幾個:負載均衡
保序處理
規模和切片
異常恢復
狀態類計算(例如TopK,UV等)
從新計算
時間、窗口等相關問題框架
比較成熟度的框架有:Apache Spark, Storm(咱們公司開源Jstorm), Flink, Samza 等。第三方有:Google’s DataFlow,AWS Lambdaless
現有框架的好處是什麼?
強大計算能力,例如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中數據序列化/反序列化方式
構建了2個計算節點
分詞(flatMapValues),並將結果根據Key來Map
Reduce(根據Key來計算結果)
將結果寫到Kafka一個結果Topic中(增量方式)
在2個結算節點中,使用了一個Kafka Topic將計算結果序列化、並反序列化。至關於Map-Reduce中Streamline。
這段程序能夠執行在一個Thread中,也能夠執行在N臺機器上,主要歸結於Kafka Consumer Lib能夠幫助對數據與計算解耦分離。
Processor:Processor是一個基本的計算節點
void process (K key, V Value); void punctuate(long time stampe); }
Stream: Processor 處理後後結果輸出
二者的關係如圖:
對Kafka而言,在一個Partition(Shard)下,數據是先進先出嚴格有序的,所以不是問題。
流計算規模取決於2個因素:數據是否能線性擴容、計算可否線性擴容。
Kafka中的數據經過Partition方式劃分,每一個Partition嚴格有序,能夠作到彈性伸縮(實際上目前版本中彈性伸縮是不完整的,Kafka在0.10版本中能提供徹底彈性伸縮的能力)。
Kafka對於消費端提供Consumer Group功能,能夠擴展消費Instance達到與Partition一樣的水平擴展能力,過程當中保證一個消費Instance只能消費一個Partition。
Kafka Consumer Group已實現了負載均衡,所以當有消費實例crash時也能保證迅速未完成的任務,過程當中數據不丟,可能會重複(取決於消費checkpoint配合)
這個問題相對比較複雜,在流計算場景中,分爲兩類計算:
Stateless(無狀態):例如Filter,Map,Joins,這些只要數據流過一遍便可,不依賴於先後的狀態
Stateful(有狀態):主要是基於時間Aggregation,例如某段時間的TopK,UV等,當數據達到計算節點時須要根據內存中狀態計算出數值
Kafka Stream 提供了一個抽象概念KTable,KStream來解決狀態存儲和數據變化的問題,見下面的章節解釋。
在瞭解了RedoLog和State後,重放這個概念並不難理解
時間是流計算的一個重要熟悉,由於在現實過程當中數據採集每每並非很完美的,歷史數據的到來會打斷咱們對計算的假設。時間有兩個概念:
Event Time: 物理時間中的客觀時間,表明事件發生時的一刻
Processing Time: 實際處理的時間(到達服務器時間)
雖然Processing Time對處理比較容易,但因歷史數據的影響,採用Event Time更爲準確。一個零售業中比較典型的場景是:統計每10分鐘內每一個產品的銷量(或網站每一個時間點UV、PV的統計)。銷售數據可能會從不一樣的渠道實時流入,所以咱們必須依賴於銷售數據產生的時間點來做爲窗口,而不是數據達到計算的點。
Kafka Stream用一種比較簡單粗暴方式來解決這個問題,他會給每一個windows一個狀態,這個狀態只是表明當前時刻的數值,當有新數據達到該窗口時,狀態就被改變了。對於windows based aggregation,Kafka Stream作法是:
Table (狀態數據) + Library = Stateful Service
爲了實現狀態的概念,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字段。