sparkstreaming容错机制
sparkstreaming运行流程
- 提交一个sparkstreaming程序到服务器运行,默认提交的服务器为client,并向ClusterManager报备申请资源。
- 在一个executor中单独启动一个receiver线程(默认一个,可以更改成多个)用来接收数据。
- receiver根据指定时间间隔(默认200ms)将数据分隔成一个个block并写入内存中
- receiver服务器端将block数据备份并将划分的block的一些信息发送给driver端
- driver端根据一定的时间间隔将这些block块组成一个RDD
- driver使用DStreamGraph记录的RDD依赖关系划分job,然后提交给obScheduler进行任务分配,默认一个block对应一个task
Executor失败
- executor失败会在其他executor端启动receiver和tasks从而实现receiver端的高可用,不需要做特殊配置
Driver失败
- 可以使用checkpoint机制,定期的将driver信息写入到HDFS,从而在driver失败时恢复driver,从而实现driver端的高可用
driver的自动重启设置
步骤一
- standlone
在spark-submit中增加以下两个参数:
--deploy-mode cluster
--supervise #失败后是否重启Driver
#使用示例:
spark-submit \
--master spark://node01:7077 \
--deploy-mode cluster \
--supervise \
--class com.streaming.Demo \
--executor-memory 1g \
--total-executor-cores 2 \
original-sparkStreamingStudy-1.0-SNAPSHOT.jar
--deploy-mode cluster 集群模式
--supervise 失败之后自动重启 ,需要在集群模式下使用
- spark on yarn
- spark on yarn模式下,driver端和master共用一个JVM,被绑定在一起
在spark-submit中增加以下参数:
--deploy-mode cluster
在yarn配置中设置yarn.resourcemanager.am.max-attemps参数 ,默认为2,例如:
<value>4</value> 自动重启次数
<property>
<name>yarn.resourcemanager.am.max-attempts</name>
<value>4</value>
<description>
The maximum number of application master execution attempts.
</description>
</property>
使用示例:
spark-submit \
--master yarn \
--deploy-mode cluster \
--class com.streaming.Demo \
--executor-memory 1g \
--total-executor-cores 2 \
original-sparkStreamingStudy-1.0-SNAPSHOT.jar
步骤二
streamingContext.setCheckpoint(hdfsDirectory)
步骤三
// Function to create and setup a new StreamingContext
def functionToCreateContext(): StreamingContext = {
val ssc = new StreamingContext(...) // new context
val lines = ssc.socketTextStream(...) // create DStreams
...
ssc.checkpoint(checkpointDirectory) // set checkpoint directory
ssc
}
// Get StreamingContext from checkpoint data or create a new one
val context = StreamingContext.getOrCreate(checkpointDirectory, functionToCreateContext _)
// Do additional setup on context that needs to be done,
// irrespective of whether it is being started or restarted
context. ...
// Start the context
context.start()
context.awaitTermination()
数据丢失
executor备份
receiver所在的executor接受后,除了将数据写入自己的内存,还会将数据向blockManager备份。当该executor挂掉后,可以读取备份的数据,但是可能丢失部分没来的即备份的数据。
WAL预写日志
WAL概念
-
WAL使用在文件系统和数据库中用于数据操作的持久性,先把数据写到一个持久化的日志中,然后对数据做操作,如果操作过程中系统挂了,恢复的时候可以重新读取日志文件再次进行操作。
-
对于像kafka和flume这些使用接收器来接收数据的数据源。接收器作为一个长时间的任务运行在executor中,负责从数据源接收数据,如果数据源支持的话,向数据源确认接收到数据,然后把数据存储在executor的内存中,然后在exector上运行任务处理这些数据。
-
如果wal启用了,所有接收到的数据会保存到一个日志文件中去(HDFS), 这样保存接收数据的持久性,此外,如果只有在数据写入到log中之后接收器才向数据源确认,这样driver重启后那些保存在内存中但是没有写入到log中的数据将会重新发送,这两点保证的数据的无丢失
可靠数据源和不可靠数据源
Reliable Receiver : 当数据接收到,并且已经备份存储后,再发送回执给数据源
Unreliable Receiver : 不发送回执给数据源
- 每次receiver端的executor接受到数据后,通过WAL机制,将数据写入到HDFS,HDFS本身又有备份。这样比较安全丢失概率小。
- 但是:当写数据到HDFS磁盘的过程中executor中挂掉,数据没有写入HDFS成功,此时数据丢失。
可以使用一个可靠的数据源,如:kafka。来保证数据在写入磁盘前不丢失- kafka可以设置当数据处理成功时,会接受回执,否则会重新发送,从而保证了数据写入HDFS成功
- 而socket这种只管发送,不根据回执决定是否重新发送的数据源为不可靠数据源
实现步骤
步骤一:设置checkpoint目录
streamingContext.setCheckpoint(hdfsDirectory)
步骤二:开启WAL日志
sparkConf.set("spark.streaming.receiver.writeAheadLog.enable", "true")
步骤三:需要reliable receiver
当数据写完了WAL后,才告诉数据源数据已经消费,对于没有告诉数据源的数据,可以从数据源中重新消费数据
步骤四:取消备份
//使用StorageLevel.MEMORY_AND_DISK_SER来存储数据源,不需要后缀为2的策略了(默认是2),因为HDFS已经是多副本了
val initDS: ReceiverInputDStream[String] = sc.socketTextStream("node01",9999,StorageLevel.MEMORY_AND_DISK_SER)
某个task特别慢
推测机制
spark.speculation=true,每隔一段时间来检查有哪些正在运行的task需要重新调度(spark.speculation.interval=100ms),假设总的task有10个,成功的task的数量 > 0.75 * 10(spark.speculation.quantile=0.75),正在运行的task的运行时间 > 1.5 * 成功运行task的平均时间(spark.speculation.multiplier=1.5),则这个正在运行的task需要重新等待调度。
推测机制的风险
在分布式环境中导致某个Task执行缓慢的情况有很多,负载不均、程序bug、资源不均、数据倾斜等,而且这些情况在分布式任务计算环境中是常态。Speculative Task这种以空间换时间的思路对计算资源是种压榨,同时如果Speculative Task本身也变成了Slow Task会导致情况进一步恶化。负载不均、数据倾斜等原因导致的问题,使用Speculative Task并不会解决问题,慢的task会重复反复提交,反复的占用资源。
Speculative Task要谨慎使用