一、流計算
1、流計算基本介紹
(1)、流計算秉承一個基本理念,即數據的價值隨着時間的流逝而降低,
(2)、目前有三類常見的流計算框架和平臺:商業級的流計算平臺、開源流計算框架、公司爲支持自身業務開發的流計算框架
a、商業級:IBM InfoSphere Streams和IBM StreamBase
b、較爲常見的是開源流計算框架,代表如下:
-
Twitter Storm:免費、開源的分佈式實時計算系統,可簡單、高效、可靠地處理大量的流數據
-
ahoo! S4(Simple Scalable Streaming System):開源流計算平臺,是通用的、分佈式的、可擴展的、分區容錯的、可插拔的流式系統
c、公司爲支持自身業務開發的流計算框架:
-
Facebook Puma
-
Dstream(百度)
-
銀河流數據處理平臺(淘寶)
2、流計算處理流程
傳統的數據處理流程,需要先採集數據並存儲在關係數據庫等數據管理系統中,之後由用戶通過查詢操作和數據管理系統進行交互
流計算的處理流程一般包含三個階段:數據實時採集、數據實時計算、實時查詢服務
(1)數據實時採集
通常採集多個數據源的海量數據,需要保證實時性、低延遲與穩定可靠
目前有許多互聯網公司發佈的開源分佈式日誌採集系統
-
Facebook的Scribe
-
LinkedIn的Kafka
-
淘寶的Time Tunnel
-
基於Hadoop的Chukwa和Flume
二、Spark Streaming
1、Spark Streaming基本原理
Spark Streaming可整合多種輸入數據源,如Kafka、Flume、HDFS,甚至是普通的TCP套接字。經處理後的數據可存儲至文件系統、數據庫,或顯示在儀表盤裏
Spark Streaming的基本原理是將實時輸入數據流以時間片(秒級)爲單位進行拆分,然後經Spark引擎以類似批處理的方式處理每個時間片數據
Spark Streaming最主要的抽象是DStream(Discretized Stream,離散化數據流),表示連續不斷的數據流。在內部實現上,Spark Streaming的輸入數據按照時間片(如1秒)分成一段一段,每一段數據轉換爲Spark中的RDD,這些分段就是Dstream,並且對DStream的操作都最終轉變爲對相應的RDD的操作
2、Spark Streaming與Storm的對比
-
Spark Streaming和Storm最大的區別在於,Spark Streaming無法實現毫秒級的流計算,而Storm可以實現毫秒級響應
-
Spark Streaming構建在Spark上,一方面是因爲Spark的低延遲執行引擎(100ms+)可以用於實時計算,另一方面,相比於Storm,RDD數據集更容易做高效的容錯處理
-
Spark Streaming採用的小批量處理的方式使得它可以同時兼容批量和實時數據處理的邏輯和算法,因此,方便了一些需要歷史數據和實時數據聯合分析的特定應用場合
3、DStream操作概述
(1)、Spark Streaming工作機制
-
在Spark Streaming中,會有一個組件 Receiver,作爲一個長期運行的 task 跑在一個Executor上
-
每個Receiver都會負責一個input DStream(比如從文件中讀取數據的文件流,比如套接字流,或者從Kafka中讀取的一個輸入流等等)
-
Spark Streaming通過input DStream與外部數據源進行連接,讀取相關數據
(2)Spark Streaming程序的基本步驟
-
通過創建輸入DStream來定義輸入源
-
通過對DStream應用轉換操作和輸出操作來定義流計算
-
用streamingContext.start()來開始接收數據和處理流程
-
通過streamingContext.awaitTermination()方法來等待處理結束(手動結束或因爲錯誤而結束)
-
可以通過 streamingContext.stop() 來手動結束流計算進程
(3)、創建StreamingContext對象
import org.apache.spark._
import org.apache.spark.streaming._
val conf = new SparkConf().setAppName("TestDStream").setMaster("local[2]")
val ssc = new StreamingContext(conf, Seconds(1))
4、 基本數據源
(1)文件流
cd /usr/local/spark/mycode
mkdir streaming
cd streaming
mkdir logfile
cd logfile
import org.apache.spark._
import org.apache.spark.streaming._
object WordCountStreaming {
def main(args: Array[String]) {
//設置爲本地運行模式,2個線程,一個監聽,另一個處理數據
val sparkConf = new SparkConf().setAppName("WordCountStreaming").setMaster("local[2]")
val ssc = new StreamingContext(sparkConf, Seconds(2))// 時間間隔爲2秒
//這裏採用本地文件,當然你也可以採用HDFS文件
val lines = ssc.textFileStream("file:///usr/local/spark/mycode/streaming/logfile")
val words = lines.flatMap(_.split(" "))
val wordCounts = words.map(x => (x, 1)).reduceByKey(_ + _)
wordCounts.print()
ssc.start()
ssc.awaitTermination()
}
}
在“/usr/local/spark/mycode/streaming/logfile”目錄下新建一個log.txt文件,就可以在監聽窗口中顯示詞頻統計結果
(2)套接字流
a、Socket工作原理
b、Streaming代碼
NetworkWordCount
import org.apache.spark._
import org.apache.spark.streaming._
import org.apache.spark.storage.StorageLevel
object NetworkWordCount {
def main(args: Array[String]) {
if (args.length < 2) {
System.err.println("Usage: NetworkWordCount <hostname> <port>")
System.exit(1)
}
StreamingExamples.setStreamingLogLevels()
val sparkConf = new SparkConf().setAppName("NetworkWordCount").setMaster("local[2]")
val ssc = new StreamingContext(sparkConf, Seconds(1))
val lines = ssc.socketTextStream(args(0), args(1).toInt, StorageLevel.MEMORY_AND_DISK_SER)
val words = lines.flatMap(_.split(" "))
val wordCounts = words.map(x => (x, 1)).reduceByKey(_ + _)
wordCounts.print()
ssc.start()
ssc.awaitTermination()
}
}
StreamingExamples
package org.apache.spark.examples.streaming
import org.apache.spark.internal.Logging
import org.apache.log4j.{Level, Logger}
/** Utility functions for Spark Streaming examples. */
object StreamingExamples extends Logging {
/** Set reasonable logging levels for streaming if the user has not configured log4j. */
def setStreamingLogLevels() {
val log4jInitialized = Logger.getRootLogger.getAllAppenders.hasMoreElements
if (!log4jInitialized) {
// We first log something to initialize Spark's default logging, then we override the
// logging level.
logInfo("Setting log level to [WARN] for streaming example." +
" To override add a custom log4j.properties to the classpath.")
Logger.getRootLogger.setLevel(Level.WARN)
}
}
}
c、構建Socket數據源
第一種方式linux 下
#可以在nc窗口中隨意輸入一些單詞,監聽窗口就會自動獲得單詞數據流信息,
nc -lk 9999
第二種方式:自定義數據源
DataSourceSocket
Import java.io.{PrintWriter}
Import java.net.ServerSocket
Import scala.io.Source
object DataSourceSocket {
def index(length: Int) = {
val rdm = new java.util.Random
rdm.nextInt(length)
}
def main(args: Array[String]) {
if (args.length != 3) {
System.err.println("Usage: <filename> <port> <millisecond>")
System.exit(1)
}
val fileName = args(0)
val lines = Source.fromFile(fileName).getLines.toList
val rowCount = lines.length
val listener = new ServerSocket(args(1).toInt)
while (true) {
val socket = listener.accept()
new Thread() {
override def run = {
println("Got client connected from: " + socket.getInetAddress)
val out = new PrintWriter(socket.getOutputStream(), true)
while (true) {
Thread.sleep(args(2).toLong)
val content = lines(index(rowCount))
println(content)
out.write(content + '\n')
out.flush()
}
socket.close()
}
}.start()
}
}
}
這個窗口會不斷打印出一些隨機讀取到的文本信息,這些信息也是Socket數據源,會被監聽程序捕捉到
(3)RDD隊列流
在調試 Spark Streaming 應用程序的時候,我們可以使用streamingContext.queueStream(queueOfRDD)創建基於RDD隊列的DStream
新建一個 QueueStream.scala 代碼文件,功能是:每隔1秒創建一個RDD,Streaming每隔2秒就對數據進行處理
import org.apache.spark.SparkConf
import org.apache.spark.rdd.RDD
import org.apache.spark.streaming.{Seconds, StreamingContext}
object QueueStream {
def main(args: Array[String]) {
val sparkConf = new SparkConf().setAppName("TestRDDQueue").setMaster("local[2]")
val ssc = new StreamingContext(sparkConf, Seconds(2))
val rddQueue =new scala.collection.mutable.SynchronizedQueue[RDD[Int]]()
val queueStream = ssc.queueStream(rddQueue)
val mappedStream = queueStream.map(r => (r % 10, 1))
val reducedStream = mappedStream.reduceByKey(_ + _)
reducedStream.print()
ssc.start()
for (i <- 1 to 10){
rddQueue += ssc.sparkContext.makeRDD(1 to 100,2)
Thread.sleep(1000)
}
ssc.stop()
}
}