Flink 异步I/O

1.为什么需要异步IO

flink在做实时处理时,有时候需要和外部数据交互,但是通常情况下这个交互过程是同步的,这样就会产生大量的等待时间;而异步操作可以在单个函数实例中同时处理多个请求,并且同时接收相应。这样等待时间就平均分摊到了多个请求上,大大减少了请求的等待时长,可以提高实时处理的吞吐量。

2.使用flink异步IO的先决条件

  • 需要所连接的数据库支持异步客户端
  • 在没有异步客户端的情况下,可以通过创建多个客户端并使用线程池处理同步调用来尝试将同步客户端转变为有限的并发客户端

3. flink异步IO的使用步骤

  • 实现AsyncFunction接口;
  • 一个回调,该函数取回操作的结果,然后将结果传递给ResultFuture;
  • 在DataStream上应用异步IO操作。

4. 使用示例

import scala.concurrent._
import ExecutionContext.Implicits.global

/**
  * 使用scala并发包的Future模拟一个异步客户端
  */
class DatabaseClient {
  def query: Future[Long] = Future {
    System.currentTimeMillis() / 1000
  }
}

 

/** 'AsyncFunction' 的一个实现,向数据库发送异步请求并设置回调 
  * 改编自官网实例
  */

class AsyncDatabaseRequest extends AsyncFunction[Int, (Int, Long)] {

  /** The database specific client that can issue concurrent requests with callbacks */
  lazy val client: DatabaseClient = new DatabaseClient

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

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

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

    // 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: Long => resultFuture.complete(Iterable((str, result)))
    }
  }
}

object AsynchronousIOTest {

  def main(args: Array[String]): Unit = {

    val env = StreamExecutionEnvironment.getExecutionEnvironment

    val data: immutable.Seq[Int] = Range(1, 10)

    // 创建数据流
    val dataStream: DataStream[Int] = env.fromCollection(data)

     // 使用异步IO
     val asyn = AsyncDataStream.unorderedWait(
      dataStream,//执行异步操作的DataStream
      new AsyncDatabaseRequest,//
      1000, TimeUnit.MILLISECONDS, //超时时间
      100 // 进行中的异步请求的最大数量
    )

    asyn.print()

    env.execute("AsynchronousIOTest")

  }

}

结果顺序

AsyncDataStream 有两个静态方法,orderedWait 和 unorderedWait,对应了两种输出模式:有序和无序。

  • 有序:消息的发送顺序与接受到的顺序相同(包括 watermark ),也就是先进先出。
  • 无序:
    在 ProcessingTime 的情况下,完全无序,先返回的结果先发送。
    在 EventTime 的情况下,watermark 不能超越消息,消息也不能超越 watermark,也就是说 watermark 定义的顺序的边界。在两个 watermark 之间的消息的发送是无序的,但是在watermark之后的消息不能先于该watermark之前的消息发送。

超时处理

当异步IO请求超时时,默认情况下会引发异常并重新启动作业。如果要处理超时,可以重写AsyncFunction#timeout方法。

  override def timeout(input: Int,
                       resultFuture: ResultFuture[(Int, Long)]): Unit =
    super.timeout(input, resultFuture)

 

容错保证

异步IO运算符提供完全一次的容错保证。它在检查点中存储正在进行的异步请求的记录,并在从故障中恢复时恢复/重新触发请求。

其他资料

关于flink异步IO的更多信息可以参考flink官网或者Flink 原理与实现:Aysnc I/O这篇文章。

Futures和事件驱动编程的知识可以参考《AKKA入门与实践》这本书第二章的内容:Actor与并发。


参考资料:

https://ci.apache.org/projects/flink/flink-docs-release-1.8/dev/stream/operators/asyncio.html
http://wuchong.me/blog/2017/05/17/flink-internals-async-io/
《AKKA入门与实践》

### 实现 Flink 中的异步 Sink 功能 在 Apache Flink 的流处理框架中,异步 I/O 是一种高效的方式,可以用来优化对外部系统的写入操作。通过使用 `AsyncFunction` 或者自定义实现,可以在不影响主数据流的情况下执行异步操作。 以下是关于如何在 Flink 中实现异步 Sink 功能的具体说明: #### 1. 使用 Async I/O API 进行异步调用 Flink 提供了一个内置的 `AsyncFunction` 接口来支持异步操作。该接口允许开发者将异步请求封装到函数中,并通过回调机制获取结果[^3]。 下面是一个简单的代码示例展示如何创建一个基于 Future 的异步 Sink 函数: ```java import org.apache.flink.streaming.api.datastream.DataStream; import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment; import org.apache.flink.table.functions.AsyncTableFunction; public class AsyncSinkExample { public static void main(String[] args) throws Exception { final StreamExecutionEnvironment env = StreamExecutionEnvironment.getExecutionEnvironment(); DataStream<String> input = env.fromElements("key1", "key2", "key3"); // 调用 asyncIOMap 方法注册异步 IO 操作 input.asyncIO(new MyAsyncFunction(), 100).print(); env.execute("Async Sink Example"); } private static class MyAsyncFunction implements org.apache.flink.streaming.api.functions.sink.AsyncSinkBase<String, String> { @Override public void invokeAsync(String input, Context context, ResultFuture<String> resultFuture) { CompletableFuture.supplyAsync(() -> processElement(input)) .whenComplete((output, throwable) -> { if (throwable != null) { System.err.println("Error processing element: " + throwable.getMessage()); resultFuture.complete(Collections.emptyList()); // 返回空集合以避免阻塞 } else { resultFuture.complete(Collections.singletonList(output)); } }); } private String processElement(String input) { try { Thread.sleep(10); // Simulate an asynchronous operation. } catch (InterruptedException e) { throw new RuntimeException(e); } return input.toUpperCase(); // 假设我们只是简单地转换成大写字母作为模拟的结果。 } } } ``` 上述代码展示了如何利用 Java 的 `CompletableFuture` 来完成异步任务,并将其集成到 Flink 数据流中[^4]。 #### 2. 处理错误情况下的恢复策略 为了确保程序能够稳定运行,在遇到不可预期的异常时应配置合适的 Checkpoint 和 Restart 策略。这可以通过以下方式实现: - **启用 Checkpoints**: 设置合理的 checkpoint 时间间隔以及存储位置。 ```java env.enableCheckpointing(5000); // every 5 seconds ``` - **指定 Restart Strategy**: 定义失败后的重试次数或者时间窗口内的最大尝试数。 ```java env.setRestartStrategy(RestartStrategies.fixedDelayRestart( 3, // 尝试重新启动的最大次数 Time.of(10, TimeUnit.SECONDS))); // 每次重启之间的延迟时间为 10 秒钟 ``` 这些措施有助于提高作业的整体健壮性和可用性[^1]。 #### 3. 防止反压现象的发生 正如提到的一样,如果未来对象中的错误未被妥善处理,则可能导致下游算子得不到任何输入而引发所谓的“反压”。因此建议总是显式捕获可能发生的异常并采取适当行动,比如记录日志或发送告警通知等[^2]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值