Scala + SBT - 如何为着色的 Akka 库配置 reference.conf
Scala + SBT - How to configure reference.conf for a shaded Akka library
TL;DR
我正在尝试隐藏 akka
库的一个版本并将其与我的应用程序捆绑在一起(以便能够 运行 CDH 5.7
上的 spray-can
服务器Spark 1.6
的版本)。着色过程 messes up akka
's default configuration,并在为着色 akka
手动提供 akka
的 reference.conf
的单独版本后,看起来这两个版本仍然以某种方式混淆了。
是否已知阴影 akka
版本会导致问题?我做错了什么?
背景
我有一个 Scala
/Spark
应用程序目前 运行 正在 Spark 1.6.1
独立运行。应用程序 运行 是一个使用 spray 1.3.3
的 spray-can
http 服务器,它需要 akka 2.3.9
(Spark 1.6.1
独立版包括一个兼容的 akka 2.3.11
)。
我正在尝试将应用程序迁移到新的基于 Cloudera
的 Spark
集群 运行 宁 CDH 5.7
版本的 Spark 1.6
。问题是 CDH 5.7
中的 Spark 1.6
与 akka 2.2.3
捆绑在一起,这不足以使 spray 1.3.3
正常运行。
尝试的解决方案
根据 sbt-assembly
的着色功能中 this post, I decided to shade akka 2.3.9
and bundle it along with my application. Although this time I stumbled upon a new problem - akka
has it's default configuration defined in a reference.conf
file, which should be located on the application's classpath. Due to a known issue 的建议,着色 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.applyOrElse(Mailboxes.scala:197)
at akka_2_3_9_shade.dispatch.Mailboxes$$anonfun.applyOrElse(Mailboxes.scala:195)
at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:33)
at scala.util.Failure$$anonfun$recover.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.<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(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.apply(App.scala:71)
at scala.App$$anonfun$main.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(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.apply(DynamicAccess.scala:69)
at akka_2_3_9_shade.actor.ReflectiveDynamicAccess$$anonfun$getClassFor.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)
好的,所以最终我设法解决了这个问题。
原来 akka
使用定义为字符串文字的键从配置文件加载(部分)配置设置。例如,您可以在 akka/actor/ActorSystem.scala
中找到很多这样的内容。
并且 sbt-assembly
似乎没有 更改对字符串文字中带阴影的 library/package 名称的引用。
另外,一些配置键 被 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
所必需的)
现在,必须在应用程序 Boot.scala
代码 ActorSystem
的初始化期间 显式 提供此自定义配置文件:
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
}
TL;DR
我正在尝试隐藏 akka
库的一个版本并将其与我的应用程序捆绑在一起(以便能够 运行 CDH 5.7
上的 spray-can
服务器Spark 1.6
的版本)。着色过程 messes up akka
's default configuration,并在为着色 akka
手动提供 akka
的 reference.conf
的单独版本后,看起来这两个版本仍然以某种方式混淆了。
是否已知阴影 akka
版本会导致问题?我做错了什么?
背景
我有一个 Scala
/Spark
应用程序目前 运行 正在 Spark 1.6.1
独立运行。应用程序 运行 是一个使用 spray 1.3.3
的 spray-can
http 服务器,它需要 akka 2.3.9
(Spark 1.6.1
独立版包括一个兼容的 akka 2.3.11
)。
我正在尝试将应用程序迁移到新的基于 Cloudera
的 Spark
集群 运行 宁 CDH 5.7
版本的 Spark 1.6
。问题是 CDH 5.7
中的 Spark 1.6
与 akka 2.2.3
捆绑在一起,这不足以使 spray 1.3.3
正常运行。
尝试的解决方案
根据 sbt-assembly
的着色功能中 this post, I decided to shade akka 2.3.9
and bundle it along with my application. Although this time I stumbled upon a new problem - akka
has it's default configuration defined in a reference.conf
file, which should be located on the application's classpath. Due to a known issue 的建议,着色 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.applyOrElse(Mailboxes.scala:197)
at akka_2_3_9_shade.dispatch.Mailboxes$$anonfun.applyOrElse(Mailboxes.scala:195)
at scala.runtime.AbstractPartialFunction.apply(AbstractPartialFunction.scala:33)
at scala.util.Failure$$anonfun$recover.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.<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(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.apply(App.scala:71)
at scala.App$$anonfun$main.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(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.apply(DynamicAccess.scala:69)
at akka_2_3_9_shade.actor.ReflectiveDynamicAccess$$anonfun$getClassFor.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)
好的,所以最终我设法解决了这个问题。
原来 akka
使用定义为字符串文字的键从配置文件加载(部分)配置设置。例如,您可以在 akka/actor/ActorSystem.scala
中找到很多这样的内容。
并且 sbt-assembly
似乎没有 更改对字符串文字中带阴影的 library/package 名称的引用。
另外,一些配置键 被 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
所必需的)
现在,必须在应用程序 Boot.scala
代码 ActorSystem
的初始化期间 显式 提供此自定义配置文件:
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
}