本期内容:
1 数据流生命周期
2 深度思考
一切不能进行实时流处理的数据都是无效的数据。在流处理时代,SparkStreaming有着强大吸引力,而且发展前景广阔,加之Spark的生态系统,Streaming可以方便调用其他的诸如SQL,MLlib等强大框架,它必将一统天下。
Spark Streaming运行时与其说是Spark Core上的一个流式处理框架,不如说是Spark Core上的一个最复杂的应用程序。如果可以掌握Spark streaming这个复杂的应用程序,那么其他的再复杂的应用程序都不在话下了。这里选择Spark Streaming作为版本定制的切入点也是大势所趋。
Receiver在启动的时候是通过ReceiverSupervisor的start方法启动的。
Receiver接受数据的过程类似于MVC模式: Receiver,ReceiverSupervisor和Driver的关系相当于Model,Control,View,也就是MVC。 Model就是Receiver,存储数据Control,就是ReceiverSupervisor,Driver是获得元数据,也就是View。
Receiver接受数据流这个的过程:首先会调用ReceiverSupervisor的onStart()方法来启动Receiver。
然后开启blockPushingThread线程,它将Receiver接受到的将数据写入到BlockManager中。
private val blockPushingThread = new Thread() { override def run() { keepPushingBlocks() } }
每过一个时间间隔会从队列中取出所有的blocks,调用pushBlocks方法
private def pushBlock(block: Block) {
listener.onPushBlock(block.id, block.buffer)
logInfo("Pushed block " + block.id)
}
ReceiverSupervisorImpl,它本生是一个接口,这是在standalone模式下的具体实现。所以说它是数据流中的Controller。
在改监听器中定义了onPushBlock方法
def onPushBlock(blockId: StreamBlockId, arrayBuffer: ArrayBuffer[_]) {
pushArrayBuffer(arrayBuffer, None, Some(blockId))
}
所以最终会调用其中的pushArrayBuffer方法。将数据放入到blocksForPushing队列中。
blocksForPushing的数据是由blockIntervalTimer定时器定期的将BlockGenerator的currentBuffer中的数据写入的。
private def updateCurrentBuffer(time: Long): Unit = {
try {
var newBlock: Block = null
synchronized {
if (currentBuffer.nonEmpty) {
val newBlockBuffer = currentBuffer
currentBuffer = new ArrayBuffer[Any]
val blockId = StreamBlockId(receiverId, time - blockIntervalMs)
listener.onGenerateBlock(blockId)
newBlock = new Block(blockId, newBlockBuffer)
}
}
if (newBlock != null) {
blocksForPushing.put(newBlock) // put is blocking when queue is full
}
} catch {
case ie: InterruptedException =>
logInfo("Block updating timer thread was interrupted")
case e: Exception =>
reportError("Error in block updating thread", e)
}
}
每个200ms就会将currentBuffer中的数据写到blocksForPushing队列中,而blocksForPushing队列会每个10ms就写入到BlockManager中。
最后借用一下兄弟们画的流程图