新手福利:Apache Spark入門攻略

新手福利:Apache Spark入門攻略

發表於2015-07-10 18:07|5734次閱讀| 來源Dzone|10 條評論| 作者Ashwini Kuntamukkala
摘要:本文聚焦Apache Spark入門,瞭解其在大數據領域的地位,覆蓋Apache Spark的安裝及應用程序的建立,並解釋一些常見的行爲和操作。

【編者按】時至今日,Spark已成爲大數據領域最火的一個開源項目,具備高性能、易於使用等特性。然而作爲一個年輕的開源項目,其使用上存在的挑戰亦不可爲不大,這裏爲大家分享SciSpike軟件架構師Ashwini Kuntamukkala在Dzone上進行的Spark入門總結(雖然有些地方基於的是Spark 1.0版本,但仍然值得閱讀)—— Apache Spark:An Engine for Large-Scale Data Processing,由 OneAPM工程師翻譯。

本文聚焦Apache Spark入門,瞭解其在大數據領域的地位,覆蓋Apache Spark的安裝及應用程序的建立,並解釋一些常見的行爲和操作。

一、 爲什麼要使用Apache Spark

時下,我們正處在一個“大數據”的時代,每時每刻,都有各種類型的數據被生產。而在此紫外,數據增幅的速度也在顯著增加。從廣義上看,這些數據包含交易數據、社交媒體內容(比如文本、圖像和視頻)以及傳感器數據。那麼,爲什麼要在這些內容上投入如此多精力,其原因無非就是從海量數據中提取洞見可以對生活和生產實踐進行很好的指導。

在幾年前,只有少部分公司擁有足夠的技術力量和資金去儲存和挖掘大量數據,並對其挖掘從而獲得洞見。然而,被雅虎2009年開源的Apache Hadoop對這一狀況產生了顛覆性的衝擊——通過使用商用服務器組成的集羣大幅度地降低了海量數據處理的門檻。因此,許多行業(比如Health care、Infrastructure、Finance、Insurance、Telematics、Consumer、Retail、Marketing、E-commerce、Media、 Manufacturing和Entertainment)開始了Hadoop的征程,走上了海量數據提取價值的道路。着眼Hadoop,其主要提供了兩個方面的功能:

  • 通過水平擴展商用主機,HDFS提供了一個廉價的方式對海量數據進行容錯存儲。
  • MapReduce計算範例,提供了一個簡單的編程模型來挖掘數據並獲得洞見。

下圖展示了MapReduce的數據處理流程,其中一個Map-Reduce step的輸出將作爲下一個典型Hadoop job的輸入結果。


在整個過程中,中間結果會藉助磁盤傳遞,因此對比計算,大量的Map-Reduced作業都受限於IO。然而對於ETL、數據整合和清理這樣的用例來說,IO約束並不會產生很大的影響,因爲這些場景對數據處理時間往往不會有較高的需求。然而,在現實世界中,同樣存在許多對延時要求較爲苛刻的用例,比如:

  1. 對流數據進行處理來做近實時分析。舉個例子,通過分析點擊流數據做視頻推薦,從而提高用戶的參與度。在這個用例中,開發者必須在精度和延時之間做平衡。
  2. 在大型數據集上進行交互式分析,數據科學家可以在數據集上做ad-hoc查詢。


毫無疑問,歷經數年發展,Hadoop生態圈中的豐富工具已深受用戶喜愛,然而這裏仍然存在衆多問題給使用帶來了挑戰:

1.每個用例都需要多個不同的技術堆棧來支撐,在不同使用場景下,大量的解決方案往往捉襟見肘。

2.在生產環境中機構往往需要精通數門技術。

3.許多技術存在版本兼容性問題。

4.無法在並行job中更快地共享數據。

而通過Apache Spark,上述問題迎刃而解!Apache Spark是一個輕量級的內存集羣計算平臺,通過不同的組件來支撐批、流和交互式用例,如下圖。


二、 關於Apache Spark

Apache Spark是個開源和兼容Hadoop的集羣計算平臺。由加州大學伯克利分校的AMPLabs開發,作爲Berkeley Data Analytics Stack(BDAS)的一部分,當下由大數據公司Databricks保駕護航,更是Apache旗下的頂級項目,下圖顯示了Apache Spark堆棧中的不同組件。


Apache Spark的5大優勢:

