SparkStreaming如何优雅的停止服务

我们都知道SparkStreaming程序是一个长服务,一旦运转起来不会轻易停掉,那么如果我们想要停掉正在运行的程序应该怎么做呢?
如果运行的是spark on yarn模式直接使用

yarn application -kill taskId

暴力停掉sparkstreaming是有可能出现问题的,比如你的数据源是kafka,已经加载了一批数据到sparkstreaming中正在处理,如果中途停掉,这个批次的数据很有可能没有处理完,就被强制stop了,下次启动时候会重复消费或者部分数据丢失。

如何优雅的关闭spark streaming呢?方式主要有三种:

第一种:全人工介入

Spark 1.3及其前的版本

通过 Runtime.getRuntime().addShutdownHook 注册关闭钩子, JVM将在关闭之前执行关闭钩子中的 run函数(不管是正常退出还是异常退出都会调用),所以我们可以在 driver 代码中加入以下代码:
JAVA代码:

Runtime.getRuntime().addShutdownHook(new Thread() {
 override def run() {
   log("Shutting down streaming app...")
   ssc.stop(true, true)
   log("Shutdown of streaming app complete.")
 }
})


Scala方式:

    sys.ShutdownHookThread
    {
      ssc.stop(true, true)
    }

这样就能保证即使 application 被强行 kill 掉,在 driver 结束前,ssc.stop(true, true)也会被调用,从而保证已接收的数据都会被处理。

Spark 1.4及其后的版本

上一小节介绍的方法仅适用于 1.3及以前的版本,在 1.4及其后的版本中不仅不能保证生效,甚至会引起死锁等线程问题。在 1.4及其后的版本中,只需要在SparkConf里面设置下面的参数即可:

sparkConf.set("spark.streaming.stopGracefullyOnShutdown","true")

然后按照下面的步骤依次操作:

  1. 通过Hadoop 8088页面找到运行的程序
  2. 打开spark ui的监控页面
  3. 打开executor的监控页面
  4. 登录liunx找到驱动节点所在的机器ip以及运行的端口号
  5. 然后执行一个封装好的命令
sudo ss -tanlp |  grep 5555 |awk '{print $6}'|awk  -F, '{print $2}' | sudo  xargs kill -15

注意上面的操作执行后,sparkstreaming程序,并不会立即停止,而是会把当前的批处理里面的数据处理完毕后 才会停掉,此间sparkstreaming不会再消费kafka的数据,这样以来就能保证结果不丢和重复。

从上面的步骤可以看出,这样停掉一个spark streaming程序是比较复杂的。那么有没有更加优雅的方式来停止它呢?答案是有的

第二种:使用HDFS系统做消息通知

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

第三种:内部暴露一个socket或者http端口用来接收请求,等待触发关闭流程序

这种方式,需要在driver启动一个socket线程,或者http服务,这里推荐使用http服务,因为socket有点偏底层处理起来稍微复杂点,如果使用http服务,我们可以直接用内嵌的jetty,对外暴露一个http接口,spark ui页面用的也是内嵌的jetty提供服务,所以我不需要在pom里面引入额外的依赖,在关闭的时候,找到驱动所在ip,就可以直接通过curl或者浏览器就直接关闭流程序。
找到驱动程序所在的ip,可以在程序启动的log中看到,也可以在spark master ui的页面上找到。这种方式不依赖任何外部的存储系统,仅仅部署的时候需要一个额外的端口号用来暴露http服务。

至此,关于优雅的停止spark streaming的主流方式已经介绍完毕,推荐使用第二种或者第三种,如果想要最大程度减少对外部系统的依赖,推荐使用第三种方式。

关于具体第二种和第三种的样例代码,下篇文章会整理一下放在github中给大家参考。

你可能感兴趣的:(SparkStreaming如何优雅的停止服务)