目前為止,已經(jīng)討論了機(jī)器學(xué)習(xí)和批處理模式的數(shù)據(jù)挖掘。現(xiàn)在審視持續(xù)處理流數(shù)據(jù),實(shí)時(shí)檢測(cè)其中的事實(shí)和模式,好像從湖泊來到了河流。
先研究一下不斷改變的動(dòng)態(tài)環(huán)境帶來的挑戰(zhàn),在列出流處理應(yīng)用的先決條件(如,與Twitter的TCP Sockets)之后, 結(jié)合Spark, Kafka and Flume 把數(shù)據(jù)放入一個(gè)低延遲,高吞吐量,可縮放的處理流水線。
要點(diǎn)如下:
? 分析流式應(yīng)用架構(gòu)的挑戰(zhàn),約束和需求
? 利用Spark Streaming 從 TCP socket 中處理實(shí)時(shí)數(shù)據(jù)
? 連接 Twitter 服務(wù),準(zhǔn)實(shí)時(shí)解析 tweets
? 使用 Spark, Kafka 和 Flume 建立一個(gè)可靠,容錯(cuò),可縮放,高吞吐量,低延遲的集成應(yīng)用
? 以 Lambda 和 Kappa 的架構(gòu)范式結(jié)尾
Spark Streaming在數(shù)據(jù)密集型應(yīng)用中的位置
按照慣例, 先看一下最初的數(shù)據(jù)密集型應(yīng)用架構(gòu),指明我們所感興趣的 Spark Streaming 模塊的所處位置.
下圖著重指明了整體架構(gòu)中的Spark Streaming模塊,Spark SQL和 Spark MLlib:
數(shù)據(jù)流可以來自股票市場(chǎng)的時(shí)序分析,企業(yè)交易,各種交互,事件,web流量,點(diǎn)擊流,和傳感器數(shù)據(jù)等,都是及時(shí)且?guī)в袝r(shí)間戳的數(shù)據(jù)。用例有欺詐檢測(cè)和防偽,移動(dòng)的交叉銷售和銷售提升,或者交通預(yù)警。這些數(shù)據(jù)流需要及時(shí)處理以便于監(jiān)測(cè),例如 異常檢測(cè),異常奇點(diǎn),垃圾郵件,欺詐和入侵; 也可提供基礎(chǔ)的統(tǒng)計(jì),見解,趨勢(shì),和推薦。 某些情形,總結(jié)性的匯總信息需要存儲(chǔ)以備將來使用。從架構(gòu)范式的角度看,我們從面向服務(wù)的架構(gòu)轉(zhuǎn)為事件驅(qū)動(dòng)的架構(gòu)。
數(shù)據(jù)的流處理有兩個(gè)模型:
? 在數(shù)據(jù)到達(dá)時(shí)及時(shí)處理每條記錄,在處理前不需要在容器中緩存記錄,比如
Twitter's Storm, Yahoo's S4, 和 Google's MillWheel.
? 微型批處理 或者小間隔執(zhí)行批處理計(jì)算,例如
Spark Streaming and Storm Trident. 根據(jù)微型批處理設(shè)置的時(shí)間窗口,將流入的數(shù)據(jù)記錄緩沖到一個(gè)容器中。
Spark Streaming 經(jīng)常用來與 Storm 比較,這是兩種不同的流數(shù)據(jù)模型。 Spark Streaming 基于微型批處理,而 Storm 基于及時(shí)處理流入的數(shù)據(jù)。 Storm 也提供了微型批處理選項(xiàng) 即 Storm Triden。
流處理應(yīng)用的主要驅(qū)動(dòng)因素的時(shí)間延遲。時(shí)延從 RPC (short for Remote Procedure Call) 的毫秒 到幾秒 到微信批處理方案的幾分鐘不等。
RPC 允許請(qǐng)求程序與遠(yuǎn)程服務(wù)器之間的同步操作。線程允許多個(gè) RPC 調(diào)用的并發(fā)。
分布式RPC 模型實(shí)現(xiàn)的一個(gè)實(shí)例就是 Apache Storm.
Storm 實(shí)現(xiàn)了無邊界元組的無狀態(tài)毫秒級(jí)延遲處理,結(jié)合數(shù)據(jù)流作為噴發(fā)源使用了拓?fù)浠蚨ㄏ颦h(huán)圖的及時(shí),提供了過濾, join, 聚合和轉(zhuǎn)換.
Storm 也實(shí)現(xiàn)了一個(gè)高層抽象叫做 Trident , 與Spark類似, 以微型批處理進(jìn)行流式數(shù)據(jù)處理。
考慮時(shí)延的限制, 從毫秒到秒級(jí), Storm 是個(gè)很好的候選. 對(duì)于秒到分鐘級(jí), Spark Streaming and Storm Trident
是最佳選擇。 對(duì)于若干分鐘以上, Spark 和 NoSQL 數(shù)據(jù)庫如
Cassandra 和HBase 是合適的方案. 對(duì)于多小時(shí)運(yùn)算海量數(shù)據(jù),Hadoop 是理想的競(jìng)爭(zhēng)者。
盡管吞吐量逾時(shí)延相關(guān),但不是一個(gè)簡(jiǎn)單的反線性關(guān)系。
如果處理一條消息需要2 ms, 只取決于延遲的情況, 可以假設(shè)吞吐量限制在500條消息每秒。如果緩存了8ms的數(shù)據(jù),那么批處理允許更高的吞吐量。 10 ms 時(shí)延, 系統(tǒng)可以緩沖10,000 條消息. 為了一個(gè)可忍受的時(shí)延增長(zhǎng),我們要顯著的增加吞吐量。這是Spark Streaming 微型批處理的一個(gè)魔法。
Spark Streaming 內(nèi)部工作方式
Spark Streaming 充分利用了 Spark 的核心架構(gòu),作為流處理功能的入口點(diǎn),它構(gòu)建在 SparkContext 的 StreamingContext 之上. 集群管理器將至少單獨(dú)分片一個(gè)工作節(jié)點(diǎn)作為接收器,這是一個(gè)長(zhǎng)時(shí)間運(yùn)行的任務(wù)執(zhí)行器來處理進(jìn)入的流數(shù)據(jù)。
執(zhí)行器創(chuàng)建 Discretized Streams 或者從輸入數(shù)據(jù)流中得來的 DStreams, DStream 默認(rèn)為另一個(gè)worker 的緩存。 接收器服務(wù)于輸入數(shù)據(jù)流,多個(gè)接收器提升了并行性,產(chǎn)生多個(gè)DStreams , Spark 用它unite 或 join RDD.
下圖給出了 Spark Streaming 的那邊工作概要。客戶端通過集群管理器與
Spark 集群交互 ,Spark Streaming 有一個(gè)專屬的worker 運(yùn)行著長(zhǎng)任務(wù)來接待輸入的數(shù)據(jù)流,轉(zhuǎn)化為 discretized streams 或DStreams. 接收器采集,緩存,復(fù)制數(shù)據(jù),然后放入一個(gè) RDDs的流.
Spark 接收器能夠從多種數(shù)據(jù)源接收數(shù)據(jù)。核心的輸入源包括從TCP socket 和 HDFS/Amazon S3 到 Akka Actors. 其它的數(shù)據(jù)源包括
Apache Kafka, Apache Flume, Amazon Kinesis, ZeroMQ, Twitter, 以及其它用戶定義的接收器。
我們可以區(qū)分資源是否可靠,可靠的數(shù)據(jù)源可以確認(rèn)數(shù)據(jù)的接收憑證以及副本能否重發(fā),非可靠數(shù)據(jù)源指接收器不能確認(rèn)消息的接收憑證。 Spark 可以對(duì)workers,分區(qū)和接收器的數(shù)量進(jìn)行縮放。
下圖給出了Spark Streaming 可能的數(shù)據(jù)源和持久化選項(xiàng):
Spark Streaming 的底層基礎(chǔ)
Spark Streaming 由接收器, Discretized Streams 和 用于持久化的Spark Connectors 組成.
作為 Spark Core, 基本的數(shù)據(jù)結(jié)構(gòu)是 RDD, 基本編程抽是 Discretized Stream 或 DStream.
下圖解釋了看做 RDDs連續(xù)序列的 Discretized Streams. DStream 的批處理間隔是可配置的。
DStreams 是在批處理間隔是對(duì)流入數(shù)據(jù)的快照。時(shí)間步長(zhǎng)可以從500ms 到幾秒,DStream 的底層結(jié)構(gòu)是一個(gè) RDD.
一個(gè)DStream 基本上是 RDDs的一個(gè)連續(xù)序列。這使得Spark Streaming 非常強(qiáng)大,可以充分利用Spark Core 中已有的函數(shù),數(shù)據(jù)轉(zhuǎn)換和動(dòng)作, 可以與Spark SQL 對(duì)話, 在流入的數(shù)據(jù)流上執(zhí)行SQL 查詢和 Spark MLlib. 通用的及基于鍵值對(duì)RDDs的數(shù)據(jù)轉(zhuǎn)換同樣適用。DStreams 得益于 RDDs 內(nèi)部體系和容錯(cuò)機(jī)制。 discretized stream 有額外的轉(zhuǎn)換和輸出操作。DStream 的大多數(shù)通用操作是 transform 和 foreachRDD.
下圖給出了 DStreams的生命周期,將消息通過微型批處理實(shí)現(xiàn)創(chuàng)建成 RDDs ,RDDs上的數(shù)據(jù)轉(zhuǎn)換函數(shù)和動(dòng)作 觸發(fā)了 Spark jobs. 從上而下分布解釋:
- 輸入流中, 流入的消息緩存在容器中,時(shí)間窗口有微型批處理分配.
- 在 discretized stream 步驟中, 緩存的微型批處理轉(zhuǎn)化成 DStream RDDs.
- Mapped DStream 是通過使用轉(zhuǎn)換函數(shù)從原始的 DStream中獲得的. 這三個(gè)步驟構(gòu)成了在預(yù)定義時(shí)間窗口的所接收數(shù)據(jù)的轉(zhuǎn)換。 底層的數(shù)據(jù)結(jié)構(gòu)是RDD, 我們保留了轉(zhuǎn)換中的數(shù)據(jù)世系。
-
最后一步是 RDD上的一個(gè)動(dòng)作,觸發(fā)了Spark job.
5-5 Dstream
數(shù)據(jù)轉(zhuǎn)換可以無狀態(tài)的或者有狀態(tài)的。無狀態(tài)意味著沒有狀態(tài)需要程序維護(hù),而有狀態(tài)意味著程序保持狀態(tài),前面所記住的操作可能影響當(dāng)前的操作。一個(gè)有狀態(tài)操作需要或修改一些系統(tǒng)的狀態(tài),無狀態(tài)操作則不是。 無狀態(tài)數(shù)據(jù)轉(zhuǎn)換在一個(gè)時(shí)間點(diǎn)處理每個(gè)批處理中的一個(gè)
DStream. 有狀態(tài)數(shù)據(jù)轉(zhuǎn)換處理多個(gè)批處理來獲得結(jié)果,需要可配置的目錄檢查點(diǎn)。檢查點(diǎn)是容錯(cuò)體系的主要機(jī)制, Spark Streaming 周期性存儲(chǔ)一個(gè)應(yīng)用的數(shù)據(jù)和元數(shù)據(jù)。Spark Streaming 中的有狀態(tài)數(shù)據(jù)轉(zhuǎn)換有兩種類型:updateStateByKey 和 windowed transformations.
updateStateByKey 維護(hù)了在 Pair RDD 中一個(gè)流中的每個(gè)鍵值的狀態(tài)。當(dāng)DStream中的鍵值漲停被更新時(shí)返回一個(gè)新的狀態(tài)。 一個(gè)例子是tweets的stream 中運(yùn)行了given hashtags 的個(gè)數(shù)統(tǒng)計(jì)。Windowed transformations 在多個(gè)批處理的滑動(dòng)窗口中執(zhí)行。一個(gè)窗口有一個(gè)定義好的長(zhǎng)度或時(shí)間單元的區(qū)間,是多個(gè)單一
DStream 處理的間隔,定義了一個(gè)窗口轉(zhuǎn)換中有多少個(gè)批處理或窗口轉(zhuǎn)換中計(jì)算的頻率。
下面的 schema 在 DStreams上的窗口操作,在給定長(zhǎng)度和滑動(dòng)間隔時(shí)生成 window DStreams :
函數(shù)示例是 countByWindow (windowLength, slideInterval). 在 DStream上的一個(gè)滑動(dòng)窗口中,計(jì)算每個(gè)單元素RDD的元素個(gè)數(shù),返回一個(gè)新的DStream. 通過每60秒計(jì)算一次tweets流中指定的hashtags的數(shù)據(jù)作為示例的解釋。 窗口的時(shí)間幀是指定的。分鐘級(jí)的窗口長(zhǎng)度是有道理的。
由于是計(jì)算和內(nèi)存密集型的,所以小時(shí)級(jí)的窗口長(zhǎng)度是不推薦的。在Cassandra 或 HBase 這樣的數(shù)據(jù)中聚合數(shù)據(jù)更為方便。窗口數(shù)據(jù)轉(zhuǎn)換計(jì)算出的結(jié)果是基于窗口長(zhǎng)度和窗口滑動(dòng)間隔的。
Spark 的性能受窗口長(zhǎng)度,窗口滑動(dòng)間隔,和持久化的影響。
構(gòu)建容錯(cuò)系統(tǒng)
實(shí)時(shí)流處理系統(tǒng)必須是 24/7可用的,這需要在各種錯(cuò)誤發(fā)生時(shí)系統(tǒng)是可靠的。 Spark 和 RDD 抽象 被設(shè)計(jì)成可以無縫處理集群中任何節(jié)點(diǎn)的故障.
Spark Streaming 容錯(cuò)處理的主要機(jī)制是 核對(duì)檢驗(yàn)點(diǎn),自動(dòng)重啟驅(qū)動(dòng), 和自動(dòng)故障切換。檢驗(yàn)點(diǎn)中存儲(chǔ)了應(yīng)用的狀態(tài),使它 Spark能夠從驅(qū)動(dòng)故障中國年恢復(fù)。
Spark Version 1.2 通過寫前日志, 可靠的接收器, 和文件流保證了數(shù)據(jù)的零丟失。 寫前日志代表了接收數(shù)據(jù)的容錯(cuò)存儲(chǔ)。出現(xiàn)故障要求重新計(jì)算結(jié)果。
DStream 操著有著準(zhǔn)確的語意。數(shù)據(jù)轉(zhuǎn)換可能計(jì)算多次,但產(chǎn)生相同的結(jié)果。 DStream 輸出操作至少有一個(gè)語意。輸出操作也可能執(zhí)行多次。
以TCP sockets處理實(shí)時(shí)數(shù)據(jù)
理解了流操作的基礎(chǔ),先在TCP socket 上作實(shí)驗(yàn)。 TCP socket 建立在客戶端和服務(wù)器之間 的雙工通信,通過已建的連接交互數(shù)據(jù)。 WebSocket 與Http 連接不一樣,是長(zhǎng)連接。
HTTP 不會(huì)在server側(cè)保持一個(gè)連接并連續(xù)推送數(shù)據(jù)到web瀏覽器。很多 web應(yīng)用因而通過AJAX和XML請(qǐng)求采用了長(zhǎng)輪詢 . WebSockets 是HTML5 中實(shí)現(xiàn)的標(biāo)準(zhǔn),支持現(xiàn)代的 web瀏覽器并稱為了實(shí)時(shí)通訊的跨平臺(tái)標(biāo)準(zhǔn)。
創(chuàng)建TCP sockets
運(yùn)行netcat 創(chuàng)建一個(gè) TCP Socket Server, 這是Linux系統(tǒng)中的一個(gè)小工具可以作為數(shù)據(jù)服務(wù)器,命令如下:
#
# Socket Server
#
an@an-VB:~$ nc -lk 9999
hello world
how are you
hello world
cool it works
一旦 netcat運(yùn)行起來, 以Spark Streaming 的客戶端打開第二個(gè)控制臺(tái)來接收和處理數(shù)據(jù)。 一旦 Spark Streaming 客戶端控制臺(tái)監(jiān)聽我們的鍵入單詞并處理, 就是反饋, hello world.
處理實(shí)時(shí)數(shù)據(jù)
我們將使用 Spark 提供的Spark
Streaming 示例程序 network_wordcount.py. 可以從GitHub 獲得 https://github.com/apache/spark/blob/master/examples/src/main/python/streaming/network_wordcount.py.
代碼如下:
"""
Counts words in UTF8 encoded, '\n' delimited text received from the
network every second.
Usage: network_wordcount.py <hostname> <port>
<hostname> and <port> describe the TCP server that Spark Streaming
would connect to receive data.
To run this on your local machine, you need to first run a Netcat
server
`$ nc -lk 9999`
and then run the example
`$ bin/spark-submit examples/src/main/python/streaming/network_
wordcount.py localhost 9999`
"""
from __future__ import print_function
import sys
from pyspark.streaming import StreamingContext
if __name__ == "__main__":
if len(sys.argv) != 3:
print("Usage: network_wordcount.py <hostname> <port>",
file=sys.stderr)
exit(-1)
sc = SparkContext(appName="PythonStreamingNetworkWordCount")
ssc = StreamingContext(sc, 1)
lines = ssc.socketTextStream(sys.argv[1], int(sys.argv[2]))
counts = lines.flatMap(lambda line: line.split(" "))\
.map(lambda word: (word, 1))\
.reduceByKey(lambda a, b: a+b)
ssc.start()
ssc.awaitTermination()
解釋一下程序的過程:
- 第一行代碼初始化 :
ssc = StreamingContext(sc, 1)
2.接著, 建立流式計(jì)算.
連接localhost 或 127.0.0.1 的9999端口從接收數(shù)據(jù)中得到一個(gè)或多個(gè) DStream 對(duì)象 :
stream = ssc.socketTextStream("127.0.0.1", 9999)
定義DStream 的計(jì)算: 數(shù)據(jù)轉(zhuǎn)換和輸出操作:
stream.map(x: lambda (x,1)) .reduce(a+b) .print()
開始計(jì)算:
ssc.start()
手工刮起活錯(cuò)誤處理完成時(shí)中斷程序:
ssc.awaitTermination()
當(dāng)?shù)竭_(dá)完成條件時(shí),可以手工完成處理:
ssc.stop()
通過瀏覽Spark 的監(jiān)測(cè)主頁localhost:4040 可以監(jiān)測(cè)Spark Streaming 流應(yīng)用 .
這里是程序運(yùn)行的結(jié)果以及netcat記錄的字符:
#
# Socket Client
#
an@an-VB:~/spark/spark-1.5.0-bin-hadoop2.6$ ./bin/spark-submit
examples/src/main/python/streaming/network_wordcount.py localhost 9999
連接localhost 的9999端口 運(yùn)行Spark Streaming network_count 程序:
an@an-VB:~/spark/spark-1.5.0-bin-hadoop2.6$ ./bin/spark-submit examples/
src/main/python/streaming/network_wordcount.py localhost 9999
-------------------------------------------
Time: 2015-10-18 20:06:06
-------------------------------------------
(u'world', 1)
(u'hello', 1)
-------------------------------------------
Time: 2015-10-18 20:06:07
-------------------------------------------
. . .
-------------------------------------------
Time: 2015-10-18 20:06:17
-------------------------------------------
(u'you', 1)
(u'how', 1)
(u'are', 1)
-------------------------------------------
Time: 2015-10-18 20:06:18
-------------------------------------------
. . .
-------------------------------------------
Time: 2015-10-18 20:06:26
-------------------------------------------
(u'', 1)
(u'world', 1)
(u'hello', 1)
因此, 已經(jīng)建立了本地9999端口的socket連接,流式處理
netcat 發(fā)送的數(shù)據(jù) , 執(zhí)行發(fā)送消息的單純統(tǒng)計(jì)。
實(shí)時(shí)控制Twitter數(shù)據(jù)
Twitter 提供了兩個(gè) APIs. 搜索 API允許我們根據(jù)搜索條目獲得過去的 tweets. 這就是我們前面章節(jié)如何從Twitter采集數(shù)據(jù)的方式。我們的目的是通過,Twitter 提供的實(shí)時(shí)streaming API ,從博客世界接收用戶剛發(fā)送的 tweets .
實(shí)時(shí)處理Tweets
下面的程序連接了 Twitter服務(wù),處理流入的 tweets單不包括已刪除或無效的 tweets,實(shí)時(shí)解析相關(guān)tweet提取 screen 名稱, 或 tweet text, retweet count, geo-location 信息. 處理過的 tweets 通過Spark Streaming 收集到一個(gè)RDD Queue,然后每隔一秒顯示在控制臺(tái)上 :
"""
Twitter Streaming API Spark Streaming into an RDD-Queue to process
tweets live Create a queue of RDDs that will be mapped/reduced one at a time in 1 second intervals.
To run this example use
'$ bin/spark-submit examples/AN_Spark/AN_Spark_Code/s07_
twitterstreaming.py'
"""
#
import time
from pyspark import SparkContext
from pyspark.streaming import StreamingContext
import twitter
import dateutil.parser
import json
# Connecting Streaming Twitter with Streaming Spark via Queue
class Tweet(dict):
def __init__(self, tweet_in):
super(Tweet, self).__init__(self)
if tweet_in and 'delete' not in tweet_in:
self['timestamp'] = dateutil.parser.parse(tweet_
in[u'created_at']
).replace(tzinfo=None).isoformat()
self['text'] = tweet_in['text'].encode('utf-8')
#self['text'] = tweet_in['text']
self['hashtags'] = [x['text'].encode('utf-8') for x in
tweet_in['entities']['hashtags']]
#self['hashtags'] = [x['text'] for x in tweet_
in['entities']['hashtags']]
self['geo'] = tweet_in['geo']['coordinates'] if tweet_
in['geo'] else None
self['id'] = tweet_in['id']
self['screen_name'] = tweet_in['user']['screen_name'].
encode('utf-8')
#self['screen_name'] = tweet_in['user']['screen_name']
self['user_id'] = tweet_in['user']['id'`
def connect_twitter():
twitter_stream = twitter.TwitterStream(auth=twitter.OAuth(
token = "get_your_own_credentials",
token_secret = "get_your_own_credentials",
consumer_key = "get_your_own_credentials",
consumer_secret = "get_your_own_credentials"))
return twitter_stream
def get_next_tweet(twitter_stream):
stream = twitter_stream.statuses.sample(block=True)
tweet_in = None
while not tweet_in or 'delete' in tweet_in:
tweet_in = stream.next()
tweet_parsed = Tweet(tweet_in)
return json.dumps(tweet_parsed)
def process_rdd_queue(twitter_stream):
# Create the queue through which RDDs can be pushed to
# a QueueInputDStream
rddQueue = []
for i in range(3):
rddQueue += [ssc.sparkContext.parallelize([get_next_
tweet(twitter_stream)], 5)]
lines = ssc.queueStream(rddQueue)
lines.pprint()
if __name__ == "__main__":
sc = SparkContext(appName="PythonStreamingQueueStream")
ssc = StreamingContext(sc, 1)
# Instantiate the twitter_stream
twitter_stream = connect_twitter()
# Get RDD queue of the streams json or parsed
process_rdd_queue(twitter_stream)
ssc.start()
time.sleep(2)
ssc.stop(stopSparkContext=True, stopGraceFully=True)
運(yùn)行這個(gè)程序,有如下輸出:
an@an-VB:~/spark/spark-1.5.0-bin-hadoop2.6$ bin/spark-submit examples/
AN_Spark/AN_Spark_Code/s07_twitterstreaming.py
Time: 2015-11-03 21:53:14
{"user_id": 3242732207, "screen_name": "cypuqygoducu", "timestamp":
"2015-11-03T20:53:04", "hashtags": [], "text": "RT @VIralBuzzNewss:
Our Distinctive Edition Holiday break Challenge Is In this article!
Hooray!... - https://t.co/9d8wumrd5v https://t.co/\u2026", "geo": null,
"id": 661647303678259200}
Time: 2015-11-03 21:53:15
{"user_id": 352673159, "screen_name": "melly_boo_orig", "timestamp":
"2015-11-03T20:53:05", "hashtags": ["eminem"], "text": "#eminem
https://t.co/GlEjPJnwxy", "geo": null, "id": 661647307847409668}
Time: 2015-11-03 21:53:16
{"user_id": 500620889, "screen_name": "NBAtheist", "timestamp": "2015-11-
03T20:53:06", "hashtags": ["tehInterwebbies", "Nutters"], "text": "See?
That didn't take long or any actual effort. This is #tehInterwebbies
... #Nutters Abound! https://t.co/QS8gLStYFO", "geo": null, "id":
661647312062709761}
我們有了一個(gè)用Spark流式接收并飛速地處理它們.
# 構(gòu)建一個(gè)穩(wěn)定縮放的流式應(yīng)用
攝取數(shù)據(jù)是從各種源中獲取數(shù)據(jù)并馬上處理或存儲(chǔ)起來稍后處理的過程。數(shù)據(jù)消費(fèi)系統(tǒng)是分散的,可以從架構(gòu)或物理上與數(shù)據(jù)源分開。數(shù)據(jù)攝取經(jīng)常通過手工腳本實(shí)現(xiàn)或基本的自動(dòng)化。實(shí)際上,可以調(diào)用象 Flume 和Kafka這樣的高層框架。數(shù)據(jù)攝取的挑戰(zhàn)來自原數(shù)據(jù)的物理分散和暫態(tài)導(dǎo)致的集成集成脆弱性。對(duì)于天氣,交通,社交媒體,網(wǎng)絡(luò)行為,傳感器,安全,監(jiān)控來說,數(shù)據(jù)產(chǎn)品是連續(xù)的。
不斷增長(zhǎng)的數(shù)據(jù)容量和流量以及變化的數(shù)據(jù)結(jié)構(gòu)和語意是數(shù)據(jù)攝取混亂并有錯(cuò)誤傾向。
我們的目標(biāo)是更加的敏捷,可靠和伸縮性。數(shù)據(jù)攝取的敏捷性,可靠性和伸縮性決定了流水線的健康程度。敏捷性意味著可以集成新的數(shù)據(jù)源,并按需改變現(xiàn)存的數(shù)據(jù)源。 為了保證安全可靠, 需要從基礎(chǔ)設(shè)施上防止數(shù)據(jù)丟失,防止下游應(yīng)用在入口處得到的數(shù)據(jù)遭到破壞。伸縮性避免了攝取瓶頸,能夠保持?jǐn)?shù)據(jù)的快速處理。

使所有數(shù)據(jù)得到快速的響應(yīng),核心驅(qū)動(dòng)是統(tǒng)一日志。統(tǒng)一日志是實(shí)時(shí)訂閱的集中化企業(yè)結(jié)構(gòu)化日志。所有的組織數(shù)據(jù)放到一個(gè)中心化的日志中。記錄編號(hào)從零開始,被看作是一個(gè)提交日志或者 journal. Unified Log 的概念是Kappa 架構(gòu)的核心宗旨。
Unified Log 的特性如下:
? Unified: 針對(duì)所有組織的單一部署。
? Append only: 事件是可追加且不可變的
? Ordered: 在一個(gè)切片內(nèi)每個(gè)事件有位惟一的偏移量
? Distributed: 考慮容錯(cuò), Unified Log 冗余分布在集群的各臺(tái)主機(jī)上
? Fast: 系統(tǒng)每秒攝取成千上萬的消息
#搭建 Kafka
為了獨(dú)立數(shù)據(jù)上行和數(shù)據(jù)消費(fèi),需要對(duì)數(shù)據(jù)提供者和消費(fèi)者解耦合。 由于雙方有不同的周期和約束, Kafka 可以解耦合數(shù)據(jù)處理的流水線.Apache Kafka 是一個(gè)分布式的發(fā)布訂閱消息系統(tǒng),也可以理解成一個(gè)分布式的提交日志。消息按照話題存儲(chǔ).
Apache Kafka 有如下特性. 它支持:
? 高容量事件的高吞吐量
? 新的和派生的 feeds 的實(shí)時(shí)處理
? 低時(shí)延的企業(yè)級(jí)消息系統(tǒng)
?容錯(cuò)歸功于分區(qū)內(nèi)的分布式消息存儲(chǔ),每個(gè)消息都有一個(gè)惟一的序列 ID 叫做 offset.
Consumers 通過 元組(offset, partition, topic)檢查它們的指針.
深入解剖一下 Kafka.Kafka 有三個(gè)基本組件: producers, consumers 和brokers. Producers
推送并寫數(shù)據(jù)到brokers. Consumers 從broker 拉取并讀到數(shù)據(jù). Brokers不推送消息給consumers. Consumers 從brokers拉取數(shù)據(jù). 分布式協(xié)作由 Apache Zookeeper完成.
brokers以話題形式存儲(chǔ)和管理數(shù)據(jù). 話題分割到復(fù)制分區(qū)內(nèi)。數(shù)據(jù)持久化broker, 在存留期間并不移除. 如果一個(gè)consumer 失敗了, 它總是回到broker 再次獲取數(shù)據(jù).
Kafka 需要 Apache ZooKeeper支持. ZooKeeper 是分布式應(yīng)用的一個(gè)高性能協(xié)調(diào)服務(wù)。它集中管理配置,注冊(cè)或命名服務(wù),組的成員關(guān)系,鎖, 服務(wù)器間的同步協(xié)調(diào),提供了層次化命名空間,有元數(shù)據(jù),監(jiān)測(cè)統(tǒng)計(jì)和集群狀態(tài)。 ZooKeeper 可以快速地引入 brokers和 consumers,然后在集群內(nèi)重新均衡。
Kafka producers 不需要 ZooKeeper. Kafka brokers 使用 ZooKeeper提供通用的狀態(tài)信息在故障的時(shí)候選舉leader. Kafka consumers 使用ZooKeeper 跟蹤消息的偏移量. 新版本的 Kafka 將通過 ZooKeeper 保存consumers,并提取Kafka 中特定的話題信息 .Kafka 為producers提供了自動(dòng)的負(fù)載均衡.
下圖描述了Kafka 的建立過程:

## 安裝測(cè)試Kafka
下載 Apache Kafka 執(zhí)行文件
http://kafka.apache.org/downloads.html 按下列步驟安裝軟件:
1. 下載代碼 .
2.
下載 0.8.2.0 版本并解壓:
> tar -xzf kafka_2.10-0.8.2.0.tgz
> cd kafka_2.10-0.8.2.0
3.啟動(dòng)zooeeper. 使用一個(gè)方便的腳步讓Kafka 得到 ZooKeeper 的一個(gè)單節(jié)點(diǎn)實(shí)例
.
bin/zookeeper-server-start.sh config/zookeeper.properties
an@an-VB:~/kafka/kafka_2.10-0.8.2.0$ bin/zookeeper-server-start.sh
config/zookeeper.properties
[2015-10-31 22:49:14,808] INFO Reading configuration from:
config/zookeeper.properties (org.apache.zookeeper.server.quorum.
QuorumPeerConfig)
[2015-10-31 22:49:14,816] INFO autopurge.snapRetainCount set to 3
(org.apache.zookeeper.server.DatadirCleanupManager)..
4.啟動(dòng) Kafka server:
bin/kafka-server-start.sh config/server.properties
an@an-VB:~/kafka/kafka_2.10-0.8.2.0$ bin/kafka-server-start.sh
config/server.properties
[2015-10-31 22:52:04,643] INFO Verifying properties (kafka.utils.
VerifiableProperties)
[2015-10-31 22:52:04,714] INFO Property broker.id is overridden to
0 (kafka.utils.VerifiableProperties)
[2015-10-31 22:52:04,715] INFO Property log.cleaner.enable is
overridden to false (kafka.utils.VerifiableProperties)
[2015-10-31 22:52:04,715] INFO Property log.dirs is overridden to
/tmp/kafka-logs (kafka.utils.VerifiableProperties)
[2013-04-22
15:01:47,051] INFO Property socket.send.buffer.bytes is overridden
to 1048576 (kafka.utils.VerifiableProperties)
5.創(chuàng)建主題. 這里創(chuàng)建一個(gè)名為 test 的主題,只有一個(gè)分區(qū)和一個(gè)副本 :
bin/kafka-topics.sh --create --zookeeper localhost:2181
--replication-factor 1 --partitions 1 --topic test
6.如果運(yùn)行 list topic 命令,可以看到這一主題:
bin/kafka-topics.sh --list --zookeeper localhost:2181
Test
an@an-VB:~/kafka/kafka_2.10-0.8.2.0$ bin/kafka-topics.sh --create
--zookeeper localhost:2181 --replication-factor 1 --partitions 1
--topic test
Created topic "test".
an@an-VB:~/kafka/kafka_2.10-0.8.2.0$ bin/kafka-topics.sh --list
--zookeeper localhost:2181
test
7.通過創(chuàng)建一個(gè) producer and consumer來檢查Kafka 的安裝。 先創(chuàng)建一個(gè) producer ,從控制臺(tái)鍵入消息:
an@an-VB:~/kafka/kafka_2.10-0.8.2.0$ bin/kafka-console-producer.sh
--broker-list localhost:9092 --topic test
[2015-10-31 22:54:43,698] WARN Property topic is not valid (kafka.
utils.VerifiableProperties)
This is a message
This is another message
8. 啟動(dòng)一個(gè) consumer 檢查接收到的消息:
an@an-VB:~$ cd kafka/
an@an-
VB:~/kafka$ cd kafka_2.10-0.8.2.0/
an@an-VB:~/kafka/kafka_2.10-0.8.2.0$ bin/kafka-console-consumer.sh
--zookeeper localhost:2181 --topic test --from-beginning
This is a message
This is another message
consumer 正確接收消息的過程:
1. 檢查Kafka 和 Spark Streaming consumer. 使用 Spark
Streaming Kafka 單詞統(tǒng)計(jì)示例. 要注意的是: 當(dāng)發(fā)送Spark
job的時(shí)候,必須綁定 Kafka 包, --packages org.apache.
spark:spark-streaming-kafka_2.10:1.5.0. 命令如下:
./bin/spark-submit --packages org.apache.spark:spark-streaming-
kafka_2.10:1.5.0 \ examples/src/main/python/streaming/kafka_
wordcount.py
localhost:2181 test
2 當(dāng)用Kafka 啟動(dòng)Spark Streaming 單詞計(jì)算程序的時(shí)候, 得到下面的輸出:
an@an-VB:~/spark/spark-1.5.0-bin-hadoop2.6$ ./bin/spark-submit
--packages org.apache.spark:spark-streaming-kafka_2.10:1.5.0
examples/src/main/python/streaming/kafka_wordcount.py
localhost:2181 test
Time: 2015-10-31 23:46:33
(u'', 1)
(u'from', 2)
(u'Hello', 2)
(u'Kafka', 2)
Time: 2015-10-31 23:46:34
Time: 2015-10-31 23:46:35
3.安裝Kafka Python driver 以便對(duì) Producers and Consumers 編程,并使用python 與Kafka and Spark 交互. 我門使用David Arthur開發(fā)的庫, aka, Mumrah
on GitHub 網(wǎng)址 (https://github.com/mumrah). 安裝明亮如下:
pip install kafka-python
an@an-VB:~$ pip install kafka-python
Collecting kafka-python
Downloading kafka-python-0.9.4.tar.gz (63kB)
...
Successfully installed kafka-python-0.9.4
## 開發(fā) producers
下面的程序創(chuàng)建了一個(gè)簡(jiǎn)單的Kafka Producer ,來發(fā)送一個(gè)消息 *this is a message sent from the Kafka producer*: 5次,帶時(shí)間戳:
kafka producer
import time
from kafka.common import LeaderNotAvailableError
from kafka.client import KafkaClient
from kafka.producer import SimpleProducer
from datetime import datetime
def print_response(response=None):
if response:
print('Error: {0}'.format(response[0].error))
print('Offset: {0}'.format(response[0].offset))
def main():
kafka = KafkaClient("localhost:9092")
producer = SimpleProducer(kafka)
try:
time.sleep(5)
topic = 'test'
for i in range(5):
time.sleep(1)
msg = 'This is a message sent from the kafka producer: '
+ str(datetime.now().time()) + ' -- '
+ str(datetime.now().strftime("%A, %d %B %Y
%I:%M%p"))
print_response(producer.send_messages(topic, msg))
except LeaderNotAvailableError:
# https://github.com/mumrah/kafka-python/issues/249
time.sleep(1)
print_response(producer.send_messages(topic, msg))
kafka.close()
if name == "main":
main()
運(yùn)行程序,產(chǎn)生如下輸出:
an@an-VB:~/spark/spark-1.5.0-bin-hadoop2.6/examples/AN_Spark/AN_Spark_
Code$ python s08_kafka_producer_01.py
Error: 0
Offset: 13
Error: 0
Offset: 14
Error: 0
Offset: 15
Error: 0
Offset: 16
Error: 0
Offset: 17
an@an-VB:~/spark/spark-1.5.0-bin-hadoop2.6/examples/AN_Spark/AN_Spark_Code$
沒有錯(cuò)誤,并且Kafka broker 給出了消息的偏移量。
## 開發(fā) consumers
為了從 Kafka brokers獲取消息, 開發(fā)一個(gè)Kafka consumer:
kafka consumer
consumes messages from "test" topic and writes them to console.
from kafka.client import KafkaClient
from kafka.consumer import SimpleConsumer
def main():
kafka = KafkaClient("localhost:9092")
print("Consumer established connection to kafka")
consumer = SimpleConsumer(kafka, "my-group", "test")
for message in consumer:
# This will wait and print messages as they become available
print(message)
if name == "main":
main()
運(yùn)行程序, 可以確認(rèn)consumer 收到了所有消息:
an@an-VB:~$ cd ~/spark/spark-1.5.0-bin- hadoop2.6/examples/AN_Spark/AN_Spark_Code/
an@an-VB:~/spark/spark-1.5.0-bin-h(huán)adoop2.6/examples/AN_Spark/AN_Spark_Code$ python s08_kafka_consumer_01.py
Consumer established connection to kafka
OffsetAndMessage(offset=13, message=Message(magic=0, attributes=0,
key=None, value='This is a message sent from the kafka producer:
11:50:17.867309Sunday, 01 November 2015 11:50AM'))
...
OffsetAndMessage(offset=17, message=Message(magic=0, attributes=0,
key=None, value='This is a message sent from the kafka producer:
11:50:22.051423Sunday, 01 November 2015 11:50AM'))
## 在Kafka 上開發(fā)Spark Streaming consumer
基于Spark Streaming 中提供的示例代碼, 創(chuàng)建 一個(gè)Kafka的 Spark Streaming consumer,并對(duì)Brokers中存儲(chǔ)的消息執(zhí)行 單詞統(tǒng)計(jì):
Kafka Spark Streaming Consumer
from future import print_function
import sys
from pyspark import SparkContext
from pyspark.streaming import StreamingContext
from pyspark.streaming.kafka import KafkaUtils
if name == "main":
if len(sys.argv) != 3:
print("Usage: kafka_spark_consumer_01.py <zk> <topic>",
file=sys.stderr)
exit(-1)
sc = SparkContext(appName="PythonStreamingKafkaWordCount")
ssc = StreamingContext(sc, 1)
zkQuorum, topic = sys.argv[1:]
kvs = KafkaUtils.createStream(ssc, zkQuorum, "spark-streaming-
consumer", {topic: 1})
lines = kvs.map(lambda x: x[1])
counts = lines.flatMap(lambda line: line.split(" ")) \
.map(lambda word: (word, 1)) \
.reduceByKey(lambda a, b: a+b)
counts.pprint()
ssc.start()
ssc.awaitTermination()
執(zhí)行程序,并運(yùn)行 Spark submit 命令:
./bin/spark-submit --packages org.apache.spark:spark-streaming-
kafka_2.10:1.5.0 examples/AN_Spark/AN_Spark_Code/s08_kafka_spark_
consumer_01.py localhost:2181 test
得到如下輸出:
an@an-VB:~$ cd spark/spark-1.5.0-bin-hadoop2.6/
an@an-VB:~/spark/spark-1.5.0-bin-hadoop2.6$ ./bin/spark-submit
--packages org.apache.spark:spark-streaming-kafka_2.10:1.5.0
examples/AN_Spark/AN_Spark_Code/s08_kafka_spark_consumer_01.py
localhost:2181 test
...
:: retrieving :: org.apache.spark#spark-submit-parent
confs: [default]
0 artifacts copied, 10 already retrieved (0kB/18ms)
Time: 2015-11-01 12:13:16
Time: 2015-11-01 12:13:17
Time: 2015-11-01 12:13:18
Time: 2015-11-01 12:13:19
(u'a', 5)
(u'the', 5)
(u'11:50AM', 5)
(u'from', 5)
(u'This', 5)
(u'11:50:21.044374Sunday,', 1)
(u'message', 5)
(u'11:50:20.036422Sunday,', 1)
(u'11:50:22.051423Sunday,', 1)
(u'11:50:17.867309Sunday,', 1)
...
Time: 2015-11-01 12:13:20
Time: 2015-11-01 12:13:21
# 探索flume
Flume 是一個(gè)連續(xù)攝取數(shù)據(jù)的系統(tǒng),最初被設(shè)計(jì)成一個(gè)日志聚會(huì)系統(tǒng),但演變?yōu)榭梢蕴幚砣魏晤愋偷牧魇录?shù)據(jù)。
Flume 是一個(gè)分布式,可靠的,具有伸縮性的流水線系統(tǒng),可以有效地采集,聚會(huì),傳輸大容量數(shù)據(jù),內(nèi)置支持上下文路由,過濾復(fù)制,和多路復(fù)用。 容錯(cuò)和健壯性好,擁有可調(diào)的可靠性機(jī)制,多種故障切換和恢復(fù)機(jī)制. 它使用簡(jiǎn)單可擴(kuò)展的數(shù)據(jù)模型應(yīng)用于實(shí)時(shí)分析。
Flume 提供了:
? 保證傳輸?shù)恼Z意
? 低時(shí)延可靠的數(shù)據(jù)傳輸
? 不需要編碼的聲明式配置
? 可擴(kuò)展和定制的設(shè)置
? 集成了大多數(shù)通用的端點(diǎn)數(shù)據(jù)源
Flume 包括以下元素:
? Event: 一個(gè)事件是Flume從源到目的地址傳輸數(shù)據(jù)的基本單元. 象一條消息那樣,對(duì) Flume 而言是不透明的字節(jié)數(shù)組,通過可選的頭信息來做上下文路由。
? Client: 一個(gè) client 生產(chǎn)和傳輸事件. 客戶端把 Flume和數(shù)據(jù)消費(fèi)者解耦合,是一個(gè)生成事件并把它們發(fā)送到一個(gè)或多個(gè)代理 的實(shí)體,可以是定制的客戶端或者
Flume log4J 程序 或 潛入到應(yīng)用中的代理.
? Agent: 一個(gè)代理是一個(gè)容器,承載了源,信道, sinks, 和其它元素使數(shù)據(jù)能在不同地址間傳輸。它對(duì)托管的組件提供了配置,生命周期管理和監(jiān)控。代理在物理上是一個(gè)運(yùn)行 Flume的Java 虛擬機(jī)。
? Source: 源師Flume 接收事件的實(shí)體,至少使用一個(gè)信道,通過該信道輪詢數(shù)據(jù)或者等待數(shù)據(jù)發(fā)送給它,有各種采集數(shù)據(jù)的源,例如 log4j logs 和 syslogs.
? Sink: Sink 是一個(gè)實(shí)體,將信道中的數(shù)據(jù)排空并發(fā)送到下一個(gè)目的地址。 sinks 允許數(shù)據(jù)被流式發(fā)送到一定范圍的目的地址。 Sinks 支持序列化到用戶的格式. 例如 HDFS sink 將事件寫到 HDFS.
? Channel: 信道是源和sink之間的管道,緩存流入的事件之道sink 將信道中的數(shù)據(jù)排空。源給信道提供事件,Sink 排空信道。 信道解耦合了上下行系統(tǒng),抑制了上行數(shù)據(jù)的突發(fā)性, 客服了下行故障。為了實(shí)現(xiàn)這一目的,關(guān)鍵是調(diào)整處理事件的信道容量。
信道提供了兩種層次的持久化: 內(nèi)存型信道和文件型信道。當(dāng)JVM崩潰時(shí),內(nèi)存型信道是不穩(wěn)定的,
而文件型信道通過寫前日志 把信息存儲(chǔ)到硬盤,所以可以回復(fù)。信道是全事務(wù)型的。
下圖解釋了這些概念:

# 基于Flume, Kafka和Spark開發(fā)數(shù)據(jù)流水線
利用我們已學(xué)的知識(shí),可以構(gòu)建一個(gè)彈性的數(shù)據(jù)處理流水線。通過Flume 將數(shù)據(jù)攝取和傳輸放到一起,把Kafka 這樣的可靠的發(fā)布訂閱消息系統(tǒng)作為數(shù)據(jù)代理,最后使用Spark Streaming 完成高速的計(jì)算處理。
下圖解釋了流式數(shù)據(jù)流水線的組成 connect, collect, conduct, compose, consume, consign, 和 control.
這些活動(dòng)根據(jù)用例來配置:
+ Connect 建立了streaming API的綁定
+ Collect 創(chuàng)建了采集線程.
+ Compose 專注于處理數(shù)據(jù)
+ Consume 為消費(fèi)系統(tǒng)提供處理后的數(shù)據(jù)
+
Consign 負(fù)責(zé)數(shù)據(jù)持久化
+ Control 負(fù)責(zé)系統(tǒng)、數(shù)據(jù)和營(yíng)養(yǎng)的統(tǒng)籌和監(jiān)控。

下圖介紹了流式數(shù)據(jù)流水線的概念及關(guān)鍵組件: Spark Streaming, Kafka, Flume, 和低時(shí)延數(shù)據(jù)庫. 在消費(fèi)或控制型應(yīng)用中,需要實(shí)時(shí)監(jiān)控系統(tǒng),或者在超出一定閾值時(shí)實(shí)時(shí)發(fā)送告警。

下圖介紹了Spark 在處理單平臺(tái)運(yùn)動(dòng)數(shù)據(jù)和閑置數(shù)據(jù)的獨(dú)特能力,即根據(jù)不同的用例需求與多個(gè)持久化數(shù)據(jù)存儲(chǔ)無縫對(duì)接。

這張圖是到目前為止所談?wù)摰乃懈拍睢m敳棵枋隽肆魇教幚砹魉€。底部描述了批處理流水線 ,兩者共享位于中間的通用持久化層,包含各種模式的持久化和序列化。
# Lambda 和Kappa 架構(gòu)
現(xiàn)在有兩種流行的架構(gòu)范式: Lambda 和 Kappa 架構(gòu).
Lambda 是Storm 創(chuàng)建者何主要提交者 Nathan Marz 的作品. 他倡導(dǎo)在所有數(shù)據(jù)上構(gòu)建功能型架構(gòu)。批處理分支是Hadoop 的最初設(shè)想 , 是歷史數(shù)據(jù)的高時(shí)延高吞吐量的預(yù)處理,然后用于消費(fèi)。實(shí)時(shí)處理分支由Storm設(shè)計(jì)的,可以增量處理流數(shù)據(jù),快速產(chǎn)生結(jié)論性見解,從存儲(chǔ)中實(shí)時(shí)聚合數(shù)據(jù)。
Kappa 是Kafka 的主要提交者之一 Jay Kreps 和他在 Confluent (previously at LinkedIn)的同事的作品. 它倡導(dǎo)全部流式流水線,和前面談到的統(tǒng)一日志,是企業(yè)級(jí)的高效實(shí)現(xiàn).
## 理解 Lambda 架構(gòu)
Lambda 架構(gòu)結(jié)合了批處理和流式處理,在所有數(shù)據(jù)上提供了統(tǒng)一的查詢機(jī)制。有三層: 存儲(chǔ)預(yù)計(jì)算信息的批處理層,增量進(jìn)行流式實(shí)時(shí)處理的速度層,服務(wù)層合并了批處理和實(shí)時(shí)處理的隨機(jī)查詢。 下圖給出了 Lambda 的架構(gòu)。

## 理解Kappa 架構(gòu)
Kappa 架構(gòu)目標(biāo)是全企業(yè)的流模式驅(qū)動(dòng),起源于 Jay Kreps 和他在LinkedIn的同事的一個(gè)評(píng)論文章。
從那時(shí)起,他們利用Apache
Kafka 創(chuàng)建了 Confluent with Apache,并作為
Kappa 架構(gòu)的主要促成者. 基本宗旨是使用統(tǒng)一日志作為企業(yè)信息架構(gòu)的主干,進(jìn)而移動(dòng)到流處理模式。
統(tǒng)一日志是一個(gè)集中的企業(yè)結(jié)構(gòu)化日志,用于實(shí)時(shí)訂閱。所有組織的數(shù)據(jù)放入到一個(gè)集中的日志,記錄寫入時(shí)從零開始做標(biāo)記,可以時(shí)提及日志或日志集合。統(tǒng)一日志的概念是
Kappa 架構(gòu)的核心宗旨.
統(tǒng)一日志的特點(diǎn)如下:
? Unified: 所有組織的同一部署
? Append only: 事件是可追加且不可修改的
? Ordered: 每個(gè)事件在分片內(nèi)有唯一的偏移量
? Distributed: 出于容錯(cuò)考慮, 統(tǒng)一日志分布于在集群的不同主機(jī)上。
? Fast: 系統(tǒng)每秒攝取成千上萬的數(shù)據(jù)
下面的截圖展示了 Jay Kreps 宣稱對(duì) Lambda 架構(gòu)的保留意見. 他關(guān)于Lambda架構(gòu)的主要保留意見是在Hadoop and Storm兩個(gè)不同的系統(tǒng)中實(shí)現(xiàn)了相同的工作每個(gè)有著各自的特性,以及隨之而來的復(fù)雜性。
Kappa 通過Apache Kafka 既處理了實(shí)時(shí)數(shù)據(jù),也處理了歷史數(shù)據(jù)。

# 小結(jié)
我門列舉了流式處理架構(gòu)應(yīng)用的基礎(chǔ),描述了他們的挑戰(zhàn),約束和優(yōu)勢(shì)。深入了解了Spark Streaming 的內(nèi)部工作原理包括如何Spark Core 適用,以及與Spark SQL 和 Spark MLlib對(duì)話, 通過TCP sockets 解釋了流處理概念,接下來,是從Twitter 服務(wù)中實(shí)時(shí)攝取tweet。使用Kafka最大限度地增加了流處理架構(gòu)的彈性,討論了上下行數(shù)據(jù)與消費(fèi)者之間的解耦合。 還討論了Flume—這個(gè)可靠,靈活,伸縮性數(shù)據(jù)攝取和傳輸?shù)牧魉€系統(tǒng)。結(jié)合Flume, Kafka, 和Spark 在時(shí)變領(lǐng)域交付了非并行健壯性,速度和敏捷性.
在結(jié)尾觀察和點(diǎn)評(píng)了兩種流處理架構(gòu)范式 Lambda 和 Kappa 架構(gòu). Lambda 架構(gòu)在通用的查詢前端結(jié)合了 批處理和流數(shù)據(jù). 最初想法形成了 Hadoop和Storm的愿景. Spark 有自己的批處理和流處理范式, 以共有代碼為基礎(chǔ)提供了單一的環(huán)境,從而有效地在現(xiàn)實(shí)中使用這一架構(gòu)。Kappa 架構(gòu)宣揚(yáng)了同一日志的概念, 這創(chuàng)建了面向事件的架構(gòu),企業(yè)的所有數(shù)據(jù)通過信道傳輸?shù)揭粋€(gè)中心化的提交日志,同時(shí)對(duì)所有消費(fèi)者實(shí)時(shí)使用。