1.更高的性能,因爲數據被加載到集羣主機的分佈式內存中。數據可以被快速的轉換迭代,並緩存用以後續的頻繁訪問需求。很多對Spark感興趣的朋友可能也會聽過這樣一句話——在數據全部加載到內存的情況下,Spark可以比Hadoop快100倍,在內存不夠存放所有數據的情況下快Hadoop 10倍。

2.通過建立在Java、Scala、Python、SQL(應對交互式查詢)的標準API以方便各行各業使用,同時還含有大量開箱即用的機器學習庫。

3.與現有Hadoop v1 (SIMR) 和2.x (YARN) 生態兼容,因此機構可以進行無縫遷移。


4.方便下載和安裝。方便的shell(REPL: Read-Eval-Print-Loop)可以對API進行交互式的學習。

5.藉助高等級的架構提高生產力,從而可以講精力放到計算上。

同時,Apache Spark由Scala實現,代碼非常簡潔。

三、安裝Apache Spark

下表列出了一些重要鏈接和先決條件:

Current Release 1.0.1 @ http://d3kbcqa49mib13.cloudfront.net/spark-1.0.1.tgz
Downloads Page https://spark.apache.org/downloads.html
JDK Version (Required) 1.6 or higher
Scala Version (Required) 2.10 or higher
Python (Optional) [2.6, 3.0)
Simple Build Tool (Required) http://www.scala-sbt.org
Development Version git clone git://github.com/apache/spark.git
Building Instructions https://spark.apache.org/docs/latest/building-with-maven.html
Maven 3.0 or higher

如圖6所示,Apache Spark的部署方式包括standalone、Hadoop V1 SIMR、Hadoop 2 YARN/Mesos。Apache Spark需求一定的Java、Scala或Python知識。這裏,我們將專注standalone配置下的安裝和運行。

1.安裝JDK 1.6+、Scala 2.10+、Python [2.6,3] 和sbt

2.下載Apache Spark 1.0.1 Release

3.在指定目錄下Untar和Unzip spark-1.0.1.tgz 

akuntamukkala@localhost~/Downloads$ pwd 
/Users/akuntamukkala/Downloads akuntamukkala@localhost~/Downloads$ tar -zxvf spark- 1.0.1.tgz -C /Users/akuntamukkala/spark

4.運行sbt建立Apache Spark

akuntamukkala@localhost~/spark/spark-1.0.1$ pwd /Users/akuntamukkala/spark/spark-1.0.1 akuntamukkala@localhost~/spark/spark-1.0.1$ sbt/sbt assembly

5.發佈Scala的Apache Spark standalone REPL

/Users/akuntamukkala/spark/spark-1.0.1/bin/spark-shell

如果是Python

/Users/akuntamukkala/spark/spark-1.0.1/bin/ pyspark

6.查看SparkUI @ http://localhost:4040

四、Apache Spark的工作模式

Spark引擎提供了在集羣中所有主機上進行分佈式內存數據處理的能力,下圖顯示了一個典型Spark job的處理流程。


下圖顯示了Apache Spark如何在集羣中執行一個作業。


Master控制數據如何被分割,利用了數據本地性,並在Slaves上跟蹤所有分佈式計算。在某個Slave不可用時,其存儲的數據會分配給其他可用的Slaves。雖然當下(1.0.1版本)Master還存在單點故障,但後期必然會被修復。

五、彈性分佈式數據集(Resilient Distributed Dataset,RDD)

彈性分佈式數據集(RDD,從Spark 1.3版本開始已被DataFrame替代)是Apache Spark的核心理念。它是由數據組成的不可變分佈式集合,其主要進行兩個操作:transformation和action。Transformation是類似在RDD上做 filter()、map()或union() 以生成另一個RDD的操作,而action則是count()、first()、take(n)、collect() 等促發一個計算並返回值到Master或者穩定存儲系統的操作。Transformations一般都是lazy的,直到action執行後纔會被執行。Spark Master/Driver會保存RDD上的Transformations。這樣一來,如果某個RDD丟失(也就是salves宕掉),它可以快速和便捷地轉換到集羣中存活的主機上。這也就是RDD的彈性所在。

下圖展示了Transformation的lazy:


我們可以通過下面示例來理解這個概念:從文本中發現5個最常用的word。下圖顯示了一個可能的解決方案。


在上面命令中,我們對文本進行讀取並且建立字符串的RDD。每個條目代表了文本中的1行。

