【问题标题】:How to work efficiently with SBT, Spark and "provided" dependencies?如何有效地使用 SBT、Spark 和“提供的”依赖项?
【发布时间】:2016-07-26 01:40:59
【问题描述】:

我正在使用 Scala 构建一个 Apache Spark 应用程序,并且我正在使用 SBT 来构建它。事情是这样的:

  1. 当我在 IntelliJ IDEA 下开发时,我希望将 Spark 依赖项包含在类路径中(我正在启动一个带有主类的常规应用程序)
  2. 当我打包应用程序(感谢 sbt-assembly)插件时,我确实希望 Spark 依赖项包含在我的胖 JAR 中
  3. 当我通过 sbt test 运行单元测试时,我希望将 Spark 依赖项包含在类路径中(与 #1 相同,但来自 SBT)

为了匹配约束 #2,我将 Spark 依赖项声明为 provided

libraryDependencies ++= Seq(
  "org.apache.spark" %% "spark-streaming" % sparkVersion % "provided",
  ...
)

然后,sbt-assembly's documentation 建议添加以下行以包含单元测试的依赖项(约束 #3):

run in Compile <<= Defaults.runTask(fullClasspath in Compile, mainClass in (Compile, run), runner in (Compile, run))

这让我的约束 #1 没有被完全填充,即我无法在 IntelliJ IDEA 中运行应用程序,因为 Spark 依赖项没有被拾取。

使用 Maven,我使用特定的配置文件来构建 uber JAR。这样,我将 Spark 依赖项声明为主要配置文件(IDE 和单元测试)的常规依赖项,同时将它们声明为 provided 用于胖 JAR 打包。见https://github.com/aseigneurin/kafka-sandbox/blob/master/pom.xml

使用 SBT 实现这一目标的最佳方法是什么?

【问题讨论】:

  • 只是提醒一下,当使用spark-submit 时,spark 将使用 spark 安装路径中的库,而不是您打包到程序集 jar 中的库。除非你特别告诉它。配置名为 ``` sbt 和 Maven 都有汇编插件。创建程序集 jar 时,将 Spark 和 Hadoop 列为提供的依赖项;这些不需要捆绑,因为它们是由集群管理器在运行时提供的。一旦你有一个组装好的 jar,你可以在传递你的 jar 时调用 bin/spark-submit 脚本,如下所示。```

标签: intellij-idea apache-spark sbt sbt-assembly


【解决方案1】:

您不应该将 SBT 用于特定于 IDEA 的设置。 首先,如果该程序应该使用 spark-submit 运行,您如何在 IDEA 上运行它?我猜你会在 IDEA 中独立运行,同时通过 spark-submit 正常运行。 如果是这种情况,请使用 File|Project Structure|Libraries 在 IDEA 中手动添加 spark 库。您将看到 SBT 列出的所有依赖项,但您可以使用 +(加号)添加任意 jar/maven 工件。 这应该够了吧。

【讨论】:

    【解决方案2】:

    为什么不绕过 sbt 并手动将 spark-core 和 spark-streaming 作为库添加到您的模块依赖项中?

    • 打开“项目结构”对话框(例如 ⌘;)。
    • 在对话框的左侧窗格中,选择模块。
    • 在右侧窗格中,选择感兴趣的模块。
    • 在对话框右侧的“模块”页面上,选择“依赖项”选项卡。
    • 在“依赖项”选项卡上,单击“添加”并选择“库”。
    • 在“选择库”对话框中,从 maven 中选择新库
    • 找到火花核心。前org.apache.spark:spark-core_2.10:1.6.1
    • 利润

    https://www.jetbrains.com/help/idea/2016.1/configuring-module-dependencies-and-libraries.html?origin=old_help#add_existing_lib

    【讨论】:

      【解决方案3】:

      (用我从另一个频道得到的答案回答我自己的问题...)

      为了能够从 IntelliJ IDEA 运行 Spark 应用程序,您只需在 src/test/scala 目录中创建一个主类(test,而不是 main)。 IntelliJ 将获取 provided 依赖项。

      object Launch {
        def main(args: Array[String]) {
          Main.main(args)
        }
      }
      

      感谢Matthieu Blanc 指出这一点。

      【讨论】:

      • 您能否澄清一下,为什么会这样选择依赖项?我有同样的问题:尝试从 Idea 在本地运行 Spark 并使用 sbt 程序集打包它,目前我必须为后一种情况手动将“提供”添加到 sbt 构建文件中,否则 Idea 不会获取这些依赖项。
      • 你还在build.sbt中添加这一行吗? run in Compile &lt;&lt;= Defaults.runTask(fullClasspath in Compile, mainClass in (Compile, run), runner in (Compile, run))
      • 我不需要添加它来运行测试。
      • 如何告诉 sbt 也使用 provided 依赖项在普通电源中而不是 test 下的电源中,以解决不是在 IntelliJ 中而是在代码库本身固有的方式中的场景?我想知道 sbt 是否支持这一点,而 IntelliJ 又知道从 sbt 导入这种定义。
      • 如果对任何人有帮助,请从here 链接到这样做的一个建议。
      【解决方案4】:

      描述了基于创建另一个子项目以在本地运行项目的解决方案here

      基本上,您需要使用以下内容修改build.sbt 文件:

      lazy val sparkDependencies = Seq(
        "org.apache.spark" %% "spark-streaming" % sparkVersion
      )
      
      libraryDependencies ++= sparkDependencies.map(_ % "provided")
      
      lazy val localRunner = project.in(file("mainRunner")).dependsOn(RootProject(file("."))).settings(
         libraryDependencies ++= sparkDependencies.map(_ % "compile")
      )
      

      然后在运行配置下使用Use classpath of module: localRunner在本地运行新的子项目。

      【讨论】:

        【解决方案5】:

        你需要让 IntellJ 工作。

        这里的主要技巧是创建另一个依赖于主子项目的子项目,并将其所有提供的库都包含在编译范围内。为此,我在 build.sbt 中添加了以下几行:

        lazy val mainRunner = project.in(file("mainRunner")).dependsOn(RootProject(file("."))).settings(
          libraryDependencies ++= spark.map(_ % "compile")
        )
        

        现在我在 IDEA 中刷新项目并稍微更改之前的运行配置,因此它将使用新的 mainRunner 模块的类路径:

        对我来说完美无缺。

        来源:https://github.com/JetBrains/intellij-scala/wiki/%5BSBT%5D-How-to-use-provided-libraries-in-run-configurations

        【讨论】:

        • 我还必须将assembly := new File("") 添加到mainRunner 设置中,这样sbt assembly 命令就不会尝试在mainRunner 项目上运行。
        • 当你说“创建另一个子项目”是什么意思?我是否创建了一个包含 build.sbt 的子文件夹?如果我将该代码放在我的主 bu​​ild.sbt 中,我会得到 java.lang.IllegalArgumentException: requirement failed: Configurations already specified for module org.apache.spark:spark-core:2.1.1:provided 作为错误。
        • 解决方案只有在将scalaVersion设置为与build.sbt文件顶部声明的相同后才能正常工作
        【解决方案6】:

        [过时] 请参阅新答案“在 IntelliJ 配置中使用新的‘包含具有“提供”范围的依赖项’。”回答。

        添加provided 依赖项以使用IntelliJ 调试任务的最简单方法是:

        • 右击src/main/scala
        • 选择Mark Directory as... > Test Sources Root

        这告诉IntelliJsrc/main/scala 视为一个测试文件夹,它会将标记为provided 的所有依赖项添加到任何运行配置(调试/运行)中。

        每次进行 SBT 刷新时,请重做这些步骤,因为 IntelliJ 会将文件夹重置为常规源文件夹。

        【讨论】:

        • 这不是一个好的解决方案,因为对 SBT 文件的任何更改都会覆盖对项目的手动更改。更改 SBT 文件后,src/main/scala 恢复为 Sources Root
        • 配置中有一个名为“包含具有“已提供”范围的依赖项”的新复选框,使我的解决方案不再需要!
        【解决方案7】:

        在 IntelliJ 配置中使用新的“包含具有“已提供”范围的依赖项”。

        【讨论】:

        • 在以后的版本中好像去掉了这个选项
        • 是的,此选项在 Community Edition 2019.1 中可用,您需要转到“打开编辑运行/调试配置对话框”然后“编辑配置”
        【解决方案8】:

        对于运行 spark 作业,“提供”依赖项的一般解决方案有效:https://stackoverflow.com/a/21803413/1091436

        然后,您可以从 sbt、Intellij IDEA 或其他任何方式运行应用程序。

        基本上可以归结为:

        run in Compile := Defaults.runTask(fullClasspath in Compile, mainClass in (Compile, run), runner in (Compile, run)).evaluated,
        runMain in Compile := Defaults.runMainTask(fullClasspath in Compile, runner in(Compile, run)).evaluated
        

        【讨论】:

        • 我遇到了同样的问题,这种方法可以工作。但是,当我的代码在子项目中时,例如在“./subproject1”文件夹中。烦人的“NoClassDef 异常”再次出现。如何分别修改Compile / run 以应对子项目条件...?
        猜你喜欢
        • 2017-11-05
        • 2016-08-19
        • 2013-11-07
        • 2016-03-05
        • 2013-05-22
        • 1970-01-01
        • 2015-07-11
        • 2017-11-25
        • 2021-12-08
        相关资源
        最近更新 更多