如何在NiFi上两阶段拆分大型Json文件

时间:2019-10-23 12:34:27

标签: apache-nifi

我正在使用NiFi进行恢复并将很多数据存储到Kafka。我实际上处于测试阶段,并且正在使用大型Json文件。

我的Json文件记录了500K录音。

实际上,我有一个用于获取文件的处理器getFile和一个SplitJson

JsonPath表达式:$..posts.*

此配置适用于可记录50K记录的小文件,但对于大文件,它会崩溃。

我的Json文件看起来像这样,"posts":[]中有50万个寄存器

{ 
    "meta":{ 
        "requestid":"request1000",
        "http_code":200,
        "network":"twitter",
        "query_type":"realtime",
        "limit":10,
        "page":0
    },
    "posts":[ 
        { 
            "network":"twitter",
            "posted":"posted1",
            "postid":"id1",
            "text":"text1",
            "lang":"lang1",
            "type":"type1",
            "sentiment":"sentiment1",
            "url":"url1"
        },
        { 
            "network":"twitter",
            "posted":"posted2",
            "postid":"id2",
            "text":"text2",
            "lang":"lang2",
            "type":"type2",
            "sentiment":"sentiment2",
            "url":"url2"
        }
    ]
}

我阅读了一些有关此问题的文档,但是,主题是文本文件,并且演讲者建议链接许多SplitText以便逐步拆分文件。像我的Json这样的僵化结构,我不知道该怎么做。

我正在寻找一种解决方案,她可以很好地完成500K录音工作。

3 个答案:

答案 0 :(得分:1)

尝试在NiFi中使用SplitRecord处理器。

在SplitRecord处理器中定义记录Reader/Writer controller服务。

然后将Records Per Split配置为1,并使用Splits关系进行进一步处理。

(OR)

如果您想展平并整理记录,请在NiFi中使用ForkRecord处理器。

此链接的用法refer

答案 1 :(得分:1)

不幸的是,我认为这种情况(记录中的大数组)目前无法很好地处理...

SplitJson要求将整个流文件读取到内存中,并且也没有传出的拆分大小。所以这行不通。

SplitRecord通常是正确的解决方案,但是当前有两个JSON记录读取器-JsonTreeReader和JsonPathReader。这两个流记录,但是这里的问题是只有一个巨大的记录,因此它们每个都将整个文档读入内存。

已经针对此特定问题进行了一些努力,但不幸的是,没有一个人将其发布。

此PR(现已关闭)添加了一个新的JSON记录读取器,该读取器可以从JSON路径开始传输记录,在您的情况下,该路径可能是$ .posts:

https://github.com/apache/nifi/pull/3222

使用该读取器,您甚至都不需要拆分,您只需将流文件发送到PublishKafkaRecord_2_0(或PublishKafkaRecord的任何适当版本),它就会读取每个记录并将其发布到Kafka。

对于新的SelectJson处理器,还有一个开放的PR,看起来可能会有所帮助:

https://github.com/apache/nifi/pull/3455

答案 2 :(得分:0)

我在使用json时遇到了同样的问题,并曾经编写流解析器

ExeuteGroovyScript处理器与 以下代码。

它应该将较大的传入文件拆分为较小的文件:

@Grab(group='acme.groovy', module='acmejson', version='20191029')
import groovyx.acme.json.AcmeJsonParser
import groovyx.acme.json.AcmeJsonOutput

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

def objMeta=null
def count=0


ff.read().withReader("UTF-8"){reader->
    new AcmeJsonParser().withFilter{
        onValue('$.meta'){ 
            //just remember it to use later
            objMeta=it 
        }
        onValue('$.posts.[*]'){objPost->
            def ffOut = ff.clone(false) //clone without content
            ffOut.post_index=count      //add attribite with index
            //write small json
            ffOut.write("UTF-8"){writer->
                AcmeJsonOutput.writeJson([meta:objMeta, post:objPost], writer, true)
            }
            REL_SUCCESS << ffOut        //transfer to success
            count++
        }
    }.parse(reader)
}
ff.remove()

输出文件示例:

{
  "meta": {
    "requestid": "request1000",
    "http_code": 200,
    "network": "twitter",
    "query_type": "realtime",
    "limit": 10,
    "page": 0
  },
  "post": {
    "network": "twitter",
    "posted": "posted11",
    "postid": "id11",
    "text": "text11",
    "lang": "lang11",
    "type": "type11",
    "sentiment": "sentiment11",
    "url": "url11"
  }
}