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是否很好地支持此用例,因为:

  • 第三方库可能跨越多个线程(假设我无法控制跨越的线程数量,这由库决定)
  • 我不确定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