如何在Apache Beam步骤中并行化HTTP请求?

时间:2018-10-15 08:09:52

标签: google-cloud-dataflow apache-beam

我在Google Dataflow上运行了一个Apache Beam管道,其工作非常简单:

  • 它从Pub / Sub中读取单个JSON对象
  • 解析它们
  • 并通过HTTP将其发送到某个API

该API要求我分批发送75个项目。因此,我建立了DoFn来累积列表中的事件,并在事件达到75后通过此API发布。结果太慢了,所以我想不要使用线程池在不同的线程中执行这些HTTP请求。

我现在所拥有的实现看起来像这样:

private class WriteFn : DoFn<TheEvent, Void>() {
  @Transient var api: TheApi

  @Transient var currentBatch: MutableList<TheEvent>

  @Transient var executor: ExecutorService

  @Setup
  fun setup() {
    api = buildApi()
    executor = Executors.newCachedThreadPool()
  }

  @StartBundle
  fun startBundle() {
    currentBatch = mutableListOf()
  }

  @ProcessElement
  fun processElement(processContext: ProcessContext) {
    val record = processContext.element()

    currentBatch.add(record)

    if (currentBatch.size >= 75) {
      flush()
    }
  }

  private fun flush() {
    val payloadTrack = currentBatch.toList()
    executor.submit {
      api.sendToApi(payloadTrack)
    }
    currentBatch.clear()
  }

  @FinishBundle
  fun finishBundle() {
    if (currentBatch.isNotEmpty()) {
      flush()
    }
  }

  @Teardown
  fun teardown() {
    executor.shutdown()
    executor.awaitTermination(30, TimeUnit.SECONDS)
  }
}

就数据将其写入API而言,这似乎“很好”地工作。但是我不知道这是否是正确的方法,而且我感觉这很慢。

我认为它很慢的原因是,在进行负载测试(通过向Pub / Sub发送数百万个事件)时,将这些消息转发到API的管道最多需要8倍的时间(具有响应时间少于8毫秒),而不是让我的笔记本电脑将它们输入到Pub / Sub中。

我的实现是否存在任何问题?这是我应该这样做的方式吗?

还...我是否需要等待@FinishBundle方法中的所有请求完成(即通过让执行者退还期货并等待它们)?

1 个答案:

答案 0 :(得分:2)

您在这里有两个相互关联的问题:

  1. 您这样做正确吗/是否需要更改任何内容?
  2. 您需要在spring: application: name: grouptype/groupname/DB cloud: vault: authentication: TOKEN token: sometoken generic: enabled: true backend: group default-conext: grouptype/groupname/DB host: 10.20.30.40 port: 8200 scheme: http #uri: http://10.20.30.40:8200 connection-timeout: 5000 read-timeout: 15000 config: order: -10 中等待吗?

第二个答案:是的。但是实际上,您需要更彻底地冲洗,这将变得很清楚。

一旦您的@FinishBundle方法成功,Beam流道将假定捆绑包已成功完成。但是您的@FinishBundle仅发送请求-不能确保请求成功。因此,如果请求随后失败,则可能会丢失数据。您的@FinishBundle方法实际上应该处于阻塞状态并等待@FinishBundle的成功确认。顺便说一句,以上所有内容均应是幂等的,因为完成捆绑后,地震可能会发生并引起重试;-)

所以要回答第一个问题:您应该更改任何内容吗?就在上面。只要您确定在提交捆绑包之前就已经提交结果,这种方式的批处理就可以起作用。

您可能会发现这样做会导致您的管道运行变慢,因为TheApi的发生频率比@FinishBundle高。要跨捆绑批处理请求,您需要使用状态和计时器的低级功能。我在Query postgres jsonb by value regardless of keys上编写了您的用例的人为设计版本。我会对这对您的工作方式非常感兴趣。

当管道中存在持久的随机播放时,很可能只是您所期望的极低延迟(在低毫秒范围内)不可用。