scala> val hamlet = sc.textFile(“/Users/akuntamukkala/temp/gutenburg.txt”)
hamlet: org.apache.spark.rdd.RDD[String] = MappedRDD[1] at textFile at <console>:12
scala> val topWordCount = hamlet.flatMap(str=>str.split(“ “)). filter(!_.isEmpty).map(word=>(word,1)).reduceByKey(_+_).map{case (word, count) => (count, word)}.sortByKey(false)
topWordCount: org.apache.spark.rdd.RDD[(Int, String)] = MapPartitionsRDD[10] at sortByKey at <console>:14

1. 通過上述命令我們可以發現這個操作非常簡單——通過簡單的Scala API來連接transformations和actions。

2. 可能存在某些words被1個以上空格分隔的情況,導致有些words是空字符串,因此需要使用filter(!_.isEmpty)將它們過濾掉。

3. 每個word都被映射成一個鍵值對:map(word=>(word,1))。

4. 爲了合計所有計數,這裏需要調用一個reduce步驟——reduceByKey(_+_)。 _+_ 可以非常便捷地爲每個key賦值。

5. 我們得到了words以及各自的counts,下一步需要做的是根據counts排序。在Apache Spark,用戶只能根據key排序,而不是值。因此,這裏需要使用map{case (word, count) => (count, word)}將(word, count)流轉到(count, word)。

6. 需要計算最常用的5個words,因此需要使用sortByKey(false)做一個計數的遞減排序。

上述命令包含了一個.take(5) (an action operation, which triggers computation)和在 /Users/akuntamukkala/temp/gutenburg.txt文本中輸出10個最常用的words。在Python shell中用戶可以實現同樣的功能。

RDD lineage可以通過toDebugString(一個值得記住的操作)來跟蹤。

scala> topWordCount.take(5).foreach(x=>println(x))
(1044,the)
(730,and)
(679,of)
(648,to)
(511,I)

常用的Transformations:

Transformation & Purpose Example & Result
filter(func) Purpose: new RDD by selecting those data elements on which func returns true scala> val rdd = sc.parallelize(List(“ABC”,”BCD”,”DEF”)) scala> val filtered = rdd.filter(_.contains(“C”)) scala> filtered.collect() Result:
Array[String] = Array(ABC, BCD)
map(func) Purpose: return new RDD by applying func on each data element scala> val rdd=sc.parallelize(List(1,2,3,4,5)) scala> val times2 = rdd.map(_*2) scala> times2.collect() Result:
Array[Int] = Array(2, 4, 6, 8, 10)
flatMap(func) Purpose: Similar to map but func returns a Seq instead of a value. For example, mapping a sentence into a Seq of words scala> val rdd=sc.parallelize(List(“Spark is awesome”,”It is fun”)) scala> val fm=rdd.flatMap(str=>str.split(“ “)) scala> fm.collect() Result:
Array[String] = Array(Spark, is, awesome, It, is, fun)
reduceByKey(func,[numTasks]) Purpose: To aggregate values of a key using a function. “numTasks” is an optional parameter to specify number of reduce tasks scala> val word1=fm.map(word=>(word,1)) scala> val wrdCnt=word1.reduceByKey(_+_) scala> wrdCnt.collect() Result:
Array[(String, Int)] = Array((is,2), (It,1), (awesome,1), (Spark,1), (fun,1))
groupByKey([numTasks]) Purpose: To convert (K,V) to (K,Iterable<V>) scala> val cntWrd = wrdCnt.map{case (word, count) => (count, word)} scala> cntWrd.groupByKey().collect() Result:
Array[(Int, Iterable[String])] = Array((1,ArrayBuffer(It, awesome, Spark, fun)), (2,ArrayBuffer(is)))
distinct([numTasks]) Purpose: Eliminate duplicates from RDD scala> fm.distinct().collect() Result:
Array[String] = Array(is, It, awesome, Spark, fun)

常用的集合操作:

