Flink异步I/O 访问外部数据

外部数据访问的异步 I/O

本页解释了使用 Flink 的 API 进行异步 I/O 与外部数据存储。对于不熟悉异步或事件驱动编程的用户,一篇关于 Futures 和事件驱动编程的文章可能是有用的准备。

注意:有关异步 I/O 实用程序的设计和实现的详细信息,请参阅提案和设计文档 FLIP-12:异步 I/O 设计和实现。

需要异步 I/O 操作

在与外部系统交互时(例如,当使用存储在数据库中的数据丰富流事件时),需要注意与外部系统的通信延迟不会主导流应用程序的全部工作。

天真地访问外部数据库中的数据,例如在 a 中MapFunction,通常意味着同步交互:请求被发送到数据库并MapFunction等待直到收到响应。在许多情况下,这种等待占了函数时间的绝大部分。

与数据库的异步交互意味着单个并行函数实例可以同时处理多个请求并同时接收响应。这样,等待时间可以与发送其他请求和接收响应重叠。至少,等待时间分摊到多个请求中。在大多数情况下,这会导致更高的流吞吐量。

注意:在某些情况下,通过将其扩展到非常高的并行度来提高吞吐量MapFunction也是可能的,但通常会带来非常高的资源成本:拥有更多并行 MapFunction 实例意味着更多任务、线程、Flink 内部网络连接、网络与数据库、缓冲区和一般内部簿记开销的连接。

先决条件

如上一节所示,对数据库(或键/值存储)实现适当的异步 I/O 需要该数据库的客户端支持异步请求。许多流行的数据库都提供这样的客户端。

在没有这样的客户端的情况下,可以通过创建多个客户端并使用线程池处理同步调用来尝试将同步客户端变为有限的并发客户端。但是,这种方法通常不如适当的异步客户端有效。

异步 I/O API

Flink 的 Async I/O API 允许用户使用带有数据流的异步请求客户端。API 处理与数据流的集成,以及处理顺序、事件时间、容错等。

假设一个目标数据库有一个异步客户端,需要三个部分来实现对数据库的异步 I/O 流转换:

AsyncFunction调度请求的实现
一个回调,它获取操作的结果并将其传递给ResultFuture
在 DataStream 上应用异步 I/O 操作作为转换
以下代码示例说明了基本模式:

/**
 * An implementation of the 'AsyncFunction' that sends requests and sets the callback.
 */
class AsyncDatabaseRequest extends AsyncFunction[String, (String, String)] {

    /** The database specific client that can issue concurrent requests with callbacks */
    lazy val client: DatabaseClient = new DatabaseClient(host, post, credentials)

    /** The context used for the future callbacks */
    implicit lazy val executor: ExecutionContext = ExecutionContext.fromExecutor(Executors.directExecutor())


    override def asyncInvoke(str: String, resultFuture: ResultFuture[(String, String)]): Unit = {

        // issue the asynchronous request, receive a future for the result
        val resultFutureRequested: Future[String] = client.query(str)

        // set the callback to be executed once the request by the client is complete
        // the callback simply forwards the result to the result future
        resultFutureRequested.onSuccess {
            case result: String => resultFuture.complete(Iterable((str, result)))
        }
    }
}

// create the original stream
val stream: DataStream[String] = ...

// apply the async I/O transformation
val resultStream: DataStream[(String, String)] =
    AsyncDataStream.unorderedWait(stream, new AsyncDatabaseRequest(), 1000, TimeUnit.MILLISECONDS, 100)

重要说明:ResultFuture完成第一次调用ResultFuture.complete. 所有后续complete调用都将被忽略。

以下两个参数控制异步操作​​:

超时:超时定义异步请求在被认为失败之前可能需要多长时间。此参数可防止死/失败的请求。

容量:此参数定义了同时进行的异步请求的数量。尽管异步 I/O 方法通常会带来更好的吞吐量,但操作员仍然可能是流应用程序的瓶颈。限制并发请求的数量可确保操作员不会累积不断增长的待处理请求积压,但一旦容量耗尽就会触发背压。

