Spark-Streaming 流式數據處理

目前為止,已經討論了機器學習和批處理模式的數據挖掘。現在審視持續處理流數據,實時檢測其中的事實和模式,好像從湖泊來到了河流。

先研究一下不斷改變的動態環境帶來的挑戰,在列出流處理應用的先決條件(如,與Twitter的TCP Sockets)之后, 結合Spark, Kafka and Flume 把數據放入一個低延遲,高吞吐量,可縮放的處理流水線。

要點如下:

? 分析流式應用架構的挑戰,約束和需求

? 利用Spark Streaming 從 TCP socket 中處理實時數據

? 連接 Twitter 服務,準實時解析 tweets

? 使用 Spark, Kafka 和 Flume 建立一個可靠,容錯,可縮放,高吞吐量,低延遲的集成應用

? 以 Lambda 和 Kappa 的架構范式結尾

Spark Streaming在數據密集型應用中的位置

按照慣例, 先看一下最初的數據密集型應用架構,指明我們所感興趣的 Spark Streaming 模塊的所處位置.

下圖著重指明了整體架構中的Spark Streaming模塊,Spark SQL和 Spark MLlib:

5-1 Streaming 在數據密集型應用架構的位置

數據流可以來自股票市場的時序分析,企業交易,各種交互,事件,web流量,點擊流,和傳感器數據等,都是及時且帶有時間戳的數據。用例有欺詐檢測和防偽,移動的交叉銷售和銷售提升,或者交通預警。這些數據流需要及時處理以便于監測,例如 異常檢測,異常奇點,垃圾郵件,欺詐和入侵; 也可提供基礎的統計,見解,趨勢,和推薦。 某些情形,總結性的匯總信息需要存儲以備將來使用。從架構范式的角度看,我們從面向服務的架構轉為事件驅動的架構。

數據的流處理有兩個模型:

? 在數據到達時及時處理每條記錄,在處理前不需要在容器中緩存記錄,比如
Twitter's Storm, Yahoo's S4, 和 Google's MillWheel.

? 微型批處理 或者小間隔執行批處理計算,例如
Spark Streaming and Storm Trident. 根據微型批處理設置的時間窗口,將流入的數據記錄緩沖到一個容器中。
Spark Streaming 經常用來與 Storm 比較,這是兩種不同的流數據模型。 Spark Streaming 基于微型批處理,而 Storm 基于及時處理流入的數據。 Storm 也提供了微型批處理選項 即 Storm Triden。

流處理應用的主要驅動因素的時間延遲。時延從 RPC (short for Remote Procedure Call) 的毫秒 到幾秒 到微信批處理方案的幾分鐘不等。
RPC 允許請求程序與遠程服務器之間的同步操作。線程允許多個 RPC 調用的并發。

分布式RPC 模型實現的一個實例就是 Apache Storm.
Storm 實現了無邊界元組的無狀態毫秒級延遲處理,結合數據流作為噴發源使用了拓撲或定向環圖的及時,提供了過濾, join, 聚合和轉換.
Storm 也實現了一個高層抽象叫做 Trident , 與Spark類似, 以微型批處理進行流式數據處理。

考慮時延的限制, 從毫秒到秒級, Storm 是個很好的候選. 對于秒到分鐘級, Spark Streaming and Storm Trident
是最佳選擇。 對于若干分鐘以上, Spark 和 NoSQL 數據庫如
Cassandra 和HBase 是合適的方案. 對于多小時運算海量數據,Hadoop 是理想的競爭者。
盡管吞吐量逾時延相關,但不是一個簡單的反線性關系。
如果處理一條消息需要2 ms, 只取決于延遲的情況, 可以假設吞吐量限制在500條消息每秒。如果緩存了8ms的數據,那么批處理允許更高的吞吐量。 10 ms 時延, 系統可以緩沖10,000 條消息. 為了一個可忍受的時延增長,我們要顯著的增加吞吐量。這是Spark Streaming 微型批處理的一個魔法。

