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

TL;DR

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

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

Задний план

У меня есть приложение Scala/Spark, которое сейчас работает на Spark 1.6.1 автономно. Приложение запускает http-сервер spray-can с использованием 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, который идентичен исходному reference.conf akka, но со всеми вхождениями akka, замененными на akkaspray-can3akkashade.

Однако теперь кажется, что akka, предоставленный Spark, каким-то образом смешивается с затененным 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)

person Johan Hirsch    schedule 21.11.2016    source источник


Ответы (3)


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

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

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

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

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

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

  • Содержимое исходного reference.conf akka, но с префиксом akka в значениях конфигурации изменено на akka_2_3_9_shade. (это требуется для жестко запрограммированных ключей конфигурации строкового литерала)
  • Содержимое исходного reference.conf akka, но префикс akka в значениях конфигурации изменен на akka_2_3_9_shade и корневой ключ конфигурации изменен с akka на akka_2_3_9_shade. (это требуется для ключей конфигурации, которые изменяются sbt-assembly)
  • Содержимое исходного reference.conf spray, но с префиксом 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)
person Johan Hirsch    schedule 28.11.2016
comment
Этот хак работает, но удачи, если ключи akka изменятся или даже если вы введете свои собственные настройки akka. Правильным решением было бы программно затенить ключи конфигурации. sbt-assembly пока не предоставляет такой функциональности; возможно можно сделать самому через sbt. - person juanmirocks; 16.12.2017

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

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

person Igor Melnichenko    schedule 24.10.2017

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

assemblyMergeStrategy in assembly := {
  case PathList("reference.conf") => MergeStrategy.concat
}
person Tanvir    schedule 13.07.2021