Apache flink 在ProcessFunction中使用多线程库,回调依赖于out参数
我正在开发一个Apache flink 在ProcessFunction中使用多线程库,回调依赖于out参数,apache-flink,Apache Flink,我正在开发一个协处理函数,它使用第三方库根据某些规则检测特定的事件模式。因此,最后,ProcessElement1方法基本上是将事件转发到此库并注册回调,以便在检测到匹配时,协处理函数可以发出输出事件。为了实现这一点,回调依赖于对ProcessElement1中的out:Collector[T]参数的引用 话虽如此,我不确定Flink是否很好地支持此用例,因为: 第三方库可能跨越多个线程(假设我无法控制跨越的线程数量,这由库决定) 我不确定Flink是否会重新创建out,或者在某个时候做些什么,
协处理函数
,它使用第三方库根据某些规则检测特定的事件模式。因此,最后,ProcessElement1
方法基本上是将事件转发到此库并注册回调,以便在检测到匹配时,协处理函数可以发出输出事件。为了实现这一点,回调依赖于对ProcessElement1
中的out:Collector[T]
参数的引用
话虽如此,我不确定Flink是否很好地支持此用例,因为:
out
,或者在某个时候做些什么,使回调中的引用无效,使它们崩溃作为基于Arvid评论的更新。由于我当前的流程功能对我来说已经很好了,除了我没有邮箱执行器的访问权限之外,我只是创建了一个自定义操作符来注入它:
class MyOperator(myFunction: MyFunction)
extends KeyedCoProcessOperator(myFunction)
{
private lazy val mailboxExecutor = getContainingTask
.getMailboxExecutorFactory
.createExecutor(getOperatorConfig.getChainIndex)
override def open(): Unit = {
super.open()
userFunction.asInstanceOf[MyFunction].mailboxExecutor = mailboxExecutor
}
}
这样,我可以注册回调,将发送邮件进行逐一处理。在主应用程序中,我是这样使用它的:
.transform("wrapping function in operator", new MyOperator(new MyFunction()))
到目前为止,我觉得一切都很好,但如果你看到问题或知道更好的方法,再次听到你对这件事的想法会很好。特别是,访问邮箱执行器的方式确实有点笨拙…如果您有异步回调,您确实应该使用
asyncIO
。因此,使用协处理函数发出一个Tuple2
,并在它后面直接有一个asyncIO
Op现在补充说,他可能根本得不到结果,这使得
asyncIO
难以使用。您可以依靠超时来触发,以便删除元素,但这可能会减慢处理速度,因为asyncIO的“活动”元素队列有限
因此,在Flink 1.10中的方法可能是使用MailboxExecutor
实现一个自定义操作符
获取执行器仍然有点笨拙,但是您可以检查AsyncWaitOperator
和AsyncWaitOperatorFactory
使用executor的代码草图
// setup is optionally but if you use timestamped records, you usually do that
void setup(StreamTask<?, ?> containingTask, StreamConfig config, Output<StreamRecord<OUT>> output) {
super.setup(containingTask, config, output);
this.timestampedCollector = new TimestampedCollector<>(output);
}
void processElement(record) {
externalLib.addElement(record, (match) -> {
mailboxExecutor.execute(() -> {
timestampedCollector.collect(match);
});
});
}
//安装是可选的,但如果使用时间戳记录,通常会这样做
无效设置(StreamTask包含任务、StreamConfig配置、输出){
super.setup(包含任务、配置、输出);
this.timestampedCollector=新的timestampedCollector(输出);
}
无效处理元素(记录){
外部添加元素(记录,(匹配)->{
mailboxExecutor.execute(()->{
timestampedCollector.collect(匹配);
});
});
}
请注意,这涉及到相当多的@PublicEvolutioning代码,我们的议程上已经有了一些更改。因此,请准备好调整1.11的代码。如果您有异步回调,您确实应该使用
asyncIO
。因此,使用协处理函数发出一个Tuple2
,并在它后面直接有一个asyncIO
Op现在补充说,他可能根本得不到结果,这使得
asyncIO
难以使用。您可以依靠超时来触发,以便删除元素,但这可能会减慢处理速度,因为asyncIO的“活动”元素队列有限
因此,在Flink 1.10中的方法可能是使用MailboxExecutor
实现一个自定义操作符
获取执行器仍然有点笨拙,但是您可以检查AsyncWaitOperator
和AsyncWaitOperatorFactory
使用executor的代码草图
// setup is optionally but if you use timestamped records, you usually do that
void setup(StreamTask<?, ?> containingTask, StreamConfig config, Output<StreamRecord<OUT>> output) {
super.setup(containingTask, config, output);
this.timestampedCollector = new TimestampedCollector<>(output);
}
void processElement(record) {
externalLib.addElement(record, (match) -> {
mailboxExecutor.execute(() -> {
timestampedCollector.collect(match);
});
});
}
//安装是可选的,但如果使用时间戳记录,通常会这样做
无效设置(StreamTask包含任务、StreamConfig配置、输出){
super.setup(包含任务、配置、输出);
this.timestampedCollector=新的timestampedCollector(输出);
}
无效处理元素(记录){
外部添加元素(记录,(匹配)->{
mailboxExecutor.execute(()->{
timestampedCollector.collect(匹配);
});
});
}
请注意,这涉及到相当多的@PublicEvolutioning代码,我们的议程上已经有了一些更改。因此,请准备好调整1.11的代码。谢谢Arvid!我还被指向Flink用户邮件列表中的asyncIO模式(请参见)。Arvid,如果我使用AbstractStreamOperator(注册回调时传递其
输出
),而不是UDF,您的评论是否仍然适用?是,在最近的flink版本1.10.0中,这一点更为重要,因为我们删除了不同线程的同步;必须从主任务线程收集所有内容asyncIO
或底层的AsyncWaitOperator
正在引擎盖下使用它。如果您真的想提供一个自定义运算符来避免两个运算符,请看一下AsyncWaitOperator
的实现。因此,我猜没有匹配项时没有回调?(如果有,您可以使用asyncIO
回调并向其提供一个空结果集。)我用另一种方法更新了我的答案。如果您使用的是旧版本的Flink,您的方法可能仍然有效(异步输出元素时需要获取检查点锁,这样检查点就不会看到不一致的状态)。谢谢Arvid!我还被指向了Flink用户邮件列表中的asyncIO模式(请参阅)。Arvid,如果我使用AbstractStreamOperator(当regi