Java 使用批处理数据设计独立于数据源的应用程序

Java 使用批处理数据设计独立于数据源的应用程序,java,mongodb,postgresql,design-patterns,spring-kafka,Java,Mongodb,Postgresql,Design Patterns,Spring Kafka,我们有一个遗留应用程序,它为每个用户从mongo读取数据。根据用户请求,查询结果从小到大,我们的应用程序为每个用户创建一个文件,并将其放入FTP服务器/s3。我们以mongo游标的形式读取数据,并在收到批数据后立即将每个批写入文件,这样文件写入性能就比较好了。这个应用程序工作得很好,但是绑定到mongo和mongo cursor 现在我们必须重新设计此应用程序,因为我们必须支持不同的数据源,即MongoDB、Postgres DB、Kinesis、S3等。到目前为止,我们已经想到了以下想法: 为

我们有一个遗留应用程序,它为每个用户从mongo读取数据。根据用户请求,查询结果从小到大,我们的应用程序为每个用户创建一个文件,并将其放入FTP服务器/s3。我们以mongo游标的形式读取数据,并在收到批数据后立即将每个批写入文件,这样文件写入性能就比较好了。这个应用程序工作得很好,但是绑定到mongo和mongo cursor

现在我们必须重新设计此应用程序,因为我们必须支持不同的数据源,即MongoDB、Postgres DB、Kinesis、S3等。到目前为止,我们已经想到了以下想法:

为每个源构建数据API并公开分页的REST响应。这是一个可行的解决方案,但对大型企业来说可能会很慢 将查询数据与当前游标响应进行比较。 通过在kafka中输入批处理数据并在我们的文件生成器中读取批处理数据流来构建一个数据抽象层。但大多数情况下,用户要求排序数据,因此我们需要按顺序读取消息。在写入文件之前,我们将失去大吞吐量和大量额外工作来组合这些数据消息的好处。
我们正在寻找一种解决方案来替换当前的mongo游标,并使我们的文件生成器独立于数据源。

因此,听起来您基本上希望创建一个API,在该API中,您可以尽可能保持流式处理的效率,就像您在读取用户数据时编写文件一样

在这种情况下,您可能需要为ReadSources定义一个推送解析器API,它将数据流传输到WriteTargets,WriteTargets将数据写入任何您有实现的对象。排序将在ReadSource端处理,因为对于某些源,您可以按顺序读取,例如从数据库读取;对于那些您无法执行此操作的源,您可以简单地执行一个中间步骤来对数据进行排序,例如写入临时表,然后将其流式传输到WriteTarget

一个基本的实现可能看起来像这样:

public class UserDataRecord {
    private String data1;
    private String data2;

    public String getRecordAsString() {
        return data1 + "," + data2;
    }
}
这只是总体思路,需要认真改进。
任何人都可以随时更新此API。

mongodb是如何获得数据的?它是持续更新的或静态的,并且已永久填充。据我所知,这是被查询的,因此希望被放入卡夫卡,类似于一些事件数据。我想知道是否有可能用kafka取代mongo。我们的mongo数据每天使用ETL批量更新几次。挑战是我们必须应用批次筛选和排序,因此如果我们将排序后的数据放入kafka,在检索时,我们需要确保接收到的数据按顺序排列。我们需要的不是流式处理,主要是将其批量写入文件。我们必须仅从postgres/mongo读取数据,因为我们的数据文件也用于增量数据和历史数据。如果我们只需要增量文件,kafka将非常适合。我将看看这些批是否可以直接写入卡夫卡。如果你在使用mongo做其他事情,他们也可以在那里写,如果不跳过mongo的话。同样,这只是一个建议,但对我来说,先给mongo写,然后再给kafka写,听起来有点尴尬。此外,这些数据可以根据查询要求划分为多个主题,相应的消费者可以直接从他们想要的主题中读取
public interface WriteTarget<Record> {
    /** Write a record to the target */
    public void writeRecord(Record record);

    /** Finish writing to the target and save everything */
    public void commit();

    /** Undo whatever was written */
    public void rollback();
}
public abstract class ReadSource<Record> {
    protected final WriteTarget<Record> writeTarget;

    public ReadSource(WriteTarget<Record> writeTarget) { this.writeTarget = writeTarget; }

    public abstract void read();
}
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
public class RelationalDatabaseReadSource extends ReadSource<UserDataRecord> {
    private Connection dbConnection;

    public RelationalDatabaseReadSource (WriteTarget<UserDataRecord> writeTarget, Connection dbConnection) {
        super(writeTarget);
        this.dbConnection = dbConnection;
    }

    @Override public void read() {
        // read user data from DB and encapsulate it in a record
        try (Statement statement = dbConnection.createStatement();
                ResultSet resultSet = statement.executeQuery("Select * From TABLE Order By COLUMNS");) {
            while (resultSet.next()) {
                UserDataRecord record = new UserDataRecord();
                // stream the records to the write target
                writeTarget.writeRecord(record);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
public class FileWriteTarget implements WriteTarget<UserDataRecord> {
    private File fileToWrite;
    private PrintWriter writer;

    public FileWriteTarget(File fileToWrite) throws IOException {
        this.fileToWrite = fileToWrite;
        this.writer = new PrintWriter(new FileWriter(fileToWrite));
    }

    @Override public void writeRecord(UserDataRecord record) {
        writer.println(record.getRecordAsString().getBytes(StandardCharsets.UTF_8));
    }

    @Override public void commit() {
        // write trailing records
        writer.close();
    }

    @Override
    public void rollback() {
        try { writer.close(); } catch (Exception e) { }
        fileToWrite.delete();
    }
}