2015-10-23 3 views
10

Я хотел иметь возможность упаковать DataFrames в файл jar Scala и получить к ним доступ в R. Конечной целью является создание способа доступа к определенным и часто используемым таблицам базы данных в Python, R и Scala, не создавая для каждой другой библиотеки.Использование SparkR JVM для вызова методов из файла jar Scala

Чтобы сделать это, я сделал файл jar в Scala с функциями, которые используют библиотеку SparkSQL для запроса базы данных и получения требуемых DataFrames. Я хотел иметь возможность называть эти функции в R без создания другой JVM, поскольку Spark уже работает на JVM в R. Однако использование JVM Spark не отображается в SparkR API. Чтобы сделать его доступным и сделать Java-методы вызываемыми, я изменил «backend.R», «generics.R», «DataFrame.R» и «NAMESPACE» в пакете SparkR и перестроил пакет:

В «backend» .R»Я сделал„callJMethod“и„createJObject“формальные методы:

setMethod("callJMethod", signature(objId="jobj", methodName="character"), function(objId, methodName, ...) { 
    stopifnot(class(objId) == "jobj") 
    if (!isValidJobj(objId)) { 
    stop("Invalid jobj ", objId$id, 
     ". If SparkR was restarted, Spark operations need to be re-executed.") 
    } 
    invokeJava(isStatic = FALSE, objId$id, methodName, ...) 
}) 


    setMethod("newJObject", signature(className="character"), function(className, ...) { 
    invokeJava(isStatic = TRUE, className, methodName = "<init>", ...) 
}) 

Я изменил„generics.R“также содержат эти функции:

#' @rdname callJMethod 
#' @export 
setGeneric("callJMethod", function(objId, methodName, ...) { standardGeneric("callJMethod")}) 

#' @rdname newJobject 
#' @export 
setGeneric("newJObject", function(className, ...) {standardGeneric("newJObject")}) 

Затем я добавил экспорта для этих функций файл NAMESPACE:

export("cacheTable", 
    "clearCache", 
    "createDataFrame", 
    "createExternalTable", 
    "dropTempTable", 
    "jsonFile", 
    "loadDF", 
    "parquetFile", 
    "read.df", 
    "sql", 
    "table", 
    "tableNames", 
    "tables", 
    "uncacheTable", 
    "callJMethod", 
    "newJObject") 

Это позволило мне назвать функции Scala, которые я написал, не запустив новую JVM.

Методы scala, которые я написал, возвращают DataFrames, которые являются «jobj» s в R при возврате, но SparkR DataFrame - это среда + jobj. Чтобы превратить эти данные DataFrames в SparkR DataFrames, я использовал функцию dataFrame() в «DataFrame.R», которую я также сделал доступной, следуя приведенным выше шагам.

Тогда я смог получить доступ к DataFrame, который я «построил» в Scala из R, и использовать все функции SparkR на этом DataFrame. Мне было интересно, есть ли лучший способ сделать такую ​​кросс-язычную библиотеку, или если есть какая-то причина, что Spark JVM не должен быть общедоступным?

ответ

4

любая причина, по которой Spark JVM не следует публиковать?

Возможно, более одного. Разработчики Spark прилагают серьезные усилия для обеспечения стабильного публичного API. Низкие детали реализации, включая способ взаимодействия гостевых языков с JVM, просто не являются частью контракта. Он может быть полностью переписан в любой момент без какого-либо негативного воздействия на пользователей. Если вы решите использовать его, и есть обратные несовместимые изменения, вы сами по себе.

Сохранение внутренних компонентов снижает усилия по обслуживанию и поддержке программного обеспечения. Вы просто не беспокоитесь о всех возможных способах злоупотребления ими.

лучший способ сделать такую ​​библиотеку кросс-язык

Трудно сказать, не зная больше о вашем прецеденту. Я вижу по крайней мере три варианта:

  • Для начала R имеет только слабые механизмы контроля доступа. Если какая-либо часть API является внутренней, вы всегда можете использовать функцию ::: для доступа к ней.Как умные люди говорят:

    Это, как правило, дизайн ошибка использовать ::: в коде, так как соответствующий объект, вероятно, был сохранен внутренним для веских причин.

    но одно точно, это намного лучше, чем изменение источника искры. В качестве бонуса он четко отмечает части вашего кода, которые особенно хрупкие, потенциально нестабильные.

  • , если все, что вы хотите, чтобы создать DataFrames Проще всего использовать сырые SQL. Он чистый, портативный, не требует компиляции, упаковки и просто работает. Если у вас есть строка запроса, как показано ниже, сохраненный в переменной с именем q

    CREATE TEMPORARY TABLE foo 
    USING org.apache.spark.sql.jdbc 
    OPTIONS (
        url "jdbc:postgresql://localhost/test", 
        dbtable "public.foo", 
        driver "org.postgresql.Driver" 
    ) 
    

    он может быть использован в R:

    sql(sqlContext, q) 
    fooDF <- sql(sqlContext, "SELECT * FROM foo") 
    

    Python:

    sqlContext.sql(q) 
    fooDF = sqlContext.sql("SELECT * FROM foo") 
    

    Scala:

    sqlContext.sql(q) 
    val fooDF = sqlContext.sql("SELECT * FROM foo") 
    

    или непосредственно в Spark SQL.

  • , наконец, вы можете использовать Spark Data Sources API для обеспечения согласованного и поддерживаемого межплатформенного доступа.

Из этих трех я бы предпочел необработанный SQL, а затем API источников данных для сложных случаев и оставить внутренности в качестве крайней меры.

Редактировать(2016-08-04):

Если вы заинтересованы в низком уровне доступа к JVM есть относительно новый пакет rstudio/sparkapi, который предоставляет внутренний протокол SparkR RPC. Трудно предсказать, как он будет развиваться, поэтому используйте его на свой страх и риск.

+0

Но есть ли хороший способ обмена данными по R и Scala без необходимости хранить данные и повторно читать их обратно? Если мне не хватает чего-то решения 2 и 3, похоже, это требует. – shj

+0

Я не совсем уверен, чего вы хотите. Spark не поддерживает ни несколько контекстов на JVM (см. [SPARK-2243] (https://issues.apache.org/jira/browse/SPARK-2243)), ни обмен RDD между контекстами. Таким образом, все три требуют чтения данных из определенного типа хранилища. Там есть некоторые опции, такие как [spark-jobserver] (https://github.com/spark-jobserver/spark-jobserver), [«общие» RDD] (https://ignite.apache.org/features/igniterdd. html) поверх Ignite или используя [Tachyon] (http://tachyon-project.org/) как слой с памятью, который пытается решить эту проблему, но простая демонстрация JVM нигде не приведет вас. – zero323

+0

Для простых SQL-запросов вы, вероятно, можете использовать buit-in [Thrift server] (http://spark.apache.org/docs/latest/sql-programming-guide.html#running-the-thrift-jdbcodbc-server). – zero323

Смежные вопросы