Предпочтение библиотеки Apache Spark 2.0

#java #maven #elasticsearch #apache-spark #classpath

#java #maven #elasticsearch #apache-spark #путь к классу

Вопрос:

Короче говоря: в Spark 2.0 spark-submit предпочитает свою версию библиотеки Guava (14.0.1), но я хочу использовать последнюю версию jar (19.0).

Вопрос: Как убедить Spark использовать версию, указанную в моем pom.xml файле?

Мое подозрение: я могу использовать spark.driver.userClassPathFirst=true option. Но это экспериментальная функция (Spark 2.0.0 doc) — так что, может быть, есть лучшее решение?


Подробное объяснение проблемы:

Я использую Spark 2.0.0 (hadoop2.7) и Elasticsearch 2.3.4. И я борюсь с очень простым приложением, которое пытается использовать потоковую передачу Spark и Elasticsearch вместе. Вот оно:

 SparkConf sparkConf = new SparkConf().setAppName("SampleApp");
JavaStreamingContext jssc = new JavaStreamingContext(sparkConf, Durations.milliseconds(500));
jssc.checkpoint("/tmp");
JavaDStream<String> messages = jssc.textFileStream("/some_directory_path");

TransportClient client = TransportClient.builder().build()
    .addTransportAddress(new InetSocketTransportAddress(InetAddress.getByName("localhost"), 9300));

messages.foreachRDD(rdd -> {
    XContentBuilder builder = jsonBuilder()
            .startObject()
            .field("words", "some words")
            .endObject();

    clientTu.prepareIndex("indexName", "typeName")
        .setSource(builder.string())
        .get();
});

jssc.start();
jssc.awaitTermination();
  

Проект построен с использованием Maven. Вот часть pom.xml

 <dependencies>
    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-core_2.11</artifactId>
        <version>2.0.0</version>
        <scope>provided</scope>
        <exclusions>
            <exclusion>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <dependency>
        <groupId>org.apache.spark</groupId>
        <artifactId>spark-streaming_2.11</artifactId>
        <version>2.0.0</version>
        <scope>provided</scope>
        <exclusions>
            <exclusion>
                <groupId>org.apache.spark</groupId>
                <artifactId>spark-core_2.11</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <dependency>
        <groupId>org.elasticsearch</groupId>
        <artifactId>elasticsearch</artifactId>
        <version>2.3.4</version>
        <exclusions>
            <exclusion>
                <groupId>com.google.guava</groupId>
                <artifactId>guava</artifactId>
            </exclusion>
        </exclusions>
    </dependency>

    <dependency>
        <groupId>org.apache.logging.log4j</groupId>
        <artifactId>log4j-core</artifactId>
        <version>2.6.2</version>
    </dependency>

    <dependency>
        <groupId>com.google.guava</groupId>
        <artifactId>guava</artifactId>
        <version>19.0</version>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>2.4.3</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>shade</goal>
                    </goals>
                    <configuration>
                        <createSourcesJar>true</createSourcesJar>
                        <transformers>
                            <transformer
                                implementation="org.apache.maven.plugins.shade.resource.ServicesResourceTransformer" />
                            <transformer
                                implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
                                <mainClass>com.abc.App</mainClass>
                            </transformer>
                        </transformers>
                    </configuration>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>
  

Как вы можете видеть, я сделал несколько исключений.
И все кажется отличным, но после выполнения с помощью command:

 spark-submit --class com.abc.App --master local[2]  /somePath/superApp-0.0.1-SNAPSHOT.jar
  

Я получаю исключение:

 Exception in thread "main" java.lang.NoSuchMethodError: com.google.common.util.concurrent.MoreExecutors.directExecutor()Ljava/util/concurrent/Executor;
    at org.elasticsearch.threadpool.ThreadPool.<clinit>(ThreadPool.java:190)
    at org.elasticsearch.client.transport.TransportClient$Builder.build(TransportClient.java:131)
    at com.abc.App.main(App.java:44)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.spark.deploy.SparkSubmit$.org$apache$spark$deploy$SparkSubmit$$runMain(SparkSubmit.scala:729)
    at org.apache.spark.deploy.SparkSubmit$.doRunMain$1(SparkSubmit.scala:185)
    at org.apache.spark.deploy.SparkSubmit$.submit(SparkSubmit.scala:210)
    at org.apache.spark.deploy.SparkSubmit$.main(SparkSubmit.scala:124)
    at org.apache.spark.deploy.SparkSubmit.main(SparkSubmit.scala)
  

После добавления --conf spark.driver.userClassPathFirst=true в spark-submit command приложение, похоже, работает правильно. Но я не уверен, что это правильный способ справиться с этой проблемой, потому что этот параметр отмечен в документации как экспериментальный.

Другими словами, Spark предпочитает библиотеки из своей среды выполнения и игнорирует библиотеки, предоставленные в «uber» jar (собранный jar). Итак, я хочу знать, как правильно изменить это поведение?

Еще раз вопрос: что я должен сделать, чтобы быть уверенным, что конкретный jar, определенный в моем POM, будет использоваться во время выполнения (с определенной версией)?


Редактировать: в более сложном приложении, которое пытается использовать как потоковую передачу Spark, так и Elasticsearch, возникают те же проблемы с другими библиотеками (например io.netty:netty ). И в такой ситуации простая spark.driver.userClassPathFirst активация опции вообще не помогает.

Комментарии:

1. Можете ли вы проверить, какой guava pom на самом деле был включен в ваш jar? jar xf отобразит его содержимое. Кроме того, кажется, что именно эластичный поиск, а не spark, не может найти guava в пути к классу. Guava должна быть доступна в пути к классу или в комплекте с файлом jar.

2. @Phil Спасибо за комментарий. В jar есть /META-INF/maven/com.google.guava/guava/pom.properties файл, в котором есть информация: version=19.0 . Таким образом, версия в jar верна. Существует проблема во время выполнения — похоже spark-submit , игнорирует guava19 и берет его более старую версию из локального репозитория jar, расположенного внутри ./spark_2.0/jars/ . Есть идеи, как настроить Spark, чтобы исправить это?

3. Если ./spark_2.0/jars/ это локальный каталог, возможно, удаление jar из этого каталога будет работать. Кроме того, вы можете добавить директиву exclude в свою конфигурацию maven, чтобы предотвратить использование старой зависимости guava.