Scala + SBT - Как настроить reference.conf для затененной библиотеки Akka

TL;DR

Я пытаюсь затенить версию akka библиотека и связать его с моим приложением (чтобы иметь возможность запустить spray-can сервер на CDH 5.7 версия Spark 1.6). Процесс затенения портится akka по умолчанию, и после предоставления вручную отдельной версии akka "s reference.conf для затененных akka Похоже, что 2 версии как-то перепутаны.

Затенение akka версии, как известно, вызывают проблемы? Что я делаю неправильно?

Фон

у меня есть Scala / Spark приложение в настоящее время работает на Spark 1.6.1 автономный. Приложение запускает spray-can использование http-сервера spray 1.3.3, что требует akka 2.3.9 (Spark 1.6.1 автономный включает в себя совместимый akka 2.3.11).

Я пытаюсь перенести приложение на новый Cloudera -основан Spark кластер работает CDH 5.7 версия Spark 1.6, Проблема в том, что Spark 1.6 в CDH 5.7 в комплекте с akka 2.2.3 что недостаточно для spray 1.3.3 функционировать должным образом.

Попытка решения

Следуя предложению в этом посте, я решил затенить akka 2.3.9 и связать его вместе с моим приложением. Хотя на этот раз я наткнулся на новую проблему - akka имеет свою конфигурацию по умолчанию, определенную в reference.conf файл, который должен быть расположен на пути к классам приложения. Из-за известной проблемы в sbt-assembly Затенение, похоже, что затенение akka Библиотека потребует отдельной конфигурации.

Итак, я в конечном итоге затенение akka со следующим правилом тени:

ShadeRule.rename("akka.**" -> "akka_2_3_9_shade.@1")
    .inLibrary("com.typesafe.akka" % "akka-actor_2.10" % "2.3.9")
    .inAll

и в том числе дополнительный reference.conf файл в моем проекте, который идентичен akka оригинал reference.conf, но со всеми вхождениями "akka" заменены на "akka_2_3_9_shade".

Теперь, похоже, что Spark -предоставлена akka как-то путается с затененным akka, так как я получаю следующую ошибку:

Exception in thread "main" java.lang.IllegalArgumentException: Cannot instantiate MailboxType [akka.dispatch.UnboundedMailbox], defined in [akka.actor.default-mailbox], make sure it has a public constructor with [akka.actor.ActorSystem.Settings, com.typesafe.config.Config] parameters
    at akka_2_3_9_shade.dispatch.Mailboxes$$anonfun$1.applyOrElse(Mailboxes.scala:197)
    at akka_2_3_9_shade.dispatch.Mailboxes$$anonfun$1.applyOrElse(Mailboxes.scala:195)
    at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:33)
    at scala.util.Failure$$anonfun$recover$1.apply(Try.scala:185)
    at scala.util.Try$.apply(Try.scala:161)
    at scala.util.Failure.recover(Try.scala:185)
    at akka_2_3_9_shade.dispatch.Mailboxes.lookupConfiguration(Mailboxes.scala:195)
    at akka_2_3_9_shade.dispatch.Mailboxes.lookup(Mailboxes.scala:78)
    at akka_2_3_9_shade.actor.LocalActorRefProvider.akka$actor$LocalActorRefProvider$$defaultMailbox$lzycompute(ActorRefProvider.scala:561)
    at akka_2_3_9_shade.actor.LocalActorRefProvider.akka$actor$LocalActorRefProvider$$defaultMailbox(ActorRefProvider.scala:561)
    at akka_2_3_9_shade.actor.LocalActorRefProvider$$anon$1.<init>(ActorRefProvider.scala:568)
    at akka_2_3_9_shade.actor.LocalActorRefProvider.rootGuardian$lzycompute(ActorRefProvider.scala:564)
    at akka_2_3_9_shade.actor.LocalActorRefProvider.rootGuardian(ActorRefProvider.scala:563)
    at akka_2_3_9_shade.actor.LocalActorRefProvider.init(ActorRefProvider.scala:618)
    at akka_2_3_9_shade.actor.ActorSystemImpl.liftedTree2$1(ActorSystem.scala:619)
    at akka_2_3_9_shade.actor.ActorSystemImpl._start$lzycompute(ActorSystem.scala:616)
    at akka_2_3_9_shade.actor.ActorSystemImpl._start(ActorSystem.scala:616)
    at akka_2_3_9_shade.actor.ActorSystemImpl.start(ActorSystem.scala:633)
    at akka_2_3_9_shade.actor.ActorSystem$.apply(ActorSystem.scala:142)
    at akka_2_3_9_shade.actor.ActorSystem$.apply(ActorSystem.scala:109)
    at akka_2_3_9_shade.actor.ActorSystem$.apply(ActorSystem.scala:100)
    at MyApp.api.Boot$delayedInit$body.apply(Boot.scala:45)
    at scala.Function0$class.apply$mcV$sp(Function0.scala:40)
    at scala.runtime.AbstractFunction0.apply$mcV$sp(AbstractFunction0.scala:12)
    at scala.App$$anonfun$main$1.apply(App.scala:71)
    at scala.App$$anonfun$main$1.apply(App.scala:71)
    at scala.collection.immutable.List.foreach(List.scala:318)
    at scala.collection.generic.TraversableForwarder$class.foreach(TraversableForwarder.scala:32)
    at scala.App$class.main(App.scala:71)
    at MyApp.api.Boot$.main(Boot.scala:28)
    at MyApp.api.Boot.main(Boot.scala)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at org.apache.spark.deploy.SparkSubmit$.org$apache$spark$deploy$SparkSubmit$$runMain(SparkSubmit.scala:731)
    at org.apache.spark.deploy.SparkSubmit$.doRunMain$1(SparkSubmit.scala:181)
    at org.apache.spark.deploy.SparkSubmit$.submit(SparkSubmit.scala:206)
    at org.apache.spark.deploy.SparkSubmit$.main(SparkSubmit.scala:121)
    at org.apache.spark.deploy.SparkSubmit.main(SparkSubmit.scala)
