有条件执行Spark中的mapPartition?

时间:2018-11-08 18:38:10

标签: scala apache-spark client

给出一个数据帧,该数据帧具有对可通过Web访问的API进行Http调用的参数,我想将这些API调用分配给集群的所有节点,进行调用,并返回包含调用结果的新数据帧。这相对简单,我有一个可行的解决方案。

不能简单地限制API调用的速率。例如,如果您每秒只能进行100个API调用,那么我需要某种方法来确保我们不超过限制。另外,如果在较大的群集上运行,我希望有一些方法来确保此分布式API调用程序不会变成低级DDoS攻击。

一个可行的解决方案是,当每个线程从服务器收到拒绝消息时,每个线程都进入睡眠状态(HTTP 429-请求过多)。但是,此时您已经执行了DDoS攻击;在此之前,我想放慢脚步。

我试图通过累加器和广播变量来实现这一点。每个调用都实现了累加器,广播变量是开始时间。然后,每个工作人员都可以将累加器除以时间,以查看请求率是否过高。不幸的是,you can't read an accumulator from a worker。这是行不通的,而且我看不到有任何方法可以使它工作。

除了可以通过读取驱动程序来控制速率外,我可以使用相同的解决方案。我可以将数据集划分为一堆小分区,每个分区可能是10个或100个。然后,驱动程序可以在映射每个分区之前检查速率。但是,我不知道将在驱动程序端执行的条件睡眠语句引入.mapPartition()调用的任何方法。

工作流程如下所示(在Spark 1.6.3中)

input.repartition(repartitionSetter(n))
  .select(...fields of interest...)
  .as[(... Tuple DataSet specification ...)]
  .mapPartitions(distributedApiCalls)
  .toDF().toDF( ... column name specification ...)

条件语句将像这样工作:

while (tooManyCalls()) {
  logger.log("Throttling API rate in APPNAME")
  Thread.sleep(1000)
}

def tooManyCalls(): Boolean = {
  val now = Calendar.getInstance.getTimeinMillis
  val timeElapsed = (now - broadcastStartTime.value) / 1000
  (accumulator.value + n) > (timeElapsed * rateLimitPerSec) // true if going too fast
}

在这里,repartitionSetter将数据集划分为大小为n的分区,而distributedAPICalls是传递给每个分区以访问API的函数。

是否可以在MapPartion之前将条件语句合并到分布式API调用工作流中?

我们正在升级到Spark 2.X,但是此功能应在升级之前完成,因此理想的解决方案将同时适用于Spark 1.6.3和Spark 2.X

0 个答案:

没有答案