Spark Streaming流控机制

概述:

1、通过一个StreamingListener实现RateController来进行批次完成更新,事件触发调节。
2、通过PIDRateEstimator利用PID控制算法对输入速率进行反馈调节,计算合适的输入供给速率。
3、通过ReceiverTracker来将新的流控阈值速率发送到ReceiverTrackerEndpoint进行发布。
4、通过ReceiverSupervisorImpl中Receiver的Rpc端点接收并匹配处理。
5、通过Guava速率控制方法进行速率限制。

RateController

其继承了StreamingListener并重写了onBatchCompleted方法,在批次完成时系统便会调用该方法

override def onBatchCompleted(batchCompleted: StreamingListenerBatchCompleted) {
  val elements = batchCompleted.batchInfo.streamIdToInputInfo

  for {
    // 获取批次执行的基本信息,最后完成时间、处理延迟、调度延迟、以及StreamID与记录数的对应值
    processingEnd <- batchCompleted.batchInfo.processingEndTime
    workDelay <- batchCompleted.batchInfo.processingDelay
    waitDelay <- batchCompleted.batchInfo.schedulingDelay
    elems <- elements.get(streamUID).map(_.numRecords)
  } computeAndPublish(processingEnd, elems, workDelay, waitDelay)
}

然后便是计算新速率,由rateEstimator.compute计算

private def computeAndPublish(time: Long, elems: Long, workDelay: Long, waitDelay: Long): Unit =
Future[Unit] {

  val newRate = rateEstimator.compute(time, elems, workDelay, waitDelay)
  newRate.foreach { s =>
    rateLimit.set(s.toLong)
    publish(getLatestRate())
  }
}

RateEstimator与PIDRateEstimator
PIDRateEstimator实现了RateEstimator的compute方法,其计算过程主要运用到经典的工程学控制算法PID(Proportional-Integral-Derivative),这是一种通过误差的比例、积分、微分共同作用的反馈控制算法,能够很好地通过误差反馈实现比较优良的目标量的调节。文末有个链接可能会更好的帮助了解这一控制算法。


其计算代码如下,对应于PIDRateEstimator.scala文件

val newRate = (latestRate - proportional * error - 
integral * historicalError - derivative * dError).max(minRate)

可见,是将计算值和通过spark.streaming.backpressure.pid.minRate参数设置的值取大者作为下一次输入值。计算完成后我们看一下其发布过程。

ReceiverTracker

需要先看一个RateController的子类,实现父类的publish方法

private[streaming] class ReceiverRateController(id: Int, estimator: RateEstimator)
    extends RateController(id, estimator) {
  override def publish(rate: Long): Unit =
    // 这里可以看到是利用了ReceiverTracker来发送更新
    ssc.scheduler.receiverTracker.sendRateUpdate(id, rate)
}

具体send过程,下边的endpoint实际为ReceiverTrackerEndpoint,这里能够看出用的是Spark的RPC通信机制,通过在RpcEnv中注册的ReceiverTrackerEndpoint将控制信息发给Receiver。

def sendRateUpdate(streamUID: Int, newRate: Long): Unit = synchronized {
  if (isTrackerStarted) {
    endpoint.send(UpdateReceiverRateLimit(streamUID, newRate))
  }
}

然后再发送到Receiver注册的RpcEndpoint,用于接收Driver中ReceiverTracker发送过来的消息并处理。

case UpdateReceiverRateLimit(streamUID, newRate) =>
  for (info <- receiverTrackingInfos.get(streamUID); eP <- info.endpoint) {
    // 发送消息到Receiver的RPC端点
    eP.send(UpdateRateLimit(newRate))
  }

端点接收消息后匹配处理,在ReceiverSupervisorImpl中

private val endpoint = env.rpcEnv.setupEndpoint(
"Receiver-" + streamId + "-" + System.currentTimeMillis(), new ThreadSafeRpcEndpoint {
  override val rpcEnv: RpcEnv = env.rpcEnv
	......
    // 匹配到这里
    case UpdateRateLimit(eps) =>
      logInfo(s"Received a new rate limit: $eps.")
      registeredBlockGenerators.asScala.foreach { bg =>
        // 实际调用到RateLimiter的updateRate方法
        bg.updateRate(eps)
      }
  }
})

Guava限速

速率限制的实现在RateLimiter.scala中,并实际调用到Guava的实现库RateLimiter的实现。

private lazy val rateLimiter = GuavaRateLimiter.create(getInitialRateLimit().toDouble)
......
def waitToPush() {
  // 获取许可,如获取不到则阻塞
  rateLimiter.acquire()
}
......  
private[receiver] def updateRate(newRate: Long): Unit =
if (newRate > 0) {
  if (maxRateLimit > 0) {
    rateLimiter.setRate(newRate.min(maxRateLimit))
  } else {
    rateLimiter.setRate(newRate)
  }
}

可见,最终使用了Guava的RateLimiter限流实现。具体体现在控制数据放入缓存的速率,waitToPush,如果获取到许可,则放入缓存,否则等待。

def addData(data: Any): Unit = {
  if (state == Active) {
    // 这里会进行限制
    waitToPush()
    synchronized {
      if (state == Active) {
        currentBuffer += data
      } else {
        throw new SparkException(
          "Cannot add data as BlockGenerator has not been started or has been stopped")
      }
    }
  } else {
    throw new SparkException(
      "Cannot add data as BlockGenerator has not been started or has been stopped")
  }
}

具体的Guava经典算法这里不做研究。总结起来,这里的限流方法即是控制单位时间内数据输入的许可数量,也就是说单位时间内只有目标数量的数据可以获得许可。

链接

PID控制算法讲解

发布了95 篇原创文章 · 获赞 5 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_43878293/article/details/103653316