一.引言:
Spark Streaming 支持通过 Receiver 自定义数据流,实现之后由于接受客户端的不稳定时常造成streaming程序的崩溃,最早的想法是在Receiver实现中加入try catch,通过异常处理使得数据不会中断,但是Receiver整体的中断还是会迫使程序退出,不受Reciver内部的Try Catch控制,所以需要新的策略重启Receiver,好在Spark官方早就给出了解决方案,下面看看怎么使用。
二.源码:
Spark Doc中共有三种restart方案,可以根据自己的需求选择,不过大同小异主要的诉求就是Receiver出问题后重启:
1.Dirver接收消息
def restart(message: String): Unit
Restart the receiver. This method schedules the restart and returns immediately.
The stopping and subsequent starting of the receiver (by calling onStop() and
onStart()) is performed asynchronously in a background thread. The delay between
the stopping and the starting is defined by the Spark configuration
spark.streaming.receiverRestartDelay. The message will be reported to the driver.
重新启动接收器。此方法安排重新启动并立即返回。接收器的停止和随后的启动(通过调用onStop()和onStart())是在后台线程中异步执行的。停止和启动之间的延迟由spark配置定义spark.streaming.receiverRestartDelay. 消息将报告给Dirver端。
2.Diver接收消息+异常
def restart(message: String, error: Throwable): Unit
Restart the receiver. This method schedules the restart and returns immediately.
The stopping and subsequent starting of the receiver (by calling onStop() and
onStart()) is performed asynchronously in a background thread. The delay between
the stopping and the starting is defined by the Spark configuration
spark.streaming.receiverRestartDelay. The message and exception will be reported to
the driver.
重新启动接收器。此方法安排重新启动并立即返回。接收器的停止和随后的启动(通过调用onStop()和onStart())是在后台线程中异步执行的。停止和启动之间的延迟由spark配置定义spark.streaming.receiverRestartDelay. 消息和异常将报告给Dirver端。
3.直接重启
def restart(message: String, error: Throwable, millisecond: Int): Unit
Restart the receiver. This method schedules the restart and returns immediately.
The stopping and subsequent starting of the receiver (by calling onStop() and
onStart()) is performed asynchronously in a background thread.
重新启动接收器。此方法安排重新启动并立即返回。接收器的停止和随后的启动(通过调用onStop()和onStart())是在后台线程中异步执行的。
三.实现
之前实现过Demo版本的Receiver,如果想了解可以参考Spark 自定义Receiver
主要改动在Reveriver内部的 onStart()方法 和 onStop()方法:
1.onStart()
Old Version:
老版本直接起了一个线程去执行接收数据,异常处理在receiver中。
def onStart(): Unit = {
new Thread() {
override def run(): Unit = {
receive()
}
}.start()
}
New Version:
为了增加鲁棒性,需要在onStart方法中加入异常处理,并调用restart恢复。这里使用了第二种restart方法,将消息与异常传回dirver。
def onStart(): Unit = {
new Thread() {
override def run(): Unit = {
try {
receive()
} catch {
case e: ConnectException => {
restart("Connect Failed...", e)
}
case t: Throwable => {
restart("Error receiving data...", t)
}
}
}
}.start()
}
2.onStop()
Old Version:
之前实现的onStop出现异常就直接退出了,所以程序经常会终止。
def onStop: Unit = {
if (Thread.currentThread.isInterrupted) {
sys.exit(1)
}
}
New Version:
修改后取消了直接退出的逻辑,新增了report逻辑,这里类似restart向dirver汇总数据一样,可以在report内定义自己的汇报逻辑,当然也可以不实现任何方法。
def onStop: Unit = {
report()
}
四.效果
经过 onStrat() 新增重启机制 和 onSrop() 的取消退出机制+回传机制,Streaming稳定性得到保障。可以看到图中接收数据有明显的跃阶,其中的间隔就是上面提到的 spark.streaming.receiverRestartDelay 参数定义的,可以自定义也可以使用默认,换到之前逻辑跃阶处会导致Streaming程序异常退出,数据的完整性将受到影响。其次restart的调用位置可以放在receiver()函数内,也可以放在onStart()函数内,区别不是很大,因为三种restart方法都会异步重新调用onStop()和onStart(),所以将restart逻辑写在receiver函数逻辑内应该也没有问题,有需求的同学可以尝试~