Apache Nifi中的批处理流文件

时间:2019-04-26 10:12:55

标签: apache-nifi

我已经编写了自定义nifi处理器,该处理器试图批处理输入流文件。

但是,似乎它的行为不符合预期。这是发生了什么:

  

我将一些文件复制粘贴到服务器上。 FethFromServerProcessor从服务器获取这些文件并将其放入queue1MyCustomProcessorqueue1批量读取文件。我在batchSize上定义了MyCustomProcessor属性,并在其onTrigger()方法中,通过执行以下操作,从当前批次的queue1获取所有流文件:

session.get(context.getProperty(batchSize).asInteger())
     

onTrigger()的第一行创建时间戳,并将此时间戳添加到所有流文件中。因此,批处理中的所有文件都应具有相同的时间戳。但是,这没有发生。通常,第一个流文件获得一个时间戳,其余的流文件获得另一个时间戳。

似乎FetchFromServerProcessor从服务器获取第一个文件并将其放入queue1时,MyCustomProcessor被触发并从队列中获取所有文件。顺便说一句,碰巧以前只有一个文件,在此批处理中仅作为单个文件被选择。到MyCustomProcessor处理完该文件时,FetchFromServerProcessor已经从服务器获取了所有文件并将它们放入queue1中。因此,在处理完第一个文件后,MyCustomProcessor提取了queue1中的所有文件并组成了第二批文件,而我希望所有文件都在一个批文件中被提取。

如何避免形成两个批次?我看到人们在这种情况下讨论等待通知:12。但是我无法从这些帖子中快速理解。有人可以给我最少的步骤来使用等待通知处理器来实现此目的,还是可以给我指出一个最小的教程,该教程给出了逐步使用等待通知处理器的过程?我也解释了等待通知模式标准方法来解决批量相关问题吗?还是有其他标准方法可以做到这一点?

1 个答案:

答案 0 :(得分:0)

听起来好像这个批处理大小是到CustomProcessor的传入流文件所需的计数,所以为什么不这样写CustomProcessor#onTrigger()

@Override
public void onTrigger(ProcessContext context, ProcessSession session) throws ProcessException {
    final ComponentLog logger = getLogger();
    // Try to get n flowfiles from incoming queue
    final Integer desiredFlowfileCount = context.getProperty(batchSize).asInteger();
    final int queuedFlowfileCount = session.getQueueSize().getObjectCount();
    if (queuedFlowfileCount < desiredFlowfileCount) {
        // There are not yet n flowfiles queued up, so don't try to run again immediately
        if (logger.isDebugEnabled()) {
            logger.debug("Only {} flowfiles queued; waiting for {}", new Object[]{queuedFlowfileCount, desiredFlowfileCount});
        }
        context.yield();
        return;
    }

    // If we're here, we do have at least n queued flowfiles
    List<FlowFile> flowfiles = session.get(desiredFlowfileCount);

    try {
        // TODO: Perform work on all flowfiles
        flowfiles = flowfiles.stream().map(f -> session.putAttribute(f, "timestamp", "my static timestamp value")).collect(Collectors.toList());
        session.transfer(flowfiles, REL_SUCCESS);

        // If extending AbstractProcessor, this is handled for you and you don't have to explicitly commit
        session.commit();
    } catch (Exception e) {
        logger.error("Helpful error message");
        if (logger.isDebugEnabled()) {
            logger.error("Further stacktrace: ", e);
        }
        // Penalize the flowfiles if appropriate (also done for you if extending AbstractProcessor and an exception is thrown from this method
        session.rollback(true);
        //  --- OR ---
        // Transfer to failure if they can't be retried
        session.transfer(flowfiles, REL_FAILURE);
    }
}

如果不熟悉Java 8 stream语法,可以用以下语法代替:

        for (int i = 0; i < flowfiles.size(); i++) {
            // Write the same timestamp value onto all flowfiles
            FlowFile f = flowfiles.get(i);
            flowfiles.set(i, session.putAttribute(f, "timestamp", "my timestamp value"));
        }

semantics between penalization(告诉处理器延迟对特定流文件执行工作)和让步(告诉处理器等待一段时间以尝试再次执行任何工作)非常重要。

您可能还希望自定义处理器上的@TriggerSerially annotation确保没有多个线程在运行,以免出现竞争状况。