本节从安全角度来讲解sparkstreaming,因为sparkstreaming会不断的接收数据、不断的产生job、不断的提交job。所以有一个至关重要的问题就是数据安全性。
由于sparkstreaming是基于sparkcore的之上的应用程序,如果借助于sparkcore之上的容错机制,保证数据安全性,sparkcore的RDD容错机制,保证了sparkstreaming的数据安全,但前提是sparkstreaming本身接收的数据是完整且安全的。
如果我们可以确保数据安全可靠的话(sparkstreaming生产job的时候里面是基于RDD),即使运行的时候出现错误或者故障,也可以基于RDD的容错的能力自动进行恢复。所以要确保数据的安全性。
对于executor的安全容错主要是数据的安全容错。Executor计算时候的安全容错是借助Spark core的RDD的,所以天然是安全的。
数据安全性的一种方式是存储一份副本,另一种方式是不做副本,但是数据源支持存放(也就是可以反复的读取数据源的数据),如果之前读取的数据出现问题,可以重新读取数据。
做副本的方式可以借助blockmanager做备份。Blockmanager存储数据的时候有很多storagelevel,Receiver接收数据后,存储的时候指定storagelevel为MEMORY_AND_DISK_SER_2的方式。Blockmanager早存储的时候会先考虑memory,只有memory不够的时候才会考虑disk,一般memory都是够的。所以至少两个executor上都会有数据,假设一个executor挂掉,就会马上切换到另一个executor。
一、ReceiverSupervisorImpl在存储数据的时候会有两种方式,一种是WAL的方式,究竟是不是WAL得方式是通过配置修改的。默认是false。如果用WAL的方式必须有checkpoint的目录,因为WAL的数据是放在checkpoint的目录之下的。
def enableReceiverLog(conf:SparkConf): Boolean = {
conf.getBoolean(RECEIVER_WAL_ENABLE_CONF_KEY,false)
}
Storagelevel是在构建inputDstream的时候传入的,默认就是MEMORY_AND_DISK_SER_2。
*@param storageLevel Storage level to use for storing the received objects
* (default:StorageLevel.MEMORY_AND_DISK_SER_2)
*/
defsocketTextStream(
hostname: String,
port: Int,
storageLevel: StorageLevel = StorageLevel.MEMORY_AND_DISK_SER_2
):ReceiverInputDStream[String] = withNamedScope("socket text stream") {
socketStream[String](hostname, port, SocketReceiver.bytesToLines, storageLevel)
}
二、现在来看ReceiverSupervisorImpl在存储数据的另一种方式(副本方式)。根据指定的storagelevel把接收的blocks交给blockmanager。也就是通过blockmanager来存储。
/**
* Implementation of a [[org.apache.spark.streaming.receiver.ReceivedBlockHandler]]which
* stores the received blocks into ablock manager with the specified storage level.
*/
private[streaming] class BlockManagerBasedBlockHandler(
blockManager: BlockManager, storageLevel: StorageLevel)
Blockmanager存储的时候会分为多种不同的数据类型,ArrayBufferBlock,IteratorBlock,ByteBufferBlock。
Receiver在接收到数据后除了在自己这个executor上面存储,还会在另外一个executor上存储。如果一个executor出现问题会瞬间切换到另一个executor。
WAL的方式原理:在具体的目录下会做一份日志,假设后续处理的过程中出了问题,可以基于日志恢复,日志是写在checkpoint下。在生产环境下checkpoint是在HDFS上,这样日志就会有三份副本。
不管是WAL还是直接交给blockmanager都是采用副本的方式。还有一种是数据源支持数据存放,典型的就是kafka。Kafka已经成为了数据存储系统,它天然具有容错和数据副本。
Kafka有receiver和direct的方式。Receiver的方式其实是交给zookeper来管理matadata的(偏移量offset),如果数据处理失败后,kafka会基于offset重新读取数据。为什么可以重新读取?如果程序崩溃或者数据没处理完是不会给zookeper发ack。Zookeper就认为这个数据没有被消费。实际生产环境下越来越多的使用directAPI的方式,直接去操作kafka并且是自己管理offset。这就可以保证有且只有一次的容错处理。DirectKafkaInputDstream,它会去看最新的offset,并把这个内容放入batch中。
获取最新的offset,通过最新的offset减去上一个offset就可以确定读哪些数据,也就是一个batch中的数据。
@tailrec
protected final def latestLeaderOffsets(retries:Int): Map[TopicAndPartition, LeaderOffset] = {
val o = kc.getLatestLeaderOffsets(currentOffsets.keySet)
// Either.fold would confuse @tailrec, do it manually
if (o.isLeft){
val err= o.left.get.toString
if (retries<= 0) {
thrownew SparkException(err)
} else {
log.error(err)
Thread.sleep(kc.config.refreshLeaderBackoffMs)
latestLeaderOffsets(retries - 1)
}
} else {
o.right.get
}
}
容错的弊端就是消耗性能,占用时间。也不是所有情况都不能容忍数据丢失。有些情况下可以不进行容错来提高性能
假如一次处理1000个block,但是有1个block出错,就需要把1000个block进行重新读取或者恢复,这也有性能问题。
作者:大数据技术研发人员:谢彪
-
资料来源于:DT_大数据梦工厂(Spark发行版本定制)
-
DT大数据梦工厂微信公众号:DT_Spark
-
新浪微博:http://www.weibo.com/ilovepains
-
王家林老师每晚20:00免费大数据实战
YY直播:68917580