spark streaming checkpoint восстановление очень очень медленно
- цель: чтение из Kinesis и хранить данные в S3 в паркетном формате с помощью spark streaming.
- ситуация: Применение бежит отлично первоначально, бежать серии 1hour и время обработки чем 30 минут в среднем. По какой-то причине позволяет сказать, что приложение аварийно завершает работу, и мы пытаемся перезапустить с контрольной точки. Обработка длится вечно и не движется вперед. Мы попытались протестировать то же самое с интервалом в 1 минуту, обработка проходит нормально и занимает 1,2 минуты для завершения партии. Когда мы вышли из КПП, она занимает около 15 минут для каждой партии.
- заметки: мы используем s3 для контрольных точек используя 1 executor, с 19g mem & 3 ядрами на executor
установка скриншоты:
первый запуск-перед контрольной точкой Восстановление
конфигурации.скала!--15-->
object Config {
val sparkConf = new SparkConf
val sc = new SparkContext(sparkConf)
val sqlContext = new HiveContext(sc)
val eventsS3Path = sc.hadoopConfiguration.get("eventsS3Path")
val useIAMInstanceRole = sc.hadoopConfiguration.getBoolean("useIAMInstanceRole",true)
val checkpointDirectory = sc.hadoopConfiguration.get("checkpointDirectory")
// sc.hadoopConfiguration.set("spark.sql.parquet.output.committer.class","org.apache.spark.sql.parquet.DirectParquetOutputCommitter")
DateTimeZone.setDefault(DateTimeZone.forID("America/Los_Angeles"))
val numStreams = 2
def getSparkContext(): SparkContext = {
this.sc
}
def getSqlContext(): HiveContext = {
this.sqlContext
}
}
S3Basin.скала!--15-->
object S3Basin {
def main(args: Array[String]): Unit = {
Kinesis.startStreaming(s3basinFunction _)
}
def s3basinFunction(streams : DStream[Array[Byte]]): Unit ={
streams.foreachRDD(jsonRDDRaw =>{
println(s"Old partitions ${jsonRDDRaw.partitions.length}")
val jsonRDD = jsonRDDRaw.coalesce(10,true)
println(s"New partitions ${jsonRDD.partitions.length}")
if(!jsonRDD.isEmpty()){
val sqlContext = SQLContext.getOrCreate(jsonRDD.context)
sqlContext.read.json(jsonRDD.map(f=>{
val str = new String(f)
if(str.startsWith("{"message"")){
str.substring(11,str.indexOf("@version")-2)
}
else{
str
}
})).registerTempTable("events")
sqlContext.sql(
"""
|select
|to_date(from_utc_timestamp(from_unixtime(at), 'US/Pacific')) as event_date,
|hour(from_utc_timestamp(from_unixtime(at), 'US/Pacific')) as event_hour,
|*
|from events
""".stripMargin).coalesce(1).write.mode(SaveMode.Append).partitionBy("event_date", "event_hour","verb").parquet(Config.eventsS3Path)
sqlContext.dropTempTable("events")
}
})
}
}
Кинезиса.скала!--15-->
object Kinesis{
def functionToCreateContext(streamFunc: (DStream[Array[Byte]]) => Unit): StreamingContext = {
val streamingContext = new StreamingContext(Config.sc, Minutes(Config.sc.hadoopConfiguration.getInt("kinesis.StreamingBatchDuration",1))) // new context
streamingContext.checkpoint(Config.checkpointDirectory) // set checkpoint directory
val sc = Config.getSparkContext
var awsCredentails : BasicAWSCredentials = null
val kinesisClient = if(Config.useIAMInstanceRole){
new AmazonKinesisClient()
}
else{
awsCredentails = new BasicAWSCredentials(sc.hadoopConfiguration.get("kinesis.awsAccessKeyId"),sc.hadoopConfiguration.get("kinesis.awsSecretAccessKey"))
new AmazonKinesisClient(awsCredentails)
}
val endpointUrl = sc.hadoopConfiguration.get("kinesis.endpointUrl")
val appName = sc.hadoopConfiguration.get("kinesis.appName")
val streamName = sc.hadoopConfiguration.get("kinesis.streamName")
kinesisClient.setEndpoint(endpointUrl)
val numShards = kinesisClient.describeStream(streamName).getStreamDescription().getShards().size
val batchInterval = Minutes(sc.hadoopConfiguration.getInt("kinesis.StreamingBatchDuration",1))
// Kinesis checkpoint interval is the interval at which the DynamoDB is updated with information
// on sequence number of records that have been received. Same as batchInterval for this
// example.
val kinesisCheckpointInterval = batchInterval
// Get the region name from the endpoint URL to save Kinesis Client Library metadata in
// DynamoDB of the same region as the Kinesis stream
val regionName = sc.hadoopConfiguration.get("kinesis.regionName")
val kinesisStreams = (0 until Config.numStreams).map { i =>
println(s"creating stream for $i")
if(Config.useIAMInstanceRole){
KinesisUtils.createStream(streamingContext, appName, streamName, endpointUrl, regionName,
InitialPositionInStream.TRIM_HORIZON, kinesisCheckpointInterval, StorageLevel.MEMORY_AND_DISK_2)
}else{
KinesisUtils.createStream(streamingContext, appName, streamName, endpointUrl, regionName,
InitialPositionInStream.TRIM_HORIZON, kinesisCheckpointInterval, StorageLevel.MEMORY_AND_DISK_2,awsCredentails.getAWSAccessKeyId,awsCredentails.getAWSSecretKey)
}
}
val unionStreams = streamingContext.union(kinesisStreams)
streamFunc(unionStreams)
streamingContext
}
def startStreaming(streamFunc: (DStream[Array[Byte]]) => Unit) = {
val sc = Config.getSparkContext
if(sc.defaultParallelism < Config.numStreams+1){
throw new Exception(s"Number of shards = ${Config.numStreams} , number of processor = ${sc.defaultParallelism}")
}
val streamingContext = StreamingContext.getOrCreate(Config.checkpointDirectory, () => functionToCreateContext(streamFunc))
// sys.ShutdownHookThread {
// println("Gracefully stopping Spark Streaming Application")
// streamingContext.stop(true, true)
// println("Application stopped greacefully")
// }
//
streamingContext.start()
streamingContext.awaitTermination()
}
}
3 ответов
поднял проблему Jira:https://issues.apache.org/jira/browse/SPARK-19304
проблема в том, что мы читаем больше данных за итерацию, чем требуется, а затем отбрасываем данные. Этого можно избежать, добавив ограничение в getResults
вызов aws.
при перезапуске неисправного драйвера происходит следующее:
- Recover computation – checkpointed информация используется для перезапустите драйвер, восстановите контексты и перезапустите все электроприемники.
- Recover BLOCK metadata-метаданные всех блоков, которые будут необходимые для продолжения обработки будут восстановлены.
- повторно генерировать неполные задания-для пакетов с обработкой, которые не завершена из-за сбоя RDDs и соответствующий задания восстанавливаются с использованием восстановленных метаданных блока.
- прочитайте блок, сохраненный в журналах – когда эти задания выполняются, данные блока считываются непосредственно из журналов Write ahead. Это восстанавливает все необходимые данные были надежно сохранены в журналы.
- повторно отправить неподтвержденные данные-буферизованные данные, которые не были сохранены в журнал в момент сбоя будет отправлен снова источником. как она не была признана приемник.
Поскольку все эти шаги выполняются в драйвере, ваш пакет событий 0 занимает так много времени. Это должно произойти с первой партией только тогда все будет нормально.
ссылка здесь.
у меня были подобные проблемы раньше, мое приложение становится все медленнее и медленнее.
попробуйте освободить память после использования rdd, вызов rdd.unpersist()
https://spark.apache.org/docs/latest/api/java/org/apache/spark/rdd/RDD.html#unpersist(boolean)
или spark.streaming.backpressure.enabled
to true
http://spark.apache.org/docs/latest/streaming-programming-guide.html#requirements
кроме того, проверьте, ваш locality
настройка, возможно, слишком много данных перемещается.