鸣谢:如果您觉得本文对您有帮助,请点赞和收藏,Thanks。
在RDD中,任务可切分为:Application、Job、Stage、Task
。
Application:初始化一个SparkContext即生成一个Application,通常一个main函数就是一个Application;
Job:一个Action算子就会生成一个Job,常用Action算子包括reduce、aggregate、collect、first、take、foreach、saveAsTextFile:saveAsTextFile、countByKey 、collectAsMap
等等;
Stage:根据RDD之间的依赖关系的不同将Job划分成不同的Stage,遇到一个宽依赖则划分一个Stage,宽依赖指一个分区的数据会被划分到多个分区中去,这种会引发shuffle,比如groupBy操作;
Task:Stage是一个TaskSet(任务集),将Stage划分的结果发送到不同的Executor执行即为一个Task。
Application->Job->Stage-> Task
每一层都是1对n的关系。
Spark代码运行,分为Driver(驱动器)和Executor(执行器)。
通常来说,rdd算子中的代码在excuter执行,而其他代码都是在driver执行,并且只执行一次。
此处,以kafka流处理为例:消费spark streamign 消费kafka后,会产生一个DStream,要把数据输出到外面,创建连接对象的位置显示尤为重要。
通常,新手容易犯下以下错误:
dstream.foreachRDD { rdd =>
val connection = createNewConnection() // executed at the driver
rdd.foreach { record =>
connection.send(record) // executed at the worker
}
}
这是错误的做法,因为这要求将连接对象序列化并从driver驱动程序发送给worker工作程序。这样的连接对象很少能在机器之间转移。此错误可能表现为序列化错误(连接对象不可序列化:connection object not serializable
),初始化错误(连接对象需要在工作程序中初始化:connection object needs to be initialized at the workers
)等。
dstream.foreachRDD { rdd =>
rdd.foreach { record =>
val connection = createNewConnection()
connection.send(record)
connection.close()
}
}
这种做法没有错,但是,创建连接对象会浪费时间和资源。因此,为每个记录创建和销毁连接对象会导致不必要的高开销,并且会大大降低系统的整体吞吐量。所以,不要使用这种办法创建连接对象。
stream.foreachRDD { rdd =>
rdd.foreachPartition { partitionOfRecords =>
val connection = createNewConnection()
partitionOfRecords.foreach(record => connection.send(record))
connection.close()
}
}
这种做法使用 rdd.foreachPartition创建单个连接对象,就是一个分区创建一个连接对象。
当然,还可以做以下优化:
建一个静态的、慢加载的连接对象池,按需延迟创建池中的连接,如果一段时间不使用,则归还连接对象。
dstream.foreachRDD { rdd =>
rdd.foreachPartition { partitionOfRecords =>
// ConnectionPool is a static, lazily initialized pool of connections
val connection = ConnectionPool.getConnection()
partitionOfRecords.foreach(record => connection.send(record))
ConnectionPool.returnConnection(connection) // return to the pool for future reuse
}
}
备注:通过SparkSQL/SparkStreaming读写Hive/Mysql/Hbase/Kafka的代码模板可参考我的这篇文章
SparkSQL/SparkStreaming读写Hive/Mysql/Hbase/Kafka