Transformation and Purpose Example and Result
union()
Purpose: new RDD containing all elements from source RDD and argument.
Scala> val rdd1=sc.parallelize(List(‘A’,’B’))
scala> val rdd2=sc.parallelize(List(‘B’,’C’))
scala> rdd1.union(rdd2).collect()
Result:
Array[Char] = Array(A, B, B, C)
intersection()
Purpose: new RDD containing only common elements from source RDD and argument.
Scala> rdd1.intersection(rdd2).collect()
Result:
Array[Char] = Array(B)
cartesian()
Purpose: new RDD cross product of all elements from source RDD and argument
Scala> rdd1.cartesian(rdd2).collect()
Result:
Array[(Char, Char)] = Array((A,B), (A,C), (B,B), (B,C))
subtract()
Purpose: new RDD created by removing data elements in source RDD in common with argument
scala> rdd1.subtract(rdd2).collect() Result:
Array[Char] = Array(A)
join(RDD,[numTasks])
Purpose: When invoked on (K,V) and (K,W), this operation creates a new RDD of (K, (V,W))
scala> val personFruit = sc.parallelize(Seq((“Andy”, “Apple”), (“Bob”, “Banana”), (“Charlie”, “Cherry”), (“Andy”,”Apricot”)))
scala> val personSE = sc.parallelize(Seq((“Andy”, “Google”), (“Bob”, “Bing”), (“Charlie”, “Yahoo”), (“Bob”,”AltaVista”)))
scala> personFruit.join(personSE).collect()
Result:
Array[(String, (String, String))] = Array((Andy,(Apple,Google)), (Andy,(Apricot,Google)), (Charlie,(Cherry,Yahoo)), (Bob,(Banana,Bing)), (Bob,(Banana,AltaVista)))
cogroup(RDD,[numTasks])
Purpose: To convert (K,V) to (K,Iterable<V>)
scala> personFruit.cogroup(personSe).collect()
Result:
Array[(String, (Iterable[String], Iterable[String]))] = Array((Andy,(ArrayBuffer(Apple, Apricot),ArrayBuffer(google))), (Charlie,(ArrayBuffer(Cherry),ArrayBuffer(Yahoo))), (Bob,(ArrayBuffer(Banana),ArrayBuffer(Bing, AltaVista))))

更多transformations信息,請查看http://spark.apache.org/docs/latest/programming-guide.html#transformations

常用的actions

Action & Purpose Example & Result
count() Purpose: get the number of data elements in the RDD scala> val rdd = sc.parallelize(list(‘A’,’B’,’c’)) scala> rdd.count() Result:
long = 3
collect() Purpose: get all the data elements in an RDD as an array scala> val rdd = sc.parallelize(list(‘A’,’B’,’c’)) scala> rdd.collect() Result:
Array[char] = Array(A, B, c)
reduce(func) Purpose: Aggregate the data elements in an RDD using this function which takes two arguments and returns one scala> val rdd = sc.parallelize(list(1,2,3,4)) scala> rdd.reduce(_+_) Result:
Int = 10
take (n) Purpose: : fetch first n data elements in an RDD. computed by driver program. Scala> val rdd = sc.parallelize(list(1,2,3,4)) scala> rdd.take(2) Result:
Array[Int] = Array(1, 2)
foreach(func) Purpose: execute function for each data element in RDD. usually used to update an accumulator(discussed later) or interacting with external systems. Scala> val rdd = sc.parallelize(list(1,2,3,4)) scala> rdd.foreach(x=>println(“%s*10=%s”. format(x,x*10))) Result:
1*10=10 4*10=40 3*10=30 2*10=20
first() Purpose: retrieves the first data element in RDD. Similar to take(1) scala> val rdd = sc.parallelize(list(1,2,3,4)) scala> rdd.first() Result:
Int = 1
saveAsTextFile(path) Purpose: Writes the content of RDD to a text file or a set of text files to local file system/ HDFS scala> val hamlet = sc.textFile(“/users/akuntamukkala/ temp/gutenburg.txt”) scala> hamlet.filter(_.contains(“Shakespeare”)). saveAsTextFile(“/users/akuntamukkala/temp/ filtered”) Result:
akuntamukkala@localhost~/temp/filtered$ ls _SUCCESS part-00000 part-00001

更多actions參見http://spark.apache.org/docs/latest/programming-guide.html#actions 

六、RDD持久性

Apache Spark中一個主要的能力就是在集羣內存中持久化/緩存RDD。這將顯著地提升交互速度。下表顯示了Spark中各種選項。

Storage Level Purpose
MEMORY_ONLY (Default level) This option stores RDD in available cluster memory as deserialized Java objects. Some partitions may not be cached if there is not enough cluster memory. Those partitions will be recalculated on the fly as needed.
MEMORY_AND_DISK This option stores RDD as deserialized Java objects. If RDD does not fit in cluster memory, then store those partitions on the disk and read them as needed.
MEMORY_ONLY_SER This options stores RDD as serialized Java objects (One byte array per partition). This is more CPU intensive but saves memory as it is more space efficient. Some partitions may not be cached. Those will be recalculated on the fly as needed.
MEMORY_ONLY_DISK_SER This option is same as above except that disk is used when memory is not sufficient.
DISC_ONLY This option stores the RDD only on the disk
MEMORY_ONLY_2, MEMORY_AND_DISK_2, etc. Same as other levels but partitions are replicated on 2 slave nodes

