动态节流Apache Spout

时间:2019-10-15 22:28:59

标签: timeout apache-storm throttling

我有一个拓扑,其中spout从Kafka读取数据并将其发送到bolt,然后依次调用REST API(A)和另一个REST API(B)。到目前为止,API B还没有节流。现在他们已经实现了节流(每时钟分钟x最大呼叫数)。

我们需要实现节流处理程序。

选项A

最初,我们正在考虑在REST API(A)级别中进行此操作,并放置一个

Thread.sleep(x in millis)一旦调用被REST API(B)限制

但这将保留所有REST(A)呼叫等待那么长的时间(在1秒到59秒之间变化),这可能会增加新呼叫的负载。

选项B

REST API(A)将有关被限制的响应发送回Bolt。 Bolt通知Spout处理失败

  • 不更改这些消息的偏移量
  • 告诉spout停止从Kafka阅读并停止向Bolt发送消息。
  • Spout等待一段时间(例如一分钟),然后从其离开的地方恢复

选项A可以直接实施,但我认为不是一个好的解决方案。

试图确定选项B是否可以通过 topology.max.spout.pending 进行实施,但是如何动态地与Storm进行通信以限制Spout。任何人都可以对这个选项分享一些想法。

选项C

REST API(B)限制来自REST(A)的调用,该调用将不处理该调用,但会将429响应代码发送到螺栓。螺栓会将消息重新排队到另一个风暴拓扑的错误主题部分。此消息可以包含重试计数,如果同一消息再次受到限制,我们可以使用++ retry count重新进行排队。

更新帖子,找到使选项B可行的解决方案。

选项D

https://github.com/apache/storm/blob/master/external/storm-kafka-client/src/main/java/org/apache/storm/kafka/spout/KafkaSpoutRetryExponentialBackoff.java

/**
 * The time stamp of the next retry is scheduled according to the exponential backoff formula (geometric progression):
 * nextRetry = failCount == 1 ? currentTime + initialDelay : currentTime + delayPeriod^(failCount-1),
 * where failCount = 1, 2, 3, ... nextRetry = Min(nextRetry, currentTime + maxDelay).
 * <p/>
 * By specifying a value for maxRetries lower than Integer.MAX_VALUE, the user decides to sacrifice guarantee of delivery for the
 * previous polled records in favor of processing more records.
 *
 * @param initialDelay      initial delay of the first retry
 * @param delayPeriod       the time interval that is the ratio of the exponential backoff formula (geometric progression)
 * @param maxRetries        maximum number of times a tuple is retried before being acked and scheduled for commit
 * @param maxDelay          maximum amount of time waiting before retrying
 *
 */
public KafkaSpoutRetryExponentialBackoff(TimeInterval initialDelay, TimeInterval delayPeriod, int maxRetries, TimeInterval maxDelay) {
    this.initialDelay = initialDelay;
    this.delayPeriod = delayPeriod;
    this.maxRetries = maxRetries;
    this.maxDelay = maxDelay;
    LOG.debug("Instantiated {}", this.toStringImpl());
}

步骤如下:

  1. 使用上述构造函数创建kafkaSpoutRetryService
  2. 使用以下命令将重试设置为KafkaSpoutConfig KafkaSpoutConfig.builder(kafkaBootStrapServers, topic).setRetry(kafkaSpoutRetryService)
  3. 使用以下命令在Rest API(B)中出现节流的情况下使Bolt失效 collector.fail(tuple)将根据第1步和第2步的重试配置设置发出信号,通知spout再次处理元组。

1 个答案:

答案 0 :(得分:1)

您的选项D听起来不错,但是为了避免在对API A的调用中出现重复,我认为您应该考虑将拓扑分为两部分。

具有一个拓扑结构,该拓扑结构将从原始的Kafka主题(称为主题1)读取,调用REST API A,并将螺栓输出返回的所有内容写回到Kafka主题(称为主题2)。

然后创建第二个拓扑,其唯一的工作就是从主题2中读取内容,并调用REST APIB。

这将允许您在不使API B饱和时避免对API A的额外调用的情况下使用选项D。拓扑看起来像

Kafka 1->螺栓A-> REST API A-> Kafka 2 Kafka 2-> Bolt B-> REST API B

如果您想使解决方案对节流更加敏感,可以在Storm中使用topology.max.spout.pending配置来限制同时可以运行多少个元组。然后,您可以将螺栓B置于飞行中的元组中,直到节流终止为止,此时您可以使其再次尝试发送元组。您可以使用OutputCollector.resetTupleTimeout来避免在Bolt B等待节流终止时元组超时。您可以使用刻度元组使Bolt B定期唤醒,并检查节流是否已过期。