spark-Streaming

總結(jié)一下,避免后面再重復(fù)踩坑。

Spark Streaming是近實(shí)時(shí)(near real time)的小批處理系統(tǒng), 可以對(duì)接各類消息中間或者直接監(jiān)控Hdfs目錄, 可以做為實(shí)時(shí)大數(shù)據(jù)流式計(jì)算,也可以做一些按時(shí)間窗口的數(shù)據(jù)聚合分析,比如流量監(jiān)控之類的, 主要的優(yōu)勢(shì)是和spark-sql, spark-mlib, spark-graphx無(wú)縫結(jié)合的生態(tài)系統(tǒng)。

官方地址: http://spark.apache.org/docs/2.2.0/streaming-programming-guide.html

Spark Streaming

上游數(shù)據(jù)可以是Kafka, Flume, Hdfs或者是TCP Sockets;處理后的下游數(shù)據(jù)可以是落到HDFS, 數(shù)據(jù)庫(kù), 或者重新寫(xiě)回消息中間件,隨意處理。

maven環(huán)境

<dependency>
 <groupId>org.apache.spark</groupId>
<artifactId>spark-streaming_2.11</artifactId>
<version>2.2.0</version>`
</dependency>

spark-streaming2.20適配的消息中間件

Source Artifact
Kafka spark-streaming-kafka-0-8_2.11
Flume spark-streaming-flume_2.11
Kinesis spark-streaming-kinesis-asl_2.11 [Amazon Software License]

官方給了一些例子:

nc -lk 9999 同一臺(tái)機(jī)器上socket, 端口9999

$ ./bin/run-example streaming.NetworkWordCount localhost 9999

Spark Streaming的優(yōu)勢(shì)在于:

  • 能運(yùn)行在100+的結(jié)點(diǎn)上,并達(dá)到秒級(jí)延遲(最小設(shè)置batch-time為500ms,再小就很容易task大量堆積)。

  • 使用基于內(nèi)存的Spark作為執(zhí)行引擎,具有高效和容錯(cuò)的特性。

  • 能集成Spark的批處理和交互查詢。

  • 為實(shí)現(xiàn)復(fù)雜的算法提供和批處理類似的簡(jiǎn)單接口

spark Streaming封裝了kafka的高級(jí)接口: Kafka Integration Guide.

Spark Streaming

DStream是spark-streaming提供的一個(gè)抽象數(shù)據(jù)類型, 就是按時(shí)間切分的一組有序RDD集合。

關(guān)于更多的概念和方法參考官網(wǎng)教程, 這里總結(jié)一下使用的一些坑和優(yōu)化:

一, kerberos 認(rèn)證問(wèn)題:

問(wèn)題: 我們的hadoop訪問(wèn)有kerberos的認(rèn)證機(jī)制,默認(rèn)是7天更換,剛開(kāi)始沒(méi)注意這個(gè)問(wèn)題,spark-streaming的程序每隔一周崩一次

解決:

  1. --deploy-mode 由 yarn-client模式改為yarn-cluster模式;

  2. --keytab /home/xxx/xxx.keytab --principal xxx@cloudera.xxx.com (剛開(kāi)始客戶端是2.1.0沒(méi)生效,升級(jí)為2.2.0)

二, 優(yōu)雅結(jié)束:

問(wèn)題:application被人為中斷,當(dāng)前batch的數(shù)據(jù)沒(méi)處理完

解決:源代碼在spark.stop() 之前加了一個(gè)鉤子, 來(lái)達(dá)到優(yōu)雅退出, 保存斷點(diǎn)checkpoint
--conf spark.streaming.stopGracefullyOnShutdown=true;

也可以自己在JVM關(guān)閉之前添加鉤子, 來(lái)附加做一些郵件報(bào)警之類的事情(發(fā)送kill命令關(guān)閉driver進(jìn)程,不要使用(-9)強(qiáng)制關(guān)閉,不然鉤子無(wú)法捕獲)

Runtime.getRuntime().addShutdownHook(

         new Thread() { override def run() {`

            log("Gracefully stop Spark Streaming")            `

              streamingContext.stop(true, true) } }`

      )

三, 數(shù)據(jù)緩存和清除:

cache或者persist的數(shù)據(jù)一定要在foreachRDD中清除掉,不然內(nèi)存爆炸

spark.streaming.unpersist=true 這個(gè)配置只是自動(dòng)推測(cè)并清除緩存數(shù)據(jù), 最好還是代碼中處理

四,batch的最大處理量,