Spark Streaming 內部工作方式

Spark Streaming 充分利用了 Spark 的核心架構,作為流處理功能的入口點,它構建在 SparkContext 的 StreamingContext 之上. 集群管理器將至少單獨分片一個工作節點作為接收器,這是一個長時間運行的任務執行器來處理進入的流數據。
執行器創建 Discretized Streams 或者從輸入數據流中得來的 DStreams, DStream 默認為另一個worker 的緩存。 接收器服務于輸入數據流,多個接收器提升了并行性,產生多個DStreams , Spark 用它unite 或 join RDD.

下圖給出了 Spark Streaming 的那邊工作概要。客戶端通過集群管理器與
Spark 集群交互 ,Spark Streaming 有一個專屬的worker 運行著長任務來接待輸入的數據流,轉化為 discretized streams 或DStreams. 接收器采集,緩存,復制數據,然后放入一個 RDDs的流.


5-2 Spark Streaming 內部工作方式

Spark 接收器能夠從多種數據源接收數據。核心的輸入源包括從TCP socket 和 HDFS/Amazon S3 到 Akka Actors. 其它的數據源包括
Apache Kafka, Apache Flume, Amazon Kinesis, ZeroMQ, Twitter, 以及其它用戶定義的接收器。

我們可以區分資源是否可靠,可靠的數據源可以確認數據的接收憑證以及副本能否重發,非可靠數據源指接收器不能確認消息的接收憑證。 Spark 可以對workers,分區和接收器的數量進行縮放。

下圖給出了Spark Streaming 可能的數據源和持久化選項:

5-3 Spark Streaming 概覽

Spark Streaming 的底層基礎

Spark Streaming 由接收器, Discretized Streams 和 用于持久化的Spark Connectors 組成.
作為 Spark Core, 基本的數據結構是 RDD, 基本編程抽是 Discretized Stream 或 DStream.

下圖解釋了看做 RDDs連續序列的 Discretized Streams. DStream 的批處理間隔是可配置的。

5-4 RDD-Descretized Stream

DStreams 是在批處理間隔是對流入數據的快照。時間步長可以從500ms 到幾秒,DStream 的底層結構是一個 RDD.
一個DStream 基本上是 RDDs的一個連續序列。這使得Spark Streaming 非常強大,可以充分利用Spark Core 中已有的函數,數據轉換和動作, 可以與Spark SQL 對話, 在流入的數據流上執行SQL 查詢和 Spark MLlib. 通用的及基于鍵值對RDDs的數據轉換同樣適用。DStreams 得益于 RDDs 內部體系和容錯機制。 discretized stream 有額外的轉換和輸出操作。DStream 的大多數通用操作是 transform 和 foreachRDD.

下圖給出了 DStreams的生命周期,將消息通過微型批處理實現創建成 RDDs ,RDDs上的數據轉換函數和動作 觸發了 Spark jobs. 從上而下分布解釋:

  1. 輸入流中, 流入的消息緩存在容器中,時間窗口有微型批處理分配.
  1. 在 discretized stream 步驟中, 緩存的微型批處理轉化成 DStream RDDs.
  2. Mapped DStream 是通過使用轉換函數從原始的 DStream中獲得的. 這三個步驟構成了在預定義時間窗口的所接收數據的轉換。 底層的數據結構是RDD, 我們保留了轉換中的數據世系。
  3. 最后一步是 RDD上的一個動作,觸發了Spark job.


    5-5 Dstream

數據轉換可以無狀態的或者有狀態的。無狀態意味著沒有狀態需要程序維護,而有狀態意味著程序保持狀態,前面所記住的操作可能影響當前的操作。一個有狀態操作需要或修改一些系統的狀態,無狀態操作則不是。 無狀態數據轉換在一個時間點處理每個批處理中的一個
DStream. 有狀態數據轉換處理多個批處理來獲得結果,需要可配置的目錄檢查點。檢查點是容錯體系的主要機制, Spark Streaming 周期性存儲一個應用的數據和元數據。Spark Streaming 中的有狀態數據轉換有兩種類型:updateStateByKey 和 windowed transformations.