Caused by: java.lang.ClassCastException: interface akka_2_3_9_shade.dispatch.MailboxType is not assignable from class akka.dispatch.UnboundedMailbox
    at akka_2_3_9_shade.actor.ReflectiveDynamicAccess$$anonfun$getClassFor$1.apply(DynamicAccess.scala:69)
    at akka_2_3_9_shade.actor.ReflectiveDynamicAccess$$anonfun$getClassFor$1.apply(DynamicAccess.scala:66)
    at scala.util.Try$.apply(Try.scala:161)
    at akka_2_3_9_shade.actor.ReflectiveDynamicAccess.getClassFor(DynamicAccess.scala:66)
    at akka_2_3_9_shade.actor.ReflectiveDynamicAccess.CreateInstanceFor(DynamicAccess.scala:84)
    ... 34 more

Соответствующий код из моего приложения Boot.scala Файл следующий:

[45]    implicit val system = ActorSystem()
...
[48]    val service = system.actorOf(Props[MyAppApiActor], "MyApp.Api")
...
[52]    val port = config.getInt("MyApp.server.port")
[53]    IO(Http) ? Http.Bind(service, interface = "0.0.0.0", port = port)

3 ответа

Решение

ОК, так что в конце концов мне удалось решить это.

Оказывается akka загружает (некоторые из) параметров конфигурации из файла конфигурации, используя ключи, которые определены как строковые литералы. Вы можете найти много из них в akka/actor/ActorSystem.scala, например.

И кажется, что sbt-assembly не изменяет ссылки на затененную библиотеку / имя пакета в строковых литералах.

Кроме того, некоторые ключи конфигурации меняются sbt-assembly Затенение Я действительно не нашел время, чтобы найти, где и как именно они определены в akka источник, но следующее исключение, которое выбрасывается во время ActorSystem Код инициализации доказывает, что это действительно так:

ConfigException$Missing: No configuration setting found for key 'akka_2_3_9_shade'

Таким образом, решение это включить пользовательский файл конфигурации (назовите его, например, akka_spray_shade.conf) и скопируйте в него следующие разделы конфигурации:

  • Содержание akka оригинал reference.conf но имея akka префикс в значениях конфигурации изменен на akka_2_3_9_shade, (это требуется для жестко закодированных строковых литеральных конфигурационных ключей)
  • Содержание akka оригинал reference.conf но имея akka префикс в значениях конфигурации изменен на akka_2_3_9_shade и имея корневой конфигурационный ключ изменен с akka в akka_2_3_9_shade, (это требуется для ключей конфигурации, которые изменяются sbt-assembly)
  • Содержание spray оригинал reference.conf но имея akka префикс в значениях конфигурации изменен на akka_2_3_9_shade, (это необходимо, чтобы убедиться, что spray всегда относится к тени akka)

Теперь этот пользовательский файл конфигурации должен быть предоставлен явно во время инициализации ActorSystem в приложении Boot.scala код:

val akkaShadeConfig = ConfigFactory.load("akka_spray_shade")
implicit val system = ActorSystem("custom-actor-system-name", akkaShadeConfig)

Небольшое дополнение к принятому ответу.

Нет необходимости помещать эту конфигурацию в файл с произвольным именем, например akka_spray_shade.conf, Конфигурация может быть помещена в application.conf который загружается по умолчанию во время ActorSystem создание, когда пользовательская конфигурация не указана явно: ActorSystem("custom-actor-system-name") эффективно означает ActorSystem("custom-actor-system-name", ConfigFactory.load("application")),

Я тоже долго боролся с этим. Оказывается, стратегия слияния по умолчанию в sbt-assembly исключает все reference.confфайлы. Добавление этого в build.sbt решил это для меня:

      assemblyMergeStrategy in assembly := {
  case PathList("reference.conf") => MergeStrategy.concat
}
Другие вопросы по тегам