根據(jù)內(nèi)存和batchDuration設(shè)定合理的值, 保證batchDuration時(shí)間內(nèi)能處理完,不造成堆積, 也和流數(shù)據(jù)大小有關(guān)。

– conf spark.streaming.kafka.maxRatePerPartition=1000

五, 應(yīng)用程序失敗自動(dòng)重啟次數(shù), 和重試間隔

  --conf spark.yarn.maxAppAttempts=4
  --conf [spark.yarn.am](http://spark.yarn.am).attemptFailuresValidityInterval=1h

六,使用YARN Capacity Scheduler調(diào)度, 且提交到單獨(dú)的Yarn隊(duì)列

     --queue realtime_queue

七,開(kāi)啟spark推測(cè)執(zhí)行

# 推測(cè)執(zhí)行開(kāi)啟

spark.speculation                     true

# 檢測(cè)周期

spark.speculation.interval 100

# 完成task的百分比時(shí)啟動(dòng)推測(cè)

spark.speculation.quantile 0.75

# 比其他的慢多少倍時(shí)啟動(dòng)推測(cè)

spark.speculation.multiplier 1.5

八, 避免單個(gè)任務(wù)阻塞:

spark.streaming.concurrentJobs=4

九,合理的batchDuration:

不要小于500ms, 太小,會(huì)積壓數(shù)據(jù), 太大,實(shí)時(shí)性不好

十,合理GC: 開(kāi)啟并行Mark-Sweep垃圾回收機(jī)制, 其它的參照J(rèn)VM的調(diào)優(yōu),減少full-GC

--conf "spark.executor.extraJavaOptions=-XX:+UseConcMarkSweepGC"

十一,計(jì)算效率:

實(shí)時(shí)計(jì)算對(duì)效率要求很高(不然大量任務(wù)堆積), 所以spark的性能優(yōu)化的方法在這里通用, 比如:

  1. 合理的并行度partition, 一般是core的2~5倍, spark。 spark.default.parallelism=200

  2. spark.sql.shuffle.partitions 設(shè)置大一點(diǎn), 個(gè)人比較喜歡spark-sql處理邏輯,這個(gè)是sql shuffle時(shí)的并行度

  3. spark.rdd.compress=true 緩存時(shí)壓縮, 默認(rèn)是false, 節(jié)省內(nèi)存, 但是增加性能損耗

  4. 參照 http://spark.apache.org/docs/latest/tuning.html

十二, 代碼優(yōu)化:

根據(jù)實(shí)際情況優(yōu)化,在線任務(wù)和離線任務(wù)還是區(qū)別很大的,更多關(guān)注效率。

  1. 處理空Batch:
    空batch比較多, 不判斷直接寫(xiě)的話會(huì)形成很多空文件
    if(rdd.count() != 0) 或者 if(!rdd.partitions.isEmpty)
    推薦第二種, 數(shù)據(jù)量比較大時(shí) count很費(fèi)時(shí)間的

  2. 高性能算子(平時(shí)要加強(qiáng)總結(jié)):

   groupByKey  →  reduceByKey/aggregateByKey
   map →  mapPartitions
   foreachPartitions  →  foreach
  1. 序列化(廣播變量, cache, 自定義對(duì)象):

通常圖省事, 直接繼承 java的Serializable 接口。

Spark支持使用Kryo序列化機(jī)制, 大概效率是java序列化的10倍, 變少網(wǎng)絡(luò)傳輸?shù)臄?shù)據(jù),減少在集群中耗費(fèi)的內(nèi)存資源。

spark.serializer=org.apache.spark.serializer.KryoSerializer

spark.kryo.registrationRequired=true // 應(yīng)用的類沒(méi)有注冊(cè)會(huì)報(bào)錯(cuò),默認(rèn)false

  • 使用:需要先注冊(cè)算子里邊用到的類,不然會(huì)存儲(chǔ)每個(gè)對(duì)象的全類名(full class name),這樣的使用方式往往比默認(rèn)的 Java serialization 還要浪費(fèi)更多的空間。

    • 需要序列化的類繼承 java.io.Serializable
    • 注冊(cè)類繼承KryoRegistrato并且注冊(cè)那些需要序列化的類
    • 在sparkConf中設(shè)置spark.serializer和spark.kryo.registrator

十三,其它

checkpoint: http://bit1129.iteye.com/blog/2217505 沒(méi)用到自帶的checkpoint機(jī)制


Kyro序列化


import com.esotericsoftware.kryo.Kryo
import org.apache.spark.serializer.KryoRegistrator