上面的存儲等級可以通過RDD. cache()操作上的 persist()操作訪問,可以方便地指定MEMORY_ONLY選項。關於持久化等級的更多信息,可以訪問這裏http://spark.apache.org/docs/latest/programming-guide.html#rdd-persistence。

Spark使用Least Recently Used (LRU)算法來移除緩存中舊的、不常用的RDD,從而釋放出更多可用內存。同樣還提供了一個unpersist() 操作來強制移除緩存/持久化的RDD。

七、變量共享

Accumulators。Spark提供了一個非常便捷地途徑來避免可變的計數器和計數器同步問題——Accumulators。Accumulators在一個Spark context中通過默認值初始化,這些計數器在Slaves節點上可用,但是Slaves節點不能對其進行讀取。它們的作用就是來獲取原子更新,並將其轉發到Master。Master是唯一可以讀取和計算所有更新合集的節點。舉個例子:

akuntamukkala@localhost~/temp$ cat output.log
error
warning
info
trace
error
info
info
scala> val nErrors=sc.accumulator(0.0)
scala> val logs = sc.textFile(“/Users/akuntamukkala/temp/output.log”)
scala> logs.filter(_.contains(“error”)).foreach(x=>nErrors+=1)
scala> nErrors.value
Result:Int = 2

Broadcast Variables。實際生產中,通過指定key在RDDs上對數據進行合併的場景非常常見。在這種情況下,很可能會出現給slave nodes發送大體積數據集的情況,讓其負責託管需要做join的數據。因此,這裏很可能存在巨大的性能瓶頸,因爲網絡IO比內存訪問速度慢100倍。爲了解決這個問題,Spark提供了Broadcast Variables,如其名稱一樣,它會向slave nodes進行廣播。因此,節點上的RDD操作可以快速訪問Broadcast Variables值。舉個例子,期望計算一個文件中所有路線項的運輸成本。通過一個look-up table指定每種運輸類型的成本,這個look-up table就可以作爲Broadcast Variables。

akuntamukkala@localhost~/temp$ cat packagesToShip.txt ground
express
media
priority
priority
ground
express
media
scala> val map = sc.parallelize(Seq((“ground”,1),(“med”,2), (“priority”,5),(“express”,10))).collect().toMap
map: scala.collection.immutable.Map[String,Int] = Map(ground -> 1, media -> 2, priority -> 5, express -> 10)
scala> val bcMailRates = sc.broadcast(map)

上述命令中,我們建立了一個broadcast variable,基於服務類別成本的map。

scala> val pts = sc.textFile(“/Users/akuntamukkala/temp/packagesToShip.txt”)

在上述命令中,我們通過broadcast variable的mailing rates來計算運輸成本。

scala> pts.map(shipType=>(shipType,1)).reduceByKey(_+_). map{case (shipType,nPackages)=>(shipType,nPackages*bcMailRates. value(shipType))}.collect()

通過上述命令,我們使用accumulator來累加所有運輸的成本。詳細信息可通過下面的PDF查看http://ampcamp.berkeley.edu/wp-content/uploads/2012/06/matei-zaharia-amp-camp-2012-advanced-spark.pdf。

八、Spark SQL

通過Spark Engine,Spark SQL提供了一個便捷的途徑來進行交互式分析,使用一個被稱爲SchemaRDD類型的RDD。SchemaRDD可以通過已有RDDs建立,或者其他外部數據格式,比如Parquet files、JSON數據,或者在Hive上運行HQL。SchemaRDD非常類似於RDBMS中的表格。一旦數據被導入SchemaRDD,Spark引擎就可以對它進行批或流處理。Spark SQL提供了兩種類型的Contexts——SQLContext和HiveContext,擴展了SparkContext的功能。

SparkContext提供了到簡單SQL parser的訪問,而HiveContext則提供了到HiveQL parser的訪問。HiveContext允許企業利用已有的Hive基礎設施。

這裏看一個簡單的SQLContext示例。

下面文本中的用戶數據通過“|”來分割。

John Smith|38|M|201 East Heading Way #2203,Irving, TX,75063 Liana Dole|22|F|1023 West Feeder Rd, Plano,TX,75093 Craig Wolf|34|M|75942 Border Trail,Fort Worth,TX,75108 John Ledger|28|M|203 Galaxy Way,Paris, TX,75461 Joe Graham|40|M|5023 Silicon Rd,London,TX,76854

