处理大量记录会导致OutOfMemoryException-Kafka REST代理

时间:2019-04-03 08:40:48

标签: node.js apache-kafka kafka-producer-api kafka-rest

我正在使用融合的REST API代理致电Kafka。我正在读取一个CSV文件,从那里的所有记录(大约400万条记录)中创建一个对象,并将请求发送到REST代理。我不断遇到OutOfMemory异常。

确切的异常消息是:

Exception: java.lang.OutOfMemoryError thrown from the UncaughtExceptionHandler in thread "kafka-producer-network-thread | producer-81"

我只有一个REST代理服务器实例,作为Docker容器托管。环境变量设置为:

JAVA_OPTIONS=-Xmx1g

其他配置:

CPU - 1 Memory - 1024

它在崩溃之前处理大约1,00,000。 我尝试将其扩展到4个实例,同时将CPU增加到3个,并将内存增加到2046 mb。然后,它将处理约500万条记录。

在读取csv之后,我以5k记录的批次调用Kafka端点。那是用Node写的。这是节点代码

fs.createReadStream(inputFile)
  .pipe(parser({skip_lines_with_error: true}))
  .on('data', (records) => {
        country.push({ 'value' : {
            country: records[0],
            capital: records[1]
            }
        });

        if (country.length > 5000) {
            batch++;
            callKafkaProxy(country).then((rec) => {
                console.log(`'Batch done!'`);
            }).catch((reason) => {
                console.log(reason);
            });
            country = [];
        }
    })
    .on('end', () => {
        console.log('All done!');
    });
function callKafkaProxy(records) {
    const urlAndRequestOptions = {
        url: 'http://kafka-rest-proxy.com/topics/test-topic',
        headers: {
            'content-type' : 'application/vnd.kafka.json.v2+json',
            'Accept' : 'application/vnd.kafka.v2+json'
        }
    };
let recordsObject = {records: records};
//request here is a wrapper on the http package. 
return request.post(urlAndRequestOptions, recordsObject);

我觉得我缺少一些配置,这些配置可以帮助解决此问题而又不增加实例数> 1。

任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:1)

.on('data', () => {}); ... 

1。它不处理背压。创建可写流,它将处理您的批处理过程。然后只需使用管道即可。

inputStream
    .pipe(parser)
    .pipe(kafka)

然后分析这些行:

if (country.length > 5000) {
        batch++;
        callKafkaProxy(country).then((rec) => {
            console.log(`'Batch done!'`);
        ).catch((reason) => {
            console.log(reason);
        });
        country = [];
     }
  1. 您的callKafkaProxy是异步的,因此无论callKafkaProxy函数的结果如何,总是会填写您的国家/地区数组。国家/地区数组不断填充并不断提出要求。您可以通过批处理++之后的控制台日志来确保。您会看到您正在发起大量请求,而Kafka的响应速度要比发出请求的速度慢得多。

解决方案:

  1. 创建可写流。
  2. 将数据从解析器管道传输到它。 input.pipe(parser).pipe(yourJustCreatedKafkaWritableStream)
  3. 当您准备好接收其他记录时,让您的可写流将国家/地区推入数组并进行回调。当您达到优势时(如果country.length> 5000),则向kafka发出请求并等待响应,然后再进行回调。这样,您的流将是自适应的。您应该阅读有关节点流及其功能的更多信息。但是请记住,强大的功能伴随着巨大的责任,在这种情况下,您必须仔细设计代码,以免发生此类内存泄漏。

答案 1 :(得分:0)

借助Zilvinas的答案,我了解了如何利用流来批量发送数据。这是一个解决方案:

var stream = fs.createReadStream(file)
                        .pipe(es.split())
                        .pipe(es.mapSync(function (line) {

                            if (line.length) {
                                //read your line and create a record message
                            }

                            //put 5000 in a config constant
                            if (records.length === 5000) {
                                stream.pause();
                                logger.debug(`Got ${records.length} messages. Pushing to Kafka...`);
                                postChunkToKafka(records).then((response) => {     
                                  records = [];
                                  stream.resume();
                                });
                            }