NiFi - 如何在ExecuteStreamCommand中引用flowFile?

时间:2017-02-24 16:02:54

标签: apache-nifi

我需要执行以下操作: sed'1d'simple.tsv> noHeader.tsv

将从我的大流文件中删除第一行(> 1 GB)。

问题是 - 我需要在我的流文件上执行它,所以它是:

sed'1d'myFlowFile> myFlowFile

问题是:我应该如何配置ExecuteStreamCommand处理器,以便它在我的流文件上运行命令并将其返回到我的流文件? 如果sed不是最佳选择,我可以考虑采取其他方式(例如尾巴)

ExecuteStreamCommand processor

谢谢,Michal

编辑2(解决方案):

下面是最终的ExecuteStreamCommand配置,可以完成我需要的操作(从流文件中删除第一行)。 @Andy - 非常感谢所有珍贵的提示。 ExecuteStreamCommand - remove 1st line from the flow

2 个答案:

答案 0 :(得分:7)

米甲

我想确保我正确理解你的问题,因为我认为有更好的解决方案。

<强>问题:

你有一个1GB的TSV加载到NiFi,你想删除第一行。

<强>解决方案:

如果您的文件较小,最好的解决方案是使用具有以下处理器属性的ReplaceText处理器:

  • 搜索值^.*\n
  • 替换值&lt; - 空字符串

这将剥离第一行,而不必将1GB内容从NiFi发送到命令行,然后重新获取结果。不幸的是,要使用正则表达式,您需要设置最大缓冲区大小,这意味着需要将全部内容读入堆内存以执行此操作。

使用1GB文件,如果您知道第一行的确切值,则应尝试使用ModifyBytes,它允许您从流文件内容的开头和/或结尾修剪字节数。然后,您可以简单地指示处理器删除内容的第一个 n 字节。由于NiFi的写时复制内容存储库,您仍然会有大约2GB的数据,但它使用8192B缓冲区大小以流方式进行。

我最好的建议是使用ExecuteScript处理器。该处理器允许您使用各种语言(Groovy,Python,Ruby,Lua,JS)编写自定义代码,并使其在流文件上执行。使用如下所示的Groovy脚本,您可以删除第一行并以流方式复制其余部分,这样就不会对堆产生不必要的负担。

我用1MB文件测试了这个,每个流文件花了大约1.06秒(MacBook Pro 2015,16 GB RAM,OS X 10.11.6)。在更好的机器上,您显然可以获得更好的吞吐量,并且您可以将其扩展到更大的文件。

def flowfile = session.get()
if (!flowfile) return

try {
    // Here we are reading from the current flowfile content and writing to the new content
    flowfile = session.write(flowfile, { inputStream, outputStream ->
        def bufferedReader = new BufferedReader(new InputStreamReader(inputStream))

        // Ignoring the first line
        def ignoredFirstLine = bufferedReader.readLine()

        def bufferedWriter = new BufferedWriter(new OutputStreamWriter(outputStream))
        def line

        int i = 0

        // While the incoming line is not empty, write it to the outputStream
        while ((line = bufferedReader.readLine()) != null) {
            bufferedWriter.write(line)
            bufferedWriter.newLine()
            i++
        }

        // By default, INFO doesn't show in the logs and WARN will appear in the processor bulletins
        log.warn("Wrote ${i} lines to output")

        bufferedReader.close()
        bufferedWriter.close()
    } as StreamCallback)

    session.transfer(flowfile, REL_SUCCESS)
} catch (Exception e) {
    log.error(e)
    session.transfer(flowfile, REL_FAILURE)
}

一方面注意,一般来说,NiFi的一个好习惯是在可能的情况下将巨型文本文件拆分为较小的组件流文件(使用SplitText之类的东西)以获得并行处理的好处。如果1GB输入是视频,这将不适用,但正如您提到的TSV,我认为可能将初始流文件拆分为较小的部分并并行处理它们(甚至发送到集群中的其他节点)负载平衡)可以帮助您在这里的表现。

修改

我意识到我没有回答你原来的问题 - 如何将流文件的内容放入ExecuteStreamCommand处理器命令行调用中。如果您想对属性的值进行操作,则可以在 Arguments 字段中使用Expression Language语法${attribute_name}引用属性值。但是,由于内容不能从EL引用,并且您不希望通过将1GB内容移动到属性来销毁堆,因此最好的解决方案是使用{{1}将内容写入文件},对提供的文件名运行PutFile命令并将其写入另一个文件,然后使用sed将这些内容读回NiFi中的流文件。

编辑2:

这是一个template,它演示了对GetFileExecuteStreamCommand使用rev对流文件内容并将输出放入新流文件的内容中。您可以运行流并监视sed以查看输出或使用数据来源查询来检查每个处理器执行的修改。

ExecuteStreamCommand Example

ExecuteStreamCommand Configuration

答案 1 :(得分:1)

由于您要从文件中删除标题,所以我认为使用StripHeader处理器会更好。

ANKIT