updateStateByKey 維護了在 Pair RDD 中一個流中的每個鍵值的狀態。當DStream中的鍵值漲停被更新時返回一個新的狀態。 一個例子是tweets的stream 中運行了given hashtags 的個數統計。Windowed transformations 在多個批處理的滑動窗口中執行。一個窗口有一個定義好的長度或時間單元的區間,是多個單一
DStream 處理的間隔,定義了一個窗口轉換中有多少個批處理或窗口轉換中計算的頻率。

下面的 schema 在 DStreams上的窗口操作,在給定長度和滑動間隔時生成 window DStreams :


5-6 Windowing on Dstreams

函數示例是 countByWindow (windowLength, slideInterval). 在 DStream上的一個滑動窗口中,計算每個單元素RDD的元素個數,返回一個新的DStream. 通過每60秒計算一次tweets流中指定的hashtags的數據作為示例的解釋。 窗口的時間幀是指定的。分鐘級的窗口長度是有道理的。
由于是計算和內存密集型的,所以小時級的窗口長度是不推薦的。在Cassandra 或 HBase 這樣的數據中聚合數據更為方便。窗口數據轉換計算出的結果是基于窗口長度和窗口滑動間隔的。
Spark 的性能受窗口長度,窗口滑動間隔,和持久化的影響。

構建容錯系統

實時流處理系統必須是 24/7可用的,這需要在各種錯誤發生時系統是可靠的。 Spark 和 RDD 抽象 被設計成可以無縫處理集群中任何節點的故障.

Spark Streaming 容錯處理的主要機制是 核對檢驗點,自動重啟驅動, 和自動故障切換。檢驗點中存儲了應用的狀態,使它 Spark能夠從驅動故障中國年恢復。

Spark Version 1.2 通過寫前日志, 可靠的接收器, 和文件流保證了數據的零丟失。 寫前日志代表了接收數據的容錯存儲。出現故障要求重新計算結果。
DStream 操著有著準確的語意。數據轉換可能計算多次,但產生相同的結果。 DStream 輸出操作至少有一個語意。輸出操作也可能執行多次。

以TCP sockets處理實時數據

理解了流操作的基礎,先在TCP socket 上作實驗。 TCP socket 建立在客戶端和服務器之間 的雙工通信,通過已建的連接交互數據。 WebSocket 與Http 連接不一樣,是長連接。
HTTP 不會在server側保持一個連接并連續推送數據到web瀏覽器。很多 web應用因而通過AJAX和XML請求采用了長輪詢 . WebSockets 是HTML5 中實現的標準,支持現代的 web瀏覽器并稱為了實時通訊的跨平臺標準。

創建TCP sockets

運行netcat 創建一個 TCP Socket Server, 這是Linux系統中的一個小工具可以作為數據服務器,命令如下:

#
#  Socket  Server
#
an@an-VB:~$  nc  -lk  9999
hello  world
how  are  you
hello   world
cool  it  works

一旦 netcat運行起來, 以Spark Streaming 的客戶端打開第二個控制臺來接收和處理數據。 一旦 Spark Streaming 客戶端控制臺監聽我們的鍵入單詞并處理, 就是反饋, hello world.

處理實時數據

我們將使用 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()

解釋一下程序的過程:

  1. 第一行代碼初始化 :
    ssc = StreamingContext(sc, 1)

