【Flink】异步IO知多少?官方文档永远是最好的教科书

文章目录

    • Asynchronous I/O for External Data Access | 用于外部数据访问的异步I/O
      • The need for Asynchronous I/O Operations | 异步IO操作的需要
      • Prerequisites | 使用异步IO的前提条件
      • Async I/O API


Asynchronous I/O for External Data Access | 用于外部数据访问的异步I/O

老规矩,上来先上官方文档:https://ci.apache.org/projects/flink/flink-docs-release-1.7/dev/stream/operators/asyncio.html
 

The need for Asynchronous I/O Operations | 异步IO操作的需要

Flink在做数据流计算时,很多时候需要与外部系统进行交互(比如MySQL数据库、Redis、Hive、HBase等存储系统),这就需要注意系统间通信延迟是否会拖慢整个Flink作业,影响整体吞吐量和实时性
流计算系统经常会简单访问外部数据库中的数据,比如在MapFunction中,实现方式是向数据库发送用户a的查询请求,然后等待结果返回,在这之前,我们无法发送用户b的查询请求,在许多情况下,这种等待占用了函数的绝大部分时间,这就是同步交互的方式。
【Flink】异步IO知多少?官方文档永远是最好的教科书_第1张图片
如图中描述,橙色标识为等待时间,蓝色为用户的请求,绿色为数据库的反馈。可以直观地发现同步交互模式中,有较久的等待时间,这极大地阻碍了吞吐和延迟。为了解决这一问题,异步IO模式可以并发地处理多个请求和回复,也就是说a,b,c,d的用户请求可以连续向数据库发送,同时数据库先给哪个用户回复就先处理哪个回复,不需要阻塞等待,这也是异步IO的基本实现原理,异步IO的目的也是为了提高吞吐量
 

Prerequisites | 使用异步IO的前提条件

1.需要一个支持异步请求的数据库客户端(比如使用vertx,但是目前只支持scala2.12的版本,可以使用java类库来做)
2.或者在没有这样的客户端的情况下,可以通过创建多个客户端并使用线程池处理同步调用来尝试将同步客户端转变为有限的并发客户端。但是,这种方法通常比适当的异步客户端效率低。(比如可以写ExecutorService来实现)
 

Async I/O API

Async I/O API允许用户在数据流中使用异步客户端访问外部存储,该API可以处理与数据流的集成,以及消息处理顺序(handling order)事件时间(event time)容错(fault tolerance) 等。

如果目标数据库中有异步客户端,则用三步即可实现异步流式转换操作:
1.实现用来分发请求的AsyncFunction,用来向数据库发送异步请求并设置回调
2.获取操作结果的callback,并将它提交给ResultFuture
3.将异步IO操作应用于DataStream

官方给了一个Async I/O API的基本模式:

/**
 * 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)

你可能感兴趣的:(Flink)