如何将JavaRDD写入marklogic数据库

时间:2016-12-29 08:43:47

标签: apache-spark marklogic marklogic-8

我正在用marklogic数据库评估spark。我已经阅读了一个csv文件,现在我有一个JavaRDD对象,我必须将其转储到marklogic数据库。

    SparkConf conf = new SparkConf().setAppName("org.sparkexample.Dataload").setMaster("local");
    JavaSparkContext sc = new JavaSparkContext(conf);

    JavaRDD<String> data = sc.textFile("/root/ml/workArea/data.csv");
    SQLContext sqlContext = new SQLContext(sc);
    JavaRDD<Record> rdd_records = data.map(
      new Function<String, Record>() {
          public Record call(String line) throws Exception {
             String[] fields = line.split(",");
             Record sd = new Record(fields[0], fields[1], fields[2], fields[3],fields[4]);
             return sd;
          }
    });

这个JavaRDD对象我想写入marklogic数据库。

是否有任何spark api可以更快地写入marklogic数据库?

让我们说,如果我们不能直接将JavaRDD写入marklogic,那么实现这一目标的当前方法是什么?

以下是我用来将JavaRDD数据写入marklogic数据库的代码,让我知道这是否是错误的方法。

final DatabaseClient client = DatabaseClientFactory.newClient("localhost",8070, "MLTest");
    final XMLDocumentManager docMgr = client.newXMLDocumentManager();   
    rdd_records.foreachPartition(new VoidFunction<Iterator<Record>>() {
        public void call(Iterator<Record> partitionOfRecords) {
            while (partitionOfRecords.hasNext()) {
                Record record = partitionOfRecords.next();
                System.out.println("partitionOfRecords - "+record.toString());
                String docId = "/example/"+record.getID()+".xml";
                JAXBContext context = JAXBContext.newInstance(Record.class);
                JAXBHandle<Record> handle = new JAXBHandle<Record>(context);
                handle.set(record);
                docMgr.writeAs(docId, handle);
            }
      }
    });
    client.release();

我已经使用java客户端api来编写数据,但即使POJO类记录正在实现Serializable接口,我也会遇到异常。请让我知道可能是什么原因&amp;如何解决这个问题。

  

org.apache.spark.sparkexception任务不可序列化。

3 个答案:

答案 0 :(得分:1)

将数据导入MarkLogic的最简单方法是通过HTTP和客户端REST API - 特别是/ v1 / documents端点 - http://docs.marklogic.com/REST/client/management

有多种方法可以优化它,例如通过写集,但根据你的问题,我认为首先要决定的是 - 你想为每个记录写什么样的文档?您的示例显示CSV中的5列 - 通常,您将编写具有5个字段/元素的JSON或XML文档,每个字段/元素都基于列索引进行命名。因此,您需要编写一些代码来生成JSON / XML,然后使用您喜欢的任何HTTP客户端(以及一个选项是MarkLogic Java Client API)将该文档写入MarkLogic。

这解决了如何将JavaRDD写入MarkLogic的问题 - 但如果您的目标是尽快将CSV中的数据传输到MarkLogic,那么请跳过Spark并使用mlcp - https://docs.marklogic.com/guide/mlcp/import#id_70366 - 这涉及零编码。

答案 1 :(得分:0)

来自spark streaming guide的修改示例,在这里您必须实现特定于数据库的连接和编写逻辑。

public void send(JavaRDD<String> rdd) {
    rdd.foreachPartition(new VoidFunction<Iterator<String>>() {
      @Override
      public void call(Iterator<String> partitionOfRecords) {
        // ConnectionPool is a static, lazily initialized pool of
        Connection connection = ConnectionPool.getConnection();
        while (partitionOfRecords.hasNext()) {
          connection.send(partitionOfRecords.next());
        }
        ConnectionPool.returnConnection(connection); // return to the pool
        // for future reuse
      }
    });
  }

答案 2 :(得分:0)

我想知道你是否只需要确保你在VoidFunction内访问的所有内容都是可序列化的(参见this page)。 DatabaseClient和XMLDocumentManager当然不是可序列化的,因为它们是连接的资源。但是,你没有在你的VoidFunction中实例化DatabaseClient,因为效率会降低(尽管它会起作用)。我不知道以下想法是否适合火花。但我猜你可以创建一个保持单个DatabaseClient实例的类:

public static class MLClient {
  private static DatabaseClient singleton;
  private MLClient() {}

  public static DatabaseClient get(DatabaseClientFactory.Bean connectionInfo) {
    if ( connectionInfo == null ) {
      throw new IllegalArgumentException("connectionInfo cannot be null");
    }
    if ( singleton == null ) {
      singleton = connectionInfo.newClient();
    }
    return singleton;
  }
}

然后你只需在你的VoidFunction之外创建一个可序列化的DatabaseClientFactory.Bean,这样你的身份验证信息仍然是集中的

DatabaseClientFactory.Bean connectionInfo = 
  new DatabaseClientFactory.Bean();
connectionInfo.setHost("localhost");
connectionInfo.setPort(8000);
connectionInfo.setUser("admin");
connectionInfo.setPassword("admin");
connectionInfo.setAuthenticationValue("digest");

然后在你的VoidFunction中你可以得到那个单例DatabaseClient和新的XMLDocumentManager,如下所示:

DatabaseClient client = MLClient.get(connectionInfo);
XMLDocumentManager docMgr = client.newXMLDocumentManager();