2.接著, 建立流式計算.

  1. 連接localhost 或 127.0.0.1 的9999端口從接收數據中得到一個或多個 DStream 對象 :
    stream = ssc.socketTextStream("127.0.0.1", 9999)

  2. 定義DStream 的計算: 數據轉換和輸出操作:
    stream.map(x: lambda (x,1)) .reduce(a+b) .print()

  3. 開始計算:
    ssc.start()

  4. 手工刮起活錯誤處理完成時中斷程序:
    ssc.awaitTermination()

  5. 當到達完成條件時,可以手工完成處理:
    ssc.stop()
    通過瀏覽Spark 的監測主頁localhost:4040 可以監測Spark Streaming 流應用 .

這里是程序運行的結果以及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端口 運行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)


因此, 已經建立了本地9999端口的socket連接,流式處理
netcat 發送的數據 , 執行發送消息的單純統計。

實時控制Twitter數據

Twitter 提供了兩個 APIs. 搜索 API允許我們根據搜索條目獲得過去的 tweets. 這就是我們前面章節如何從Twitter采集數據的方式。我們的目的是通過,Twitter 提供的實時streaming API ,從博客世界接收用戶剛發送的 tweets .

實時處理Tweets

下面的程序連接了 Twitter服務,處理流入的 tweets單不包括已刪除或無效的 tweets,實時解析相關tweet提取 screen 名稱, 或 tweet text, retweet count, geo-location 信息. 處理過的 tweets 通過Spark Streaming 收集到一個RDD Queue,然后每隔一秒顯示在控制臺上 :