定義Scala case class來表示每一行:

case class Customer(name:String,age:Int,gender:String,address: String)

下面的代碼片段體現瞭如何使用SparkContext來建立SQLContext,讀取輸入文件,將每一行都轉換成SparkContext中的一條記錄,並通過簡單的SQL語句來查詢30歲以下的男性用戶。

val sparkConf = new SparkConf().setAppName(“Customers”)
val sc = new SparkContext(sparkConf)
val sqlContext = new SQLContext(sc)
val r = sc.textFile(“/Users/akuntamukkala/temp/customers.txt”) val records = r.map(_.split(‘|’))
val c = records.map(r=>Customer(r(0),r(1).trim.toInt,r(2),r(3))) c.registerAsTable(“customers”)
sqlContext.sql(“select * from customers where gender=’M’ and age <
            30”).collect().foreach(println) Result:[John Ledger,28,M,203 Galaxy Way,Paris,
            TX,75461]

更多使用SQL和HiveQL的示例請訪問下面鏈接https://spark.apache.org/docs/latest/sql-programming-guide.html、https://databricks-training.s3.amazonaws.com/data-exploration-using-spark-sql.html。


九、Spark Streaming

Spark Streaming提供了一個可擴展、容錯、高效的途徑來處理流數據,同時還利用了Spark的簡易編程模型。從真正意義上講,Spark Streaming會將流數據轉換成micro batches,從而將Spark批處理編程模型應用到流用例中。這種統一的編程模型讓Spark可以很好地整合批量處理和交互式流分析。下圖顯示了Spark Streaming可以從不同數據源中讀取數據進行分析。


Spark Streaming中的核心抽象是Discretized Stream(DStream)。DStream由一組RDD組成,每個RDD都包含了規定時間(可配置)流入的數據。圖12很好地展示了Spark Streaming如何通過將流入數據轉換成一系列的RDDs,再轉換成DStream。每個RDD都包含兩秒(設定的區間長度)的數據。在Spark Streaming中,最小長度可以設置爲0.5秒,因此處理延時可以達到1秒以下。

Spark Streaming同樣提供了 window operators,它有助於更有效率在一組RDD( a rolling window of time)上進行計算。同時,DStream還提供了一個API,其操作符(transformations和output operators)可以幫助用戶直接操作RDD。下面不妨看向包含在Spark Streaming下載中的一個簡單示例。示例是在Twitter流中找出趨勢hashtags,詳見下面代碼。

spark-1.0.1/examples/src/main/scala/org/apache/spark/examples/streaming/TwitterPopularTags.scala
val sparkConf = new SparkConf().setAppName(“TwitterPopularTags”)
val ssc = new StreamingContext(sparkConf, Seconds(2))
val stream = TwitterUtils.createStream(ssc, None, filters)

上述代碼用於建立Spark Streaming Context。Spark Streaming將在DStream中建立一個RDD,包含了每2秒流入的tweets。

val hashTags = stream.flatMap(status => status.getText.split(“ “).filter(_.startsWith(“#”)))

上述代碼片段將Tweet轉換成一組words,並過濾出所有以a#開頭的。

val topCounts60 = hashTags.map((_, 1)).reduceByKeyAndWindow(_ + _, Seconds(60)).map{case (topic, count) => (count, topic)}. transform(_.sortByKey(false))

上述代碼展示瞭如何整合計算60秒內一個hashtag流入的總次數。

topCounts60.foreachRDD(rdd => {
val topList = rdd.take(10)
println(“\nPopular topics in last 60 seconds (%s
total):”.format(rdd.count())) topList.foreach{case (count, tag) => println(“%s (%s
tweets)”.format(tag, count))} })

上面代碼將找出top 10趨勢tweets,然後將其打印。

ssc.start()

上述代碼讓Spark Streaming Context 開始檢索tweets。一起聚焦一些常用操作,假設我們正在從一個socket中讀入流文本。

al lines = ssc.socketTextStream(“localhost”, 9999, StorageLevel.MEMORY_AND_DISK_SER)


更多operators請訪問http://spark.apache.org/docs/latest/streaming-programming-guide.html#transformations

Spark Streaming擁有大量強大的output operators,比如上文提到的 foreachRDD(),瞭解更多可訪問   http://spark.apache.org/docs/latest/streaming-programming-guide.html#output-operations

十、附加學習資源

原文鏈接:Apache Spark:An Engine for Large-Scale Data Processing (責編/仲浩)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章