如何从Kafka Producer返回具有数十亿记录的ArrayList?

时间:2018-12-10 10:32:55

标签: java apache-kafka kafka-producer-api apache-kafka-connect

我已经准备好一个卡夫卡制片人,将List放入kafka主题中。它适用于100万行/记录。我得到的生产文件包含1.1亿多条记录。 在我的KafkaProducer中处理海量数据的最佳方法是什么?

下面是代码,我曾经处理过1百万条记录,将其放入kafka主题大约需要4分钟。

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.apache.kafka.connect.data.Schema;
import org.apache.kafka.connect.data.SchemaBuilder;
import org.apache.kafka.connect.data.Struct;
import org.apache.kafka.connect.source.SourceRecord;
import org.apache.kafka.connect.source.SourceTask;

public class KafkaSourceTask extends SourceTask {

    private String filename;

    private String topic;

    private RandomAccessFile raf;

    private long lastRecordedOffset = 0L;

    private BufferedReader bufferedReader = null;

    Schema schema = SchemaBuilder.struct().field("emp_id", 
            Schema.STRING_SCHEMA).field("name", Schema.STRING_SCHEMA)
            .field("last_name", Schema.STRING_SCHEMA).field("department", 
            Schema.STRING_SCHEMA).build();

public void start(Map<String, String> props) {
    filename = props.get("file");
    topic = props.get("topic");

}

@Override
public List<SourceRecord> poll() throws InterruptedException {
    double startTime = System.nanoTime();
    try {
        bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(new File(filename)),
                StandardCharsets.UTF_8));
        raf = new RandomAccessFile(filename, "r");
        long filePointer = raf.getFilePointer();
        System.out.println(filePointer + " - " + lastRecordedOffset);
        if (bufferedReader.ready() && (filePointer > lastRecordedOffset || filePointer == 0)) {
            raf.seek(lastRecordedOffset);

            ArrayList<SourceRecord> records = new ArrayList<>();
            String line;
            while ((line = raf.readLine()) != null) {
                records.add(new SourceRecord(null, null, topic, schema, buildRecordValue(line)));
            }
            lastRecordedOffset = raf.getFilePointer();
            raf.close();
            bufferedReader.close();

            double endTime = System.nanoTime();
            return records;
        }
    }
    catch (IOException e) {

        e.printStackTrace();
    }

    return null;
}

@Override
public synchronized void stop() {
    try {
        raf.close();
    }
    catch (IOException e) {
        e.printStackTrace();
    }
}

private Struct buildRecordValue(String line) {
    String[] values = line.split(",");
    Struct value = new Struct(schema).put("emp_id", values[0]).put("name", values[1]).put("last_name", values[2])
            .put("department", values[3]);
    return value;
}

@Override
public String version() {
    // TODO Auto-generated method stub
    return null;
}
}

对此有任何帮助或建议,我们将不胜感激。

2 个答案:

答案 0 :(得分:0)

具有数十亿条记录的ArrayList ?想想看,如果您有10亿个记录,而每个记录的大小只有1个字节(可笑的低估),那么您就有1 GB的内存消耗。

通过对“大数据”的粗略和现成的定义,由于数据无法容纳在单个主机上的内存中,因此您要么处于边缘状态,要么已过时,您需要开始使用大数据技术。首先,您可以尝试在多台计算机上使用多线程,然后在多台计算机上尝试使用多线程,这是使用Kafka(客户端API)的优势,无论是在消费还是生产时,都非常容易。

答案 1 :(得分:0)

首先,在将Kafka生产者批记录发送给代理之前,应检查并使用两个配置Model::Thing  和linger.ms

现在,您可以使用其他线程读取文件(我认为这是每行一个记录),并将其放入Java队列中,并使用托管kafka生产者的线程来连续读取此队列。

多个生产者被认为是一种反模式,尤其是在编写Kafka主题时,请查看“单一作者原则”。

无论哪种方式,您都必须对kafka生产者进行一些调整,但就像@ cricket_007所说的那样,您应该考虑将kafka connect与文件csv连接器一起使用,至少在找不到适合您的连接器的情况下,您可以自己开发连接器。

希望有帮助。