flink async 实现
主要解决两个问题
1. 如果需要scala实现flink的异步调用,请参考代码
2. 如果要异步调用的组件,并没有提供async I/O的驱动,那么请参考代码
3. (非主要)对异步调用的线程池有兴趣,可以讨论以下
废话不多说,直接上code
package com.test
import java.util.Collections
import java.util.concurrent.{Executors, TimeUnit}
import org.apache.flink.calcite.shaded.com.google.common.util.concurrent.MoreExecutors
//import org.apache.flink.runtime.concurrent.Executors
import org.apache.flink.streaming.api.datastream.AsyncDataStream
import org.apache.flink.streaming.api.environment.StreamExecutionEnvironment
import org.apache.flink.streaming.api.functions.async.{AsyncFunction, ResultFuture}
import scala.concurrent.{ExecutionContext, Future}
import scala.util.{Failure, Success}
/**
* @author johnCai
* @date 2021/4/27 下午10:03
* @version 1.0
*/
object AsyncTest {
def main(args: Array[String]): Unit = {
val env = StreamExecutionEnvironment.getExecutionEnvironment
//注意这个地方,下边统称A处
env.setParallelism(2)
val ds = env.socketTextStream("localhost",2222)
AsyncDataStream
.unorderedWait(ds,new AsyncRequest,8000, TimeUnit.MICROSECONDS,100)
.print("out put: ")
env.execute("async")
}
}
class AsyncRequest extends AsyncFunction[String,String] {
var i = 0
//第一种方式
//implicit lazy val executor: ExecutionContext = ExecutionContext.fromExecutor(Executors.directExecutor())
//第二中方式
implicit lazy val executor: ExecutionContext = ExecutionContext.fromExecutor(MoreExecutors.directExecutor())
//第三种方式(不可行)
//implicit lazy val executor: ExecutionContext = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(10))
override def asyncInvoke(input: String, resultFuture: ResultFuture[String]): Unit = {
val f = Future {
i+=1
val str = "result_"+i
println(s"in async recv $str")
Thread sleep 2000
str
}
f onComplete {
case Success(value) =>
println(s"in async send ${value}")
resultFuture.complete(Collections.singletonList(value))
case Failure(exception) => exception.printStackTrace()
}
}
}
flink官方文档摘录
实现提示 #
在实现使用 Executor(或者 Scala 中的 ExecutionContext)和回调的 Futures 时,建议使用 DirectExecutor
,因为通常回调的工作量很小,DirectExecutor
避免了额外的线程切换开销。回调通常只是把结果发送给 ResultFuture
,也就是把它添加进输出缓冲。从这里开始,包括发送记录和与 chenkpoint 交互在内的繁重逻辑都将在专有的线程池中进行处理。
DirectExecutor
可以通过 org.apache.flink.runtime.concurrent.Executors.directExecutor()
或 com.google.common.util.concurrent.MoreExecutors.directExecutor()
获得。
警告 #
Flink 不以多线程方式调用 AsyncFunction
我们想在这里明确指出一个经常混淆的地方:AsyncFunction
不是以多线程方式调用的。 只有一个 AsyncFunction
实例,它被流中相应分区内的每个记录顺序地调用。除非 asyncInvoke(...)
方法快速返回并且依赖于(客户端的)回调, 否则无法实现正确的异步 I/O。
例如,以下情况导致阻塞的 asyncInvoke(...)
函数,从而使异步行为无效:
- 使用同步数据库客户端,它的查询方法调用在返回结果前一直被阻塞。
- 在
asyncInvoke(...)
方法内阻塞等待异步客户端返回的 future 类型对象
大概就是这么个意思了,如果用 org.apache.flink.runtime.concurrent.Executors.directExecutor()
或 com.google.common.util.concurrent.MoreExecutors.directExecutor()
获得的executors,没有指定线程池大小,所以它的并行度是按代码A处(也就是flink设置的并行度,或者flink executor的线程池了),应为如果将flink的并行度设置为1,经测试完全串行,方法一和方法二等价。