超时处理

当异步 I/O 请求超时时,默认情况下会引发异常并重新启动作业。如果要处理超时,可以覆盖该AsyncFunction#timeout方法。确保您调用ResultFuture.complete()或ResultFuture.completeExceptionally()在覆盖时向 Flink 指示此输入记录的处理已完成。ResultFuture.complete(Collections.emptyList())如果您不想在超时发生时发出任何记录,则可以调用 。

结果顺序

频繁发出的并发请求AsyncFunction以某种未定义的顺序完成,基于哪个请求先完成。为了控制结果记录的发出顺序,Flink 提供了两种模式:

Unordered:异步请求完成后立即发出结果记录。在异步 I/O 操作符之后,流中记录的顺序与之前不同。当以处理时间为基本时间特性时,此模式具有最低的延迟和最低的开销。用于AsyncDataStream.unorderedWait(…)此模式。

Ordered:在这种情况下,将保留流顺序。结果记录的发出顺序与触发异步请求的顺序相同(操作员输入记录的顺序)。为了实现这一点,操作员会缓冲一条结果记录,直到它之前的所有记录都被发出(或超时)。这通常会在检查点中引入一些额外的延迟和一些开销,因为与无序模式相比,记录或结果在检查点状态下维护的时间更长。用于AsyncDataStream.orderedWait(…)此模式。

活动时间

当流应用程序使用event time 时,异步 I/O 操作符将正确处理水印。这意味着两种订购模式的具体含义如下:

无序:水印不会超过记录,反之亦然,这意味着水印建立了顺序边界。记录仅在水印之间无序发出。只有在发出该水印之后,才会发出在某个水印之后发生的记录。只有在发出水印之前输入的所有结果记录之后才会发出水印。

这意味着在存在水印的情况下,无序模式会引入一些与有序模式相同的延迟和管理开销。该开销的数量取决于水印频率。

Ordered:保留水印和记录的顺序,就像保留记录之间的顺序一样。与处理时间相比,开销没有显着变化。

请记住,摄取时间是事件时间的一种特殊情况,具有基于源处理时间自动生成的水印。

容错保证

异步 I/O 操作符提供了完全一次性的容错保证。它将进行中的异步请求的记录存储在检查点中,并在从故障中恢复时恢复/重新触发请求。

实施技巧

对于具有回调的Executor(或Scala 中的ExecutionContext )的Futures实现,我们建议使用 a ,因为回调通常做最少的工作,并且 a避免了额外的线程到线程切换开销。回调通常只将结果传递给,然后将其添加到输出缓冲区。从那里开始,包括记录发布和与检查点簿记交互的繁重逻辑无论如何都发生在专用线程池中。DirectExecutorDirectExecutorResultFuture

ADirectExecutor可以通过org.apache.flink.util.concurrent.Executors.directExecutor()或 获得com.google.common.util.concurrent.MoreExecutors.directExecutor()。

注意事项

AsyncFunction 不称为多线程

我们想在这里明确指出的一个常见混淆是,AsyncFunction不是以多线程方式调用的。仅存在一个实例,AsyncFunction并且为流的相应分区中的每个记录顺序调用它。除非该asyncInvoke(…)方法快速返回并依赖回调(由客户端),否则它不会导致正确的异步 I/O。

例如,以下模式会导致阻塞asyncInvoke(…)函数,从而使异步行为无效:

使用一个数据库客户端,它的查找/查询方法调用阻塞,直到收到返回结果

asyncInvoke(…)阻塞/等待方法内异步客户端返回的未来类型对象

AsyncFunction(AsyncWaitOperator) 可以在作业图中的任何位置使用,但它不能链接到SourceFunction/ SourceStreamTask。

原文:https://nightlies.apache.org/flink/flink-docs-release-1.15/docs/dev/datastream/operators/asyncio/

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值