case class UserInfo(name: String ,age: Int,gender: String, addr: String)

class MyRegisterKryo extends KryoRegistrator {
  override def registerClasses(kryo: Kryo): Unit = {
    kryo.register(classOf[UserInfo])
  }
}

import org.apache.spark.storage.StorageLevel
import org.apache.spark.{SparkConf, SparkContext}

import scala.collection.mutable.ArrayBuffer
import scala.util.Random


/**
  * 需要序列化的類繼承java.io.Serializable
  * 注冊(cè)類繼承KryoRegistrator并且注冊(cè)那些需要序列化的類
  * 在sparkConf中設(shè)置spark.serializer和spark.kryo.registrator
  */

object KyroExample {

  def kyroExample() {
    val conf = new SparkConf().setMaster("local[1]").setAppName("KyroTest")
    conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer")
    conf.set("spark.kryo.registrator", "tools.MyRegisterKryo")
    conf.registerKryoClasses(Array(classOf[UserInfo], classOf[scala.collection.mutable.WrappedArray.ofRef[_]]))
    val sc = new SparkContext(conf)

    val arr = new ArrayBuffer[UserInfo]()

    val nameArr = Array[String]("lsw","yyy","lss")
    val genderArr = Array[String]("male","female")
    val addressArr = Array[String]("beijing","shanghai","shengzhen","wenzhou","hangzhou")

    for(i <- 1 to 1000){
      val name = nameArr(Random.nextInt(3))
      val age = Random.nextInt(100)
      val gender = genderArr(Random.nextInt(2))
      val address = addressArr(Random.nextInt(5))
      arr.+=(UserInfo(name,age,gender,address))
    }
    val start = System.currentTimeMillis()

    val rdd = sc.parallelize(arr)

    //序列化的方式將rdd存到內(nèi)存
    rdd.persist(StorageLevel.MEMORY_ONLY_SER)
    println(System.currentTimeMillis() - start)
    sc.stop()
  }
}
 conf.set("spark.serializer", "org.apache.spark.serializer.KryoSerializer") //使用Kryo序列化庫(kù)

                  val sc = new SparkContext(conf)

 def saveAsObjectFile[T: ClassTag](rdd: RDD[T], path: String) {

                        val kryoSerializer = new KryoSerializer(rdd.context.getConf)    //  KryoSerializer對(duì)象, rdd.context.getConf獲取緩存大小

rdd.mapPartitions(iter => iter.grouped(10)
      .map(_.toArray))
      .map(splitArray => {
      //initializes kyro and calls your registrator class
      val kryo = kryoSerializer.newKryo()   //map種創(chuàng)建Kryo實(shí)例, 線程不安全,只能放在map或者mappartition中
 
      //convert data to bytes
      val bao = new ByteArrayOutputStream()    
      val output = kryoSerializer.newKryoOutput()  
      output.setOutputStream(bao)
      kryo.writeClassAndObject(output, splitArray)
      output.close()
 
      // We are ignoring key field of sequence file
      val byteWritable = new BytesWritable(bao.toByteArray)
      (NullWritable.get(), byteWritable)
    }).saveAsSequenceFile(path)

}

def objectFile[T](sc: SparkContext, path: String, minPartitions: Int = 1)

    (implicit ct: ClassTag[T]) = {

    val kryoSerializer = new KryoSerializer(sc.getConf)

    sc.sequenceFile(path, classOf[NullWritable], classOf[BytesWritable],

       minPartitions)

       .flatMap(x => {

       val kryo = kryoSerializer.newKryo()

       val input = new Input()

       input.setBuffer(x._2.getBytes)

       val data = kryo.readClassAndObject(input)

       val dataObject = data.asInstanceOf[Array[T]]

       dataObject

    })

  }

參考:

Kryo讀寫(xiě)硬盤(pán): https://www.iteblog.com/archives/1328.html

Kryo使用: https://blog.csdn.net/cjuexuan/article/details/51485427

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,923評(píng)論 6 535
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,740評(píng)論 3 420
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說(shuō)我怎么就攤上這事。” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 176,856評(píng)論 0 380
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 63,175評(píng)論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,931評(píng)論 6 410
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 55,321評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,383評(píng)論 3 443
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 42,533評(píng)論 0 289
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,082評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,891評(píng)論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,067評(píng)論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,618評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,319評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 34,732評(píng)論 0 27
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 35,987評(píng)論 1 289
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,794評(píng)論 3 394
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,076評(píng)論 2 375

推薦閱讀更多精彩內(nèi)容