You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Shaded Akka与Apache Toree版本冲突及配置文件问题排查

你的初始假设完全正确,reference.conf冲突正是问题根源

从错误日志来看,Toree自带的Akka 2.3.4中根本不存在akka.dispatch.BoundedControlAwareMessageQueueSemantics这个类——这个类是Akka 2.5.x版本才引入的。问题出在类路径上多个Akka的reference.conf被合并:当Toree启动时,它会加载自己的Akka 2.3.4配置,但你的uber-jar里的Akka 2.5.11的reference.conf也会被加载并合并进去,导致最终的配置里出现了只在高版本Akka中存在的mailbox配置项,而类路径里优先加载的是Toree的旧版本Akka类,自然找不到这个新类。


当然可以用SBT实现配置域分离,关键是结合shade重命名+配置路径修改

你可以通过sbt-assembly插件的shading功能,把你的项目依赖的Akka(包括Play WS间接依赖的部分)完全重命名到独立的命名空间,同时修改对应的reference.conf配置路径,让你的代码和Toree的Akka彻底隔离。具体步骤如下:

1. 配置SBT Assembly插件的Shade规则

在你的build.sbt中添加shade规则,将所有Akka相关的包重命名到自定义路径(比如my.shaded.akka):

// 先确保添加了sbt-assembly插件
addSbtPlugin("com.eed3si9n" % "sbt-assembly" % "0.15.0")

// 定义shade规则,重命名Akka及其相关依赖包
assemblyShadeRules in assembly := Seq(
  // 重命名核心Akka包
  ShadeRule.rename("akka.**" -> "my.shaded.akka.@1")
    .inAll,
  // 重命名Typesafe旗下的Akka相关模块(比如Play依赖的部分)
  ShadeRule.rename("com.typesafe.akka.**" -> "my.shaded.com.typesafe.akka.@1")
    .inAll
)

2. 处理reference.conf的合并与修改

Akka的配置是通过reference.conf加载的,我们需要合并所有配置文件,并将shaded后的Akka配置路径替换为新的命名空间:

assemblyMergeStrategy in assembly := {
  // 合并所有reference.conf,避免配置被覆盖
  case PathList("reference.conf") => MergeStrategy.concat
  // 其他文件使用默认合并策略
  case other =>
    val defaultStrategy = (assemblyMergeStrategy in assembly).value
    defaultStrategy(other)
}

// 额外添加一个任务,修改shaded后的reference.conf中的配置路径
assemblyOption in assembly := (assemblyOption in assembly).value.copy(
  preAppendShellScript = None,
  // 替换所有配置中的akka.前缀为my.shaded.akka.
  transformAssembly = { asm =>
    import java.io._
    val temp = File.createTempFile("shaded-", ".jar")
    val jar = new java.util.jar.JarFile(asm)
    val out = new java.util.jar.JarOutputStream(new FileOutputStream(temp))

    import scala.collection.JavaConverters._
    jar.entries().asScala.foreach { entry =>
      if (!entry.isDirectory) {
        out.putNextEntry(new java.util.jar.JarEntry(entry.getName))
        if (entry.getName == "reference.conf") {
          // 读取配置内容并替换路径
          val content = scala.io.Source.fromInputStream(jar.getInputStream(entry)).mkString
          val shadedContent = content.replaceAll("""^akka\.""", "my.shaded.akka.").replaceAll("""\bakka\.""", " my.shaded.akka.")
          out.write(shadedContent.getBytes)
        } else {
          // 其他文件直接复制
          val buffer = new Array[Byte](1024)
          var len = jar.getInputStream(entry).read(buffer)
          while (len != -1) {
            out.write(buffer, 0, len)
            len = jar.getInputStream(entry).read(buffer)
          }
        }
        out.closeEntry()
      }
    }
    jar.close()
    out.close()
    temp
  }
)

3. 调整代码中的Akka配置引用

如果你的代码直接创建了Akka的ActorSystem,需要指定使用shaded后的配置路径:

import my.shaded.akka.actor.ActorSystem
import my.shaded.akka.util.Timeout
import com.typesafe.config.ConfigFactory

// 加载shaded后的Akka配置
val shadedConfig = ConfigFactory.load().getConfig("my.shaded.akka")
val system = ActorSystem("my-shaded-system", shadedConfig)

如果是Play WS自动创建ActorSystem,需要在你的application.conf中配置它使用shaded的Akka:

play.ws.akka.actor-system = "my-shaded-ws-system"
play.ws.akka.config = "my.shaded.akka"

为什么这样能解决问题?

通过shade重命名,你的项目依赖的Akka类和配置都被移动到了独立的my.shaded.akka命名空间下,和Toree自带的akka命名空间完全隔离:

  • Toree会继续使用自己的Akka 2.3.4类和akka.*配置
  • 你的代码只会加载shaded后的Akka 2.5.11类和my.shaded.akka.*配置

这样就不会出现配置合并导致的类找不到问题了。

内容的提问来源于stack exchange,提问作者mongolol

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:29:24