如何停止SparkStreaming服务

      网上的怎么关闭SparkStreaming的文章一大堆,可我还是费了很大的力气才解决了我的问题。

    我们都知道SparkStreaming程序是一个长服务,一旦运转起来不会轻易停掉,那么如果我们想要停掉正在运行的程序应该怎么做呢?
    如果运行的是spark on yarn模式直接使用
            # yarn application -kill taskId 
    暴力停掉sparkstreaming是有可能出现问题的,比如你的数据源是kafka,已经加载了一批数据到sparkstreaming中正在处理,如果中途停掉,这个批次的数据很有可能没有处理完,就被强制stop了,下次启动时候会重复消费或者部分数据丢失。

    如何解决? 

    方案一: Spark1.4之前的版本,需要一个钩子函数(该方案弃用)

                sys.ShutdownHookThread {
                    log.info("Gracefully stopping Spark Streaming Application")
                    ssc.stop(true, true) 
                    log.info("Application stopped") 
                } 

     方案二:Spark 1.4之后的版本(有两种方式,都没有测试过)

          方式一:通过端口号找到主进程然后kill掉 

                        只需要在SparkConf里面设置下面的参数即可:                 
                                sparkConf.set("spark.streaming.stopGracefullyOnShutdown","true") 
                         然后,如果需要停掉sparkstreaming程序时: 
                                (1)登录spark ui页面在executors页面找到driver程序所在的机器 
                                (2)使用ssh命令登录这台机器上,执行下面的命令通过端口号找到主进程然后kill掉 
                                 ss -tanlp | grep 55197|awk '{print $6}'|awk -F, '{print $2}'|xargs kill -15 
                        注意:上面的操作执行后,sparkstreaming程序,并不会立即停止,而是会把当前的批处理里面的数据处理完毕后才会停掉,此间sparkstreaming不会再消费kafka的数据,这样以来就能保证结果不丢和重复。 

             方式二:发送SIGTERM信号到spark驱动程序然后关闭程序

                    首先将  spark.streaming.stopGracefullyOnShutdown  参数设置为true(默认值为false)。Spark中引入此参数来解决正常关机问题。开发人员无需再在代码中调用ssc.stop()。但需要向主程序driver端发送SIGTERM信号。在实践中,我们需要做到以下几点:
                        (1)使用Spark UI找出驱动程序进程在哪个节点上运行。在yarn群集部署模式中,驱动程序进程和AM在同一个容器中运行。 

                        (2)登录该节点并执行  ps -ef | grep java | grep ApplicationMaster  并找出pid。
                                 请注意,根据您的应用程序/环境等,grep字符串可能会有所不同。

                        (3)将SIGTERM发送到进程。

                                 kill-SIGTERM <AM-PID>  
                    Spark驱动程序收到SIGTERM信号后,应该在日志中看到关闭的信息了。

           此外还有一个问题是,spark on yarn模式下,默认情况下,Spark的spark.yarn.maxAppAttempts 参数使用 YARN中yarn.resourcemanager.am.max- attempts中的默认值 ,默认值为2。因此,在第一个AM被kill命令停止后,YARN将自动启动另一个AM 驱动程序,作为高可用,也就是上面的操作你可能要执行两次,才能真能的停掉程序,当然我们也可以在spark-submit期间设置  --conf spark.yarn.maxAppAttempts = 1 驱动程序一次挂掉之后,就真的挂掉了,这样就没有容灾机制了,需要慎重考虑。

      方案三:使用HDFS系统做消息通知(我采用这样方式)

        在驱动程序中,加一段代码,这段代码的作用每隔一段时间可以是10秒也可以是3秒,扫描HDFS上某一个文件,如果发现这个文件存在,就调用StreamContext对象stop方法,自己优雅的终止自己,其实这里HDFS可以换成redis,zk,hbase,db都可以,这里唯一的问题就是依赖了外部的一个存储系统来达到消息通知的目的,如果使用了这种方式后。停止流程序就比较简单了,登录上有hdfs客户端的机器,然后touch一个空文件到指定目录,然后等到间隔的扫描时间到之后,发现有文件存在,就知道需要关闭程序了。

package com.spark.test

import org.apache.hadoop.conf.Configuration
import org.apache.hadoop.fs.{FileSystem, Path}
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}

object Test {
  val shutdownMarker = "/tmp/spark-test/stop-spark"
  var stopFlag: Boolean = false

  def main(args: Array[String]) {
    val conf = new SparkConf().setAppName("stop spark streaming")

    val ssc = new StreamingContext(conf, Seconds(5))
    val file = ssc.textFileStream("/tmp/sparkstreaming")
    val res = file.map { line =>
      val arr = line.split("\t")
      arr(0) + "\t" + arr(2)
    }
    res.saveAsTextFiles("/tmp/stroage")

    ssc.start()
    //检查间隔毫秒
    val checkIntervalMillis = 10000
    var isStopped = false
    while (!isStopped) {
      println("calling awaitTerminationOrTimeout")
      //等待执行停止。执行过程中发生的任何异常都会在此线程中抛出,如果执行停止了返回true,
      //线程等待超时长,当超过timeout时间后,会监测ExecutorService是否已经关闭,若关闭则返回true,否则返回false。
      isStopped = ssc.awaitTerminationOrTimeout(checkIntervalMillis)
      if (isStopped) {
        println("confirmed! The streaming context is stopped. Exiting application...")
      } else {
        println("Streaming App is still running. Timeout...")
      }
      //判断文件夹是否存在
      checkShutdownMarker
      if (!isStopped && stopFlag) {
        println("stopping ssc right now")
        //第一个true:停止相关的SparkContext。无论这个流媒体上下文是否已经启动,底层的SparkContext都将被停止。
        //第二个true:则通过等待所有接收到的数据的处理完成,从而优雅地停止。
        ssc.stop(true, true)
        println("ssc is stopped!!!!!!!")
      }
    }
  }

  def checkShutdownMarker = {
    if (!stopFlag) {
      //开始检查hdfs是否有stop-spark文件夹
      val fs = FileSystem.get(new Configuration())
      //如果有返回true,如果没有返回false
      stopFlag = fs.exists(new Path(shutdownMarker))
    }
  }
}

我一直纠结 awaitTerminationOrTimeout(checkIntervalMillis) 这个方法,对我数据处理是否有影响,查阅大量资料这个方法就是等待执行停止,在执行期间发生的任何异常都会被抛出这个线程。而且检查间隔毫秒其实是线程等待超时时长,当超过timeout时间后,会监测ExecutorService是否已经关闭,若关闭则返回true,否则返回false。还有我在测试的时候,停掉spark streaming查看日志,发现会有一个警告,告诉我sparkContext 上下文已经关闭,没办法写数据。



学习连接 http://www.linkedin.com/pulse/how-shutdown-spark-streaming-job-gracefully-lan-jiang

感谢各位大神提供的方案,我在这里个了详细的说明。

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页