"""


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)

運行這個程序,有如下輸出:

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}


我們有了一個用Spark流式接收并飛速地處理它們.

  

# 構建一個穩定縮放的流式應用
 

攝取數據是從各種源中獲取數據并馬上處理或存儲起來稍后處理的過程。數據消費系統是分散的,可以從架構或物理上與數據源分開。數據攝取經常通過手工腳本實現或基本的自動化。實際上,可以調用象 Flume 和Kafka這樣的高層框架。數據攝取的挑戰來自原數據的物理分散和暫態導致的集成集成脆弱性。對于天氣,交通,社交媒體,網絡行為,傳感器,安全,監控來說,數據產品是連續的。 

不斷增長的數據容量和流量以及變化的數據結構和語意是數據攝取混亂并有錯誤傾向。   

我們的目標是更加的敏捷,可靠和伸縮性。數據攝取的敏捷性,可靠性和伸縮性決定了流水線的健康程度。敏捷性意味著可以集成新的數據源,并按需改變現存的數據源。  為了保證安全可靠, 需要從基礎設施上防止數據丟失,防止下游應用在入口處得到的數據遭到破壞。伸縮性避免了攝取瓶頸,能夠保持數據的快速處理。

![5-7 數據攝取模式](http://upload-images.jianshu.io/upload_images/73516-67ae0d0ec431c4ee?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

  使所有數據得到快速的響應,核心驅動是統一日志。統一日志是實時訂閱的集中化企業結構化日志。所有的組織數據放到一個中心化的日志中。記錄編號從零開始,被看作是一個提交日志或者 journal.   Unified  Log  的概念是Kappa 架構的核心宗旨。
Unified  Log 的特性如下:
  
  ? Unified:  針對所有組織的單一部署。  
  
? Append  only: 事件是可追加且不可變的  
  

? Ordered: 在一個切片內每個事件有位惟一的偏移量  
  

 
? Distributed:  考慮容錯, Unified  Log 冗余分布在集群的各臺主機上 
  

 
? Fast:  系統每秒攝取成千上萬的消息  

#搭建 Kafka

為了獨立數據上行和數據消費,需要對數據提供者和消費者解耦合。 由于雙方有不同的周期和約束,  Kafka 可以解耦合數據處理的流水線.Apache  Kafka  是一個分布式的發布訂閱消息系統,也可以理解成一個分布式的提交日志。消息按照話題存儲.  

Apache  Kafka  有如下特性. 它支持:
 
  

? 高容量事件的高吞吐量  

 

? 新的和派生的 feeds 的實時處理   

  
? 低時延的企業級消息系統

  
?容錯歸功于分區內的分布式消息存儲,每個消息都有一個惟一的序列  ID 叫做  offset.
Consumers  通過 元組(offset,  partition,  topic)檢查它們的指針.  


深入解剖一下 Kafka.Kafka 有三個基本組件:  producers, consumers 和brokers.  Producers 


  
推送并寫數據到brokers.  Consumers 從broker 拉取并讀到數據.  Brokers不推送消息給consumers. Consumers 從brokers拉取數據. 分布式協作由 Apache  Zookeeper完成.
brokers以話題形式存儲和管理數據. 話題分割到復制分區內。數據持久化broker,  在存留期間并不移除.  如果一個consumer 失敗了,  它總是回到broker  再次獲取數據.  






Kafka  需要 Apache  ZooKeeper支持.  ZooKeeper 是分布式應用的一個高性能協調服務。它集中管理配置,注冊或命名服務,組的成員關系,鎖, 服務器間的同步協調,提供了層次化命名空間,有元數據,監測統計和集群狀態。  ZooKeeper  可以快速地引入 brokers和  consumers,然后在集群內重新均衡。 




Kafka  producers 不需要 ZooKeeper.  Kafka  brokers  使用  ZooKeeper提供通用的狀態信息在故障的時候選舉leader.  Kafka  consumers  使用ZooKeeper  跟蹤消息的偏移量.  新版本的 Kafka 將通過 ZooKeeper  保存consumers,并提取Kafka 中特定的話題信息 .Kafka 為producers提供了自動的負載均衡.
下圖描述了Kafka  的建立過程:  
![5-8 Kafka 架構](http://upload-images.jianshu.io/upload_images/73516-37737e3f1c926923?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)  

## 安裝測試Kafka
下載 Apache  Kafka  執行文件 
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.啟動zooeeper. 使用一個方便的腳步讓Kafka 得到 ZooKeeper 的一個單節點實例   

.

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.啟動 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.創建主題.  這里創建一個名為 test 的主題,只有一個分區和一個副本  :
 

bin/kafka-topics.sh --create --zookeeper localhost:2181
--replication-factor 1 --partitions 1 --topic test


6.如果運行 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.通過創建一個  producer  and  consumer來檢查Kafka  的安裝。 先創建一個  producer ,從控制臺鍵入消息:  

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. 啟動一個 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  單詞統計示例. 要注意的是:  當發送Spark
job的時候,必須綁定 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 當用Kafka 啟動Spark  Streaming  單詞計算程序的時候,  得到下面的輸出:  

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 以便對 Producers  and  Consumers  編程,并使用python 與Kafka  and  Spark 交互.  我門使用David  Arthur開發的庫,  aka,  Mumrah
on  GitHub 網址 (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

## 開發 producers
  

下面的程序創建了一個簡單的Kafka  Producer ,來發送一個消息 *this  is  a  message  sent  from  the  Kafka  producer*:  5次,帶時間戳: 

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()


運行程序,產生如下輸出:

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$

沒有錯誤,并且Kafka  broker 給出了消息的偏移量。

## 開發 consumers
為了從 Kafka  brokers獲取消息,  開發一個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()


運行程序, 可以確認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-hadoop2.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 上開發Spark   Streaming   consumer    
基于Spark  Streaming  中提供的示例代碼,  創建 一個Kafka的  Spark  Streaming  consumer,并對Brokers中存儲的消息執行 單詞統計: 

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()

執行程序,并運行  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 是一個連續攝取數據的系統,最初被設計成一個日志聚會系統,但演變為可以處理任何類型的流事件數據。
Flume 是一個分布式,可靠的,具有伸縮性的流水線系統,可以有效地采集,聚會,傳輸大容量數據,內置支持上下文路由,過濾復制,和多路復用。 容錯和健壯性好,擁有可調的可靠性機制,多種故障切換和恢復機制.  它使用簡單可擴展的數據模型應用于實時分析。


Flume  提供了:
  

? 保證傳輸的語意
  

? 低時延可靠的數據傳輸
  

? 不需要編碼的聲明式配置
  

? 可擴展和定制的設置  


? 集成了大多數通用的端點數據源 


 Flume  包括以下元素:
? Event:  一個事件是Flume從源到目的地址傳輸數據的基本單元.  象一條消息那樣,對 Flume  而言是不透明的字節數組,通過可選的頭信息來做上下文路由。 

? Client:  一個 client  生產和傳輸事件.  客戶端把 Flume和數據消費者解耦合,是一個生成事件并把它們發送到一個或多個代理 的實體,可以是定制的客戶端或者
 Flume  log4J  程序 或  潛入到應用中的代理.


? Agent:  一個代理是一個容器,承載了源,信道, sinks, 和其它元素使數據能在不同地址間傳輸。它對托管的組件提供了配置,生命周期管理和監控。代理在物理上是一個運行 Flume的Java 虛擬機。

 
? Source:  源師Flume 接收事件的實體,至少使用一個信道,通過該信道輪詢數據或者等待數據發送給它,有各種采集數據的源,例如  log4j  logs 和 syslogs.
 

 
? Sink:  Sink 是一個實體,將信道中的數據排空并發送到下一個目的地址。 sinks 允許數據被流式發送到一定范圍的目的地址。 Sinks  支持序列化到用戶的格式.  例如 HDFS  sink 將事件寫到 HDFS.  
? Channel:  信道是源和sink之間的管道,緩存流入的事件之道sink 將信道中的數據排空。源給信道提供事件,Sink 排空信道。 信道解耦合了上下行系統,抑制了上行數據的突發性, 客服了下行故障。為了實現這一目的,關鍵是調整處理事件的信道容量。
信道提供了兩種層次的持久化:  內存型信道和文件型信道。當JVM崩潰時,內存型信道是不穩定的,
而文件型信道通過寫前日志 把信息存儲到硬盤,所以可以回復。信道是全事務型的。
  

下圖解釋了這些概念: 
![5-8 Flume](http://upload-images.jianshu.io/upload_images/73516-7d3adb34cfd7e520?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

# 基于Flume, Kafka和Spark開發數據流水線
利用我們已學的知識,可以構建一個彈性的數據處理流水線。通過Flume 將數據攝取和傳輸放到一起,把Kafka 這樣的可靠的發布訂閱消息系統作為數據代理,最后使用Spark Streaming 完成高速的計算處理。  
下圖解釋了流式數據流水線的組成 connect,  collect,  conduct,  compose,  consume,  consign,  和 control.
 這些活動根據用例來配置: 

+ Connect 建立了streaming  API的綁定 


+ Collect  創建了采集線程.
 
+ Compose 專注于處理數據
+ Consume  為消費系統提供處理后的數據
+  
Consign  負責數據持久化
+  Control  負責系統、數據和營養的統籌和監控。

![5-9 Streaming 數據流水線](http://upload-images.jianshu.io/upload_images/73516-b699f887eae27464?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)  

下圖介紹了流式數據流水線的概念及關鍵組件:  Spark  Streaming,  Kafka,  Flume,  和低時延數據庫.  在消費或控制型應用中,需要實時監控系統,或者在超出一定閾值時實時發送告警。

![5-10 flume kafka streaming](http://upload-images.jianshu.io/upload_images/73516-7f86ba52cae59131?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

下圖介紹了Spark 在處理單平臺運動數據和閑置數據的獨特能力,即根據不同的用例需求與多個持久化數據存儲無縫對接。

![5-11 運動數據處理](http://upload-images.jianshu.io/upload_images/73516-5c84b86dbc5e131f?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)  

這張圖是到目前為止所談論的所有概念。頂部描述了流式處理流水線。底部描述了批處理流水線 ,兩者共享位于中間的通用持久化層,包含各種模式的持久化和序列化。 

# Lambda 和Kappa 架構
現在有兩種流行的架構范式:   Lambda  和 Kappa  架構.  

Lambda  是Storm 創建者何主要提交者 Nathan  Marz 的作品.  他倡導在所有數據上構建功能型架構。批處理分支是Hadoop 的最初設想 , 是歷史數據的高時延高吞吐量的預處理,然后用于消費。實時處理分支由Storm設計的,可以增量處理流數據,快速產生結論性見解,從存儲中實時聚合數據。
Kappa 是Kafka 的主要提交者之一  Jay  Kreps 和他在 Confluent  (previously  at  LinkedIn)的同事的作品.  它倡導全部流式流水線,和前面談到的統一日志,是企業級的高效實現.

 

## 理解 Lambda 架構

Lambda 架構結合了批處理和流式處理,在所有數據上提供了統一的查詢機制。有三層:  存儲預計算信息的批處理層,增量進行流式實時處理的速度層,服務層合并了批處理和實時處理的隨機查詢。 下圖給出了 Lambda  的架構。 

![5-12 Lambda](http://upload-images.jianshu.io/upload_images/73516-38c3e27b45f22d05?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)  

## 理解Kappa 架構
Kappa 架構目標是全企業的流模式驅動,起源于  Jay  Kreps 和他在LinkedIn的同事的一個評論文章。
 從那時起,他們利用Apache
Kafka 創建了 Confluent  with  Apache,并作為
 Kappa  架構的主要促成者.  基本宗旨是使用統一日志作為企業信息架構的主干,進而移動到流處理模式。  

統一日志是一個集中的企業結構化日志,用于實時訂閱。所有組織的數據放入到一個集中的日志,記錄寫入時從零開始做標記,可以時提及日志或日志集合。統一日志的概念是 
Kappa 架構的核心宗旨.
統一日志的特點如下:
  
? Unified:  所有組織的同一部署  

? Append  only:  事件是可追加且不可修改的 


? Ordered:  每個事件在分片內有唯一的偏移量 
  


? Distributed: 出于容錯考慮,  統一日志分布于在集群的不同主機上。  



? Fast:  系統每秒攝取成千上萬的數據
   



下面的截圖展示了 Jay  Kreps 宣稱對  Lambda  架構的保留意見.  他關于Lambda架構的主要保留意見是在Hadoop  and  Storm兩個不同的系統中實現了相同的工作每個有著各自的特性,以及隨之而來的復雜性。
Kappa  通過Apache  Kafka 既處理了實時數據,也處理了歷史數據。

![5-13 Kappa](http://upload-images.jianshu.io/upload_images/73516-6690e2afef745ba5?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 

# 小結

我門列舉了流式處理架構應用的基礎,描述了他們的挑戰,約束和優勢。深入了解了Spark  Streaming  的內部工作原理包括如何Spark  Core 適用,以及與Spark  SQL  和 Spark  MLlib對話, 通過TCP sockets 解釋了流處理概念,接下來,是從Twitter 服務中實時攝取tweet。使用Kafka最大限度地增加了流處理架構的彈性,討論了上下行數據與消費者之間的解耦合。  還討論了Flume—這個可靠,靈活,伸縮性數據攝取和傳輸的流水線系統。結合Flume,  Kafka,  和Spark  在時變領域交付了非并行健壯性,速度和敏捷性.  

在結尾觀察和點評了兩種流處理架構范式  Lambda 和 Kappa  架構. Lambda  架構在通用的查詢前端結合了 批處理和流數據. 最初想法形成了 Hadoop和Storm的愿景.  Spark  有自己的批處理和流處理范式,  以共有代碼為基礎提供了單一的環境,從而有效地在現實中使用這一架構。Kappa  架構宣揚了同一日志的概念,  這創建了面向事件的架構,企業的所有數據通過信道傳輸到一個中心化的提交日志,同時對所有消費者實時使用。
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內容