Apache流計算框架詳細對比

原文apache

幾個月以前咱們在這裏討論過[](http://www.cakesolutions.net/teamblogs/introduction-into-distributed-real-time-stream-processing)目前對於這種日漸增長的分佈式流計算的需求的緣由。固然,目前也有不少的各式各樣的框架被用於處理這一些問題。如今咱們會在這篇文章中進行回顧,來討論下各類框架之間的類似點以及區別在哪裏,還有就是從個人角度分析的,推薦的適用的用戶場景。編程

如你所想,分佈式的流處理也就是一般意義上的持續處理、數據富集以及對於無界數據的分析過程的組合。它是一個相似於MapReduce這樣的通用計算模型,可是咱們但願它可以在毫秒級別或者秒級別完成響應。這些系統常常被有向非循環圖(Directed ACyclic Graphs,DAGs)來表示。安全

DAG主要功能便是用圖來表示鏈式的任務組合,而在流處理系統中,咱們便經常用DAG來描述一個流工做的拓撲。筆者本身是從Akka的Stream中的術語獲得了啓發。以下圖所示,數據流通過一系列的處理器從源點流動到了終點,也就是用來描述這流工做。談到Akka的Streams,我以爲要着重強調下分佈式這個概念,由於即便也有一些單機的解決方案能夠建立而且運行DAG,可是咱們仍然着眼於那些能夠運行在多機上的解決方案。網絡

Points of Interest

在不一樣的系統之間進行選擇的時候,咱們主要關注到如下幾點。架構

  • Runtime and Programming model(運行與編程模型)負載均衡

一個平臺提供的編程模型每每會決定不少它的特性,而且這個編程模型應該足夠處理全部可能的用戶案例。這是一個決定性的因素,我也會在下文中屢次討論。框架

  • Functional Primitives(函數式單元)運維

一個合格的處理平臺應該可以提供豐富的可以在獨立信息級別進行處理的函數,像map、filter這樣易於實現與擴展的一些函數。一樣也應提供像aggregation這樣的跨信息處理函數以及像join這樣的跨流進行操做的函數,雖然這樣的操做會難以擴展。dom

  • State Management(狀態管理)分佈式

大部分這些應用都有狀態性的邏輯處理過程,所以,框架自己應該容許開發者去維護、訪問以及更新這些狀態信息。

  • Message Delivery Guarantees(消息投遞的可達性保證)

通常來講,對於消息投遞而言,咱們有至多一次(at most once)、至少一次(at least once)以及剛好一次(exactly once)這三種方案。

  • at most once

At most once投遞保證每一個消息會被投遞0次或者1次,在這種機制下消息頗有可能會丟失。

  • at least once

At least once投遞保證了每一個消息會被默認投遞屢次,至少保證有一次被成功接收,信息可能有重複,可是不會丟失。

  • exactly once

exactly once意味着每一個消息對於接收者而言正好被接收一次,保證即不會丟失也不會重複。

  • Failures Handling

在一個流處理系統中,錯誤可能常常在不一樣的層級發生,譬如網絡分割、磁盤錯誤或者某個節點莫名其妙掛掉了。平臺要可以從這些故障中順利恢復,而且可以從最後一個正常的狀態繼續處理而不會損害結果。

除此以外,咱們也應該考慮到平臺的生態系統、社區的完備程度,以及是否易於開發或者是否易於運維等等。

RunTime and Programming Model

運行環境與編程模型多是某個系統的最重要的特性,由於它定義了整個系統的呈現特性、可能支持的操做以及將來的一些限制等等。所以,運行環境與編程模型就肯定了系統的能力與適用的用戶案例。目前,主要有兩種不一樣的方法來構建流處理系統,其中一個叫Native Streaming,意味着全部輸入的記錄或者事件都會根據它們進入的順序一個接着一個的處理。

另外一種方法叫作Micro-Batching。大量短的Batches會從輸入的記錄中建立出而後通過整個系統的處理,這些Batches會根據預設好的時間常量進行建立,一般是每隔幾秒建立一批。

兩種方法都有一些內在的優點與不足,首先來談談Native Streaming。好的一方面呢是Native Streaming的表現性會更好一點,由於它是直接處理輸入的流自己的,並無被一些不天然的抽象方法所限制住。同時,由於全部的記錄都是在輸入以後立馬被處理,這樣對於請求方而言響應的延遲就會優於那種Micro-Batching系統。處理這些,有狀態的操做符也會更容易被實現,咱們在下文中也會描述這個特色。不過Native Streaming系統每每吞吐量會比較低,而且由於它須要去持久化或者重放幾乎每一條請求,它的容錯的代價也會更高一些。而且負載均衡也是一個不可忽視的問題,舉例而言,咱們根據鍵對數據進行了分割而且想作進一步地處理。若是某些鍵對應的分區由於某些緣由須要更多地資源去處理,那麼這個分區每每就會變成整個系統的瓶頸。

而對於Micro-Batching而言,將流切分爲小的Batches不可避免地會下降整個系統的變現性,也就是可讀性。而一些相似於狀態管理的或者joins、splits這些操做也會更加難以實現,由於系統必須去處理整個Batch。另外,每一個Batch自己也將架構屬性與邏輯這兩個原本不該該被糅合在一塊兒的部分相鏈接了起來。而Micro-Batching的優點在於它的容錯與負載均衡會更加易於實現,它只要簡單地在某個節點上處理失敗以後轉發給另外一個節點便可。最後,值得一提的是,咱們能夠在Native Streaming的基礎上快速地構建Micro-Batching的系統。

而對於編程模型而言,又能夠分爲Compositional(組合式)與Declarative(聲明式)。組合式會提供一系列的基礎構件,相似於源讀取與操做符等等,開發人員須要將這些基礎構件組合在一塊兒而後造成一個指望的拓撲結構。新的構件每每能夠經過繼承與實現某個接口來建立。另外一方面,聲明式API中的操做符每每會被定義爲高階函數。聲明式編程模型容許咱們利用抽象類型和全部其餘的精選的材料來編寫函數式的代碼以及優化整個拓撲圖。同時,聲明式API也提供了一些開箱即用的高等級的相似於窗口管理、狀態管理這樣的操做符。下文中咱們也會提供一些代碼示例。

Apache Streaming Landscape

目前已經有了各類各樣的流處理框架,天然也沒法在本文中所有攘括。因此我必須將討論限定在某些範圍內,本文中是選擇了全部Apache旗下的流處理的框架進行討論,而且這些框架都已經提供了Scala的語法接口。主要的話就是Storm以及它的一個改進Trident Storm,還有就是當下正火的Spark。最後還會討論下來自LinkedIn的Samza以及比較有但願的Apache Flink。筆者我的以爲這是一個很是不錯的選擇,由於雖然這些框架都是出於流處理的範疇,可是他們的實現手段千差萬別。

  • Apache Storm 最初由Nathan Marz以及他的BackType的團隊在2010年建立。後來它被Twitter收購而且開源出來,而且在2014年變成了Apache的頂層項目。毫無疑問,Storm是大規模流處理中的先行者而且逐漸成爲了行業標準。Storm是一個典型的Native Streaming系統而且提供了大量底層的操做接口。另外,Storm使用了Thrift來進行拓撲的定義,而且提供了大量其餘語言的接口。

  • Trident 是一個基於Storm構建的上層的Micro-Batching系統,它簡化了Storm的拓撲構建過程而且提供了相似於窗口、聚合以及狀態管理等等沒有被Storm原生支持的功能。另外,Storm是實現了至多一次的投遞原則,而Trident實現了恰巧一次的投遞原則。Trident 提供了 Java, Clojure 以及 Scala 接口。

  • 衆所周知,Spark是一個很是流行的提供了相似於SparkSQL、Mlib這樣內建的批處理框架的庫,而且它也提供了Spark Streaming這樣優秀地流處理框架。Spark的運行環境提供了批處理功能,所以,Spark Streaming毫無疑問是實現了Micro-Batching機制。輸入的數據流會被接收者分割建立爲Micro-Batches,而後像其餘Spark任務同樣進行處理。Spark 提供了 Java, Python 以及 Scala 接口。

  • Samza最先是由LinkedIn提出的與Kafka協同工做的優秀地流解決方案,Samza已是LinkedIn內部關鍵的基礎設施之一。Samza重負依賴於Kafaka的基於日誌的機制,兩者結合地很是好。Samza提供了Compositional接口,而且也支持Scala。

  • 最後聊聊Flink. Flink可謂一個很是老的項目了,最先在2008年就啓動了,不過目前正在吸引愈來愈多的關注。Flink也是一個Native Streaming的系統,而且提供了大量高級別的API。Flink也像Spark同樣提供了批處理的功能,能夠做爲流處理的一個特殊案例來看。Flink強調萬物皆流,這是一個絕對的更好地抽象,畢竟確實是這樣。

下表就簡單列舉了上述幾個框架之間的特性:

Counting Words

Wordcount就比如流處理領域的HelloWorld,它可以很好地描述不一樣框架間的差別性。首先看看Storm是如何編寫WordCount程序的:

TopologyBuilder builder = new TopologyBuilder();
 builder.setSpout("spout", new RandomSentenceSpout(), 5);
 builder.setBolt("split", new Split(), 8).shuffleGrouping("spout");
 builder.setBolt("count", new WordCount(), 12).fieldsGrouping("split", new Fields("word"));

 ...

 Map<String, Integer> counts = new HashMap<String, Integer>();

 public void execute(Tuple tuple, BasicOutputCollector collector) {
   String word = tuple.getString(0);
   Integer count = counts.containsKey(word) ? counts.get(word) + 1 : 1;
   counts.put(word, count);
   collector.emit(new Values(word, count));
 }

首先來看看它的拓撲定義,在第2行那邊是定義了一個Spout,也就是一個輸入源。而後定義了一個Bold,也就是一個處理的組件,用於將某個句子分割成詞序列。而後還定義了另外一個Bolt用來負責真實的詞計算。5,8到12行省略的過程用於定義集羣中使用了多少個線程來供每個組件使用。如你所見,全部的定義都是比較底層的與手動的。接下來繼續看看這個8-15行,也就是真正用於WordCount的部分代碼。由於Storm沒有內建的狀態處理的支持,因此我必須自定義這樣一個本地狀態,和理想的相差甚遠啊。下面咱們繼續看看Trident。

正如我上文中說起的,Trident是一個基於Storm的Micro-Batching的擴展,它提供了狀態管理等等功能。

public static StormTopology buildTopology(LocalDRPC drpc) {
 FixedBatchSpout spout = ...

 TridentTopology topology = new TridentTopology();
 TridentState wordCounts = topology.newStream("spout1", spout)
 .each(new Fields("sentence"),new Split(), new Fields("word"))
 .groupBy(new Fields("word"))
 .persistentAggregate(new MemoryMapState.Factory(), 
 new Count(), new Fields("count"));

 ...

 }

從代碼中就能夠看出,在Trident中就可使用一些上層的譬如eachgroupBy這樣的操做符,而且能夠在Trident中內建的進行狀態管理了。接下來咱們再看看Spark提供的聲明式的接口,要記住,與前幾個例子不一樣的是,基於Spark的代碼已經至關簡化了,下面基本上就是要用到的所有的代碼了:

val conf = new SparkConf().setAppName("wordcount")
val ssc = new StreamingContext(conf, Seconds(1))

val text = ...

val counts = text.flatMap(line => line.split(" "))
 .map(word => (word, 1))
 .reduceByKey(_ + _)

counts.print()

ssc.start()
ssc.awaitTermination()

每一個Spark的流任務都須要一個StreamingContext用來指定整個流處理的入口。StreamingContext定義了Batch的間隔,上面是設置到了1秒。在6-8行便是所有的詞統計的計算過程,很是不同啊。下面再看看Apache Samza,另外一個表明性的組合式的API:

class WordCountTask extends StreamTask {

  override def process(envelope: IncomingMessageEnvelope, collector: MessageCollector, 
    coordinator: TaskCoordinator) {

    val text = envelope.getMessage.asInstanceOf[String]

    val counts = text.split(" ").foldLeft(Map.empty[String, Int]) {
      (count, word) => count + (word -> (count.getOrElse(word, 0) + 1))
    }

    collector.send(new OutgoingMessageEnvelope(new SystemStream("kafka", "wordcount"), counts))

 }

Topology定義在了Samza的屬性配置文件裏,爲了明晰起見,這裏沒有列出來。下面再看看Fink,能夠看出它的接口風格很是相似於Spark Streaming,不過咱們沒有設置時間間隔:

val env = ExecutionEnvironment.getExecutionEnvironment

 val text = env.fromElements(...)
 val counts = text.flatMap ( _.split(" ") )
   .map ( (_, 1) )
   .groupBy(0)
   .sum(1)

 counts.print()

 env.execute("wordcount")

Fault Tolerance

與批處理系統相比,流處理系統中的容錯機制當然的會比批處理中的要難一點。在批處理系統中,若是碰到了什麼錯誤,只要將計算中與該部分錯誤關聯的從新啓動就行了。不過在流計算的場景下,容錯處理會更加困難,由於會不斷地有數據進來,而且有些任務可能須要7*24地運行着。另外一個咱們碰到的挑戰就是如何保證狀態的一致性,在天天結束的時候咱們會開始事件重放,固然不可能全部的狀態操做都會保證冪等性。下面咱們就看看其餘的系統是怎麼處理的:

Storm

Storm使用了所謂的逆流備份與記錄確認的機制來保證消息會在某個錯誤以後被從新處理。記錄確認這一個操做工做以下:一個操做器會在處理完成一個記錄以後向它的上游發送一個確認消息。而一個拓撲的源會保存有全部其建立好的記錄的備份。一旦受到了從Sinks發來的包含有全部記錄的確認消息,就會把這些確認消息安全地刪除掉。當發生錯誤時,若是尚未接收到所有的確認消息,就會從拓撲的源開始重放這些記錄。這就確保了沒有數據丟失,不過會致使重複的Records處理過程,這就屬於At-Least投送原則。

Storm用一套很是巧妙的機制來保證了只用不多的字節就能保存而且追蹤確認消息,可是並無太多關注於這套機制的性能,從而使得Storm有較低地吞吐量,而且在流控制上存在一些問題,譬如這種確認機制每每在存在背壓的時候錯誤地認爲發生了故障。

Spark Streaming

Spark Streaming以及它的Micro-Batching機制則使用了另外一套方案,道理很簡單,Spark將Micro-Batches分配到多個節點運行,每一個Micro-Batch能夠成功運行或者發生故障,當發生故障時,那個對應的Micro-Batch只要簡單地從新計算便可,由於它是持久化而且無狀態的,因此要保證Exactly-Once這種投遞方式也是很簡單的。

Samza

Samza的實現手段又不同了,它利用了一套可靠地、基於Offset的消息系統,在不少狀況下指的就是Kafka。Samza會監控每一個任務的偏移量,而後在接收到消息的時候修正這些偏移量。Offset能夠是存儲在持久化介質中的一個檢查點,而後在發生故障時能夠進行恢復。不過問題在於你並不知道恢復到上一個CheckPoint以後到底哪一個消息是處理過的,有時候會致使某些消息屢次處理,這也是At-Least的投遞原則。

Flink

Flink主要是基於分佈式快照,每一個快照會保存流任務的狀態。鏈路中運送着大量的CheckPoint Barrier(檢查點障礙,就是分隔符、標識器之類的),當這些Barrier到達某個Operator的時候,Operator將自身的檢查點與流相關聯。與Storm相比,這種方式會更加高效,畢竟不用對每一個Record進行確認操做。不過要注意的是,Flink仍是Native Streaming,概念上和Spark仍是相去甚遠的。Flink也是達成了Exactly-Once投遞原則。

Managing State

大部分重要的流處理應用都會保有狀態,與無狀態的操做符相比,這些應用中須要一個輸入和一個狀態變量,而後進行處理最終輸出一個改變了的狀態。咱們須要去管理、存儲這些狀態,要保證在發生故障的時候可以重現這些狀態。狀態的重造可能會比較困難,畢竟上面提到的很多框架都不能保證Exactly-Once,有些Record可能被重放屢次。

Storm

Storm是實踐了At-Least投遞原則,而怎麼利用Trident來保證Exactly-Once呢?概念上仍是很簡單的,只須要使用事務進行提交Records,不過很明顯這種方式及其低效。因此呢,仍是能夠構建一些小的Batches,而且進行一些優化。Trident是提供了一些抽象的接口來保證明現Exactly-Once,以下圖所示,還有不少東西等着你去挖掘。

Spark Streaming

當想要在流處理系統中實現有狀態的操做時,咱們每每想到的是一個長時間運行的Operator,而後輸入一個狀態以及一系列的Records。不過Spark Streaming是以另一種方式進行處理的,Spark Streaming將狀態做爲一個單獨地Micro-Batching流進行處理,因此在對每一個小的Micro-Spark任務進行處理時會輸入一個當前的狀態和一個表明當前操做的函數,最後輸出一個通過處理的Micro-Batch以及一個更新好的狀態。

Samza

Samza的處理方式更加簡單明瞭,就是把它們放到Kafka中,而後問題就解決了。Samza提供了真正意義上的有狀態的Operators,這樣每一個任務都能保有狀態,而後全部狀態的變化都會被提交到Kafka中。在有須要的狀況下某個狀態能夠很方便地從Kafka的Topic中完成重造。爲了提升效率,Samza容許使用插件化的鍵值本地存儲來避免全部的消息所有提交到Kafka。這種思路以下圖所示,不過Samza只是提升了At-Least這種機制,將來可能會提供Exactly-Once。

Flink

Flink提供了相似於Samza的有狀態的Operator的概念,在Flink中,咱們可使用兩種不一樣的狀態。第一種是本地的或者叫作任務狀態,它是某個特定的Operator實例的當前狀態,而且這種狀態不會與其餘進行交互。另外一種呢就是維護了整個分區的狀態。

Counting Words with State

Trident

public static StormTopology buildTopology(LocalDRPC drpc) {
   FixedBatchSpout spout = ...

   TridentTopology topology = new TridentTopology();
  
   TridentState wordCounts = topology.newStream("spout1", spout)
     .each(new Fields("sentence"),new Split(), new Fields("word"))
     .groupBy(new Fields("word"))
     .persistentAggregate(new MemoryMapState.Factory(), new Count(), new Fields("count"));

 ...

 }

在第9行中,咱們能夠經過調用一個持久化的聚合函數來建立一個狀態。

Spark Streaming

// Initial RDD input to updateStateByKey
val initialRDD = ssc.sparkContext.parallelize(List.empty[(String, Int)])

val lines = ...
val words = lines.flatMap(_.split(" "))
val wordDstream = words.map(x => (x, 1))

val trackStateFunc = (batchTime: Time, word: String, one: Option[Int], 
  state: State[Int]) => {
    val sum = one.getOrElse(0) + state.getOption.getOrElse(0)
    val output = (word, sum)
    state.update(sum)
    Some(output)
  }

val stateDstream = wordDstream.trackStateByKey(
  StateSpec.function(trackStateFunc).initialState(initialRDD))

在第2行中,咱們建立了一個RDD用來保存初始狀態。而後在5,6行中進行一些轉換,接下來能夠看出,在8-14行中,咱們定義了具體的轉換方程,即輸入時一個單詞、它的統計數量和它的當前狀態。函數用來計算、更新狀態以及返回結果,最後咱們將全部的Bits一塊兒聚合。

Samza

class WordCountTask extends StreamTask with InitableTask {

  private var store: CountStore = _

  def init(config: Config, context: TaskContext) {
    this.store = context.getStore("wordcount-store")
      .asInstanceOf[KeyValueStore[String, Integer]]
  }

 override def process(envelope: IncomingMessageEnvelope,
   collector: MessageCollector, coordinator: TaskCoordinator) {

   val words = envelope.getMessage.asInstanceOf[String].split(" ")

   words.foreach { key =>
     val count: Integer = Option(store.get(key)).getOrElse(0)
     store.put(key, count + 1)
     collector.send(new OutgoingMessageEnvelope(new SystemStream("kafka", "wordcount"), 
       (key, count)))
   }
 }

在上述代碼中第3行定義了全局的狀態,這裏是使用了鍵值存儲方式,而且在5~6行中定義瞭如何初始化。而後,在整個計算過程當中咱們都使用了該狀態。

Flink

val env = ExecutionEnvironment.getExecutionEnvironment

val text = env.fromElements(...)
val words = text.flatMap ( _.split(" ") )

words.keyBy(x => x).mapWithState {
  (word, count: Option[Int]) =>
    {
      val newCount = count.getOrElse(0) + 1
      val output = (word, newCount)
      (output, Some(newCount))
    }
}

在第6行中使用了mapWithState函數,第一個參數是即將須要處理的單次,第二個參數是一個全局的狀態。

Performance

合理的性能比較也是本文的一個重要主題之一。不一樣的系統的解決方案差別很大,所以也是很難設置一個無偏的測試。一般而言,在一個流處理系統中,咱們常說的性能就是指延遲與吞吐量。這取決於不少的變量,可是整體而言標準爲若是單節點每秒能處理500K的Records就是個合格的,若是能達到100萬次以上就已經不錯了。每一個節點通常就是指24核附帶上24或者48GB的內存。
對於延遲而言,若是是Micro-Batch的話每每但願能在秒級別處理。若是是Native Streaming的話,但願能有百倍的減小,調優以後的Storm能夠很輕易達到幾十毫秒。
另外一方面,消息的可達性保證、容錯以及狀態管理都是須要考慮進去的。譬如若是你開啓了容錯機制,那麼會增長10%到15%的額外消耗。除此以外,以文章中兩個WordCount爲例,第一個是無狀態的WordCount,第二個是有狀態的WordCount,後者在Flink中可能會有25%額外的消耗,而在Spark中可能有50%的額外消耗。固然,咱們確定能夠經過調優來減小這種損耗,而且不一樣的系統都提供了不少的可調優的選項。
還有就是必定要記住,在分佈式環境下進行大數據傳輸也是一件很是昂貴的消耗,所以咱們要利用好數據本地化以及整個應用的序列化的調優。

Project Maturity(項目成熟度)

在爲你的應用選擇一個合適的框架的時候,框架自己的成熟度與社區的完備度也是一個不可忽略的部分。Storm是第一個正式提出的流處理框架,它已經成爲了業界的標準而且被應用到了像Twitter、Yahoo、Spotify等等不少公司的生產環境下。Spark則是目前最流行的Scala的庫之一,而且Spark正逐步被更多的人採納,它已經成功應用在了像Netflix、Cisco、DataStax、Indel、IBM等等不少公司內。而Samza最先由LinkedIn提出,而且正在運行在幾十個公司內。Flink則是一個正在開發中的項目,不過我相信它發展的會很是迅速。

Summary

在咱們進最後的框架推薦以前,咱們再看一下上面那張圖:

Framework Recommendations

這個問題的回答呢,也很俗套,具體狀況具體分析。總的來講,你首先呢要仔細評估下你應用的需求而且徹底理解各個框架之間的優劣比較。同時我建議是使用一個提供了上層接口的框架,這樣會更加的開發友好,而且可以更快地投入生產環境。不過別忘了,絕大部分流應用都是有狀態的,所以狀態管理也是不可忽略地一個部分。同時,我也是推薦那些遵循Exactly-Once原則的框架,這樣也會讓開發和維護更加簡單。不過不能教條主義,畢竟仍是有不少應用會須要At-Least-Once與At-Most-Once這些投遞模式的。最後,必定要保證你的系統能夠在故障狀況下很快恢復,可使用Chaos Monkey或者其餘相似的工具進行測試。在咱們以前的討論中也發現這個快速恢復的能力相當重要。

  • 對於小型與須要快速響應地項目,Storm依舊是一個很是好的選擇,特別是在你很是關注延遲度的狀況下。不過仍是要謹記容錯機制和Trident的狀態管理會嚴重影響性能。Twitter目前正在設計新的流計算系統Heron用來替代Storm,它能夠在單個項目中有很好地表現。不過Twitter可不必定會開源它。

  • 對於Spark Streaming而言,若是你的系統的基礎架構中已經使用了Spark,那仍是很推薦你試試的。另外一方面,若是你想使用Lambda架構,那Spark也是個不錯的選擇。不過你必定要記住,Micro-Batching自己的限制和延遲對於你而言不是一個關鍵因素。

  • 若是你想用Samza的話,那最好Kafka已是你的基礎設施的一員了。雖然在Samza中Kafka只是個可插拔的組件,不過基本上全部人都會使用Kafka。正如上文所說,Samza提供了強大的本地存儲功能,可以輕鬆管理數十G的狀態數據。不過它的At-Least-Once的投遞限制也是很大一個瓶頸。

  • Flink目前在概念上是一個很是優秀的流處理系統,它可以知足大部分的用戶場景而且提供了不少先進的功能,譬如窗口管理或者時間控制。因此當你發現你須要的功能在Spark當中沒法很好地實現的時候,你能夠考慮下Flink。另外,Flink也提供了很好地通用的批處理的接口,只不過你須要很大的勇氣來將你的項目結合到Flink中,而且別忘了多關注關注它的路線圖。

Dataflow與開源

我最後一個要提到的就是Dataflow和它的開源計劃。Dataflow是Google雲平臺的一個組成部分,是目前在Google內部提供了統一的用於批處理與流計算的服務接口。譬如用於批處理的MapReduce,用於編程模型定義的FlumeJava以及用於流計算的MillWheel。Google最近打算開源這貨的SDK了,Spark與Flink均可以成爲它的一個運行驅動。

Conclusion

本文咱們過了一遍經常使用的流計算框架,它們的特性與優劣對比,但願能對你有用吧。



相關文章
相關標籤/搜索