覆盖 Databricks 依赖项

Posted

技术标签:

【中文标题】覆盖 Databricks 依赖项【英文标题】:Overwrite Databricks Dependency 【发布时间】:2020-04-12 03:38:16 【问题描述】:

在我们的项目中,我们使用 com.typesafe:config 版本 1.3.4。根据最新的release notes,这个依赖已经由集群上的Databricks提供了,但是在一个非常旧的版本(1.2.1)中。 如何用我们自己的版本覆盖提供的依赖项?

我们使用 maven,在我们的依赖项中我有

<dependency>
    <groupId>com.typesafe</groupId>
    <artifactId>config</artifactId>
    <version>1.3.4</version>
</dependency>

因此,我们创建的 jar 文件应该包含较新的版本。

我通过上传 jar 文件创建了一个 Job。 Job 失败是因为找不到 1.2.1 版本之后添加的方法,所以看起来我们提供的库被集群上的旧版本覆盖了。

【问题讨论】:

不知道是否 Databricks 依赖项也在 pom.xml 中定义?还是部署环境提供的依赖? 不,它没有在我们的 pom.xml 中定义。部署环境根据你选择的Databricks运行时版本预装了很多库。 @pgruetter 你解决过这个问题吗?如果是这样,怎么做?谢谢! @OscarBonilla:是的,忘记更新了。我们确实修复了它,请参阅我的新答案。希望对您有所帮助。 【参考方案1】:

最后,我们通过在 build.sbt 中添加以下内容,为相关类添加了阴影,从而解决了这个问题

assemblyShadeRules in assembly := Seq(
  ShadeRule.rename("com.typesafe.config.**" -> "shadedSparkConfigForSpark.@1").inAll
)

【讨论】:

虽然这假设一个胖罐子是可以的【参考方案2】:

我们最终通过使用 Sparks ChildFirstURLClassLoader 解决了这个问题。该项目是开源的,所以你可以自己查看here和方法here的用法。

但作为参考,这里是完整的方法。你需要提供一个 Seq 的 jars 来覆盖你自己的,在我们的例子中是类型安全的配置。

def getChildFirstClassLoader(jars: Seq[String]): ChildFirstURLClassLoader = 
  val initialLoader = getClass.getClassLoader.asInstanceOf[URLClassLoader]

  @tailrec
  def collectUrls(clazz: ClassLoader, acc: Map[String, URL]): Map[String, URL] = 

    val urlsAcc: Map[String, URL] = acc++
      // add urls on this level to accumulator
      clazz.asInstanceOf[URLClassLoader].getURLs
      .map( url => (url.getFile.split(Environment.defaultPathSeparator).last, url))
      .filter case (name, url) => jars.contains(name)
      .toMap

    // check if any jars without URL are left
    val jarMissing = jars.exists(jar => urlsAcc.get(jar).isEmpty)
    // return accumulated if there is no parent left or no jars are missing anymore
    if (clazz.getParent == null || !jarMissing) urlsAcc else collectUrls(clazz.getParent, urlsAcc)
  

  // search classpath hierarchy until all jars are found or we have reached the top
  val urlsMap = collectUrls(initialLoader, Map())

  // check if everything found
  val jarsNotFound = jars.filter( jar => urlsMap.get(jar).isEmpty)
  if (jarsNotFound.nonEmpty) 
    logger.info(s"""available jars are $initialLoader.getURLs.mkString(", ") (not including parent classpaths)""")
    throw ConfigurationException(s"""jars $jarsNotFound.mkString(", ") not found in parent class loaders classpath. Cannot initialize ChildFirstURLClassLoader.""")
  
  // create child-first classloader
  new ChildFirstURLClassLoader(urlsMap.values.toArray, initialLoader)

如您所见,如果您指定的 jar 文件在类路径中不存在,它也有一些中止逻辑。

【讨论】:

【参考方案3】:

Databricks 支持初始化脚本(集群范围或全局范围),以便您可以安装/删除任何依赖项。详情在https://docs.databricks.com/clusters/init-scripts.html

在您的初始化脚本中,您可以删除位于 databricks 驱动程序/执行程序类路径 /databricks/jars/ 的默认 jar 文件并在那里添加预期的文件。

【讨论】:

以上是关于覆盖 Databricks 依赖项的主要内容,如果未能解决你的问题,请参考以下文章

Databricks (Spark):.egg 依赖项未自动安装?

提取 Databricks 集群依赖项并将它们添加到 build.sbt 以在 Azure DevOps 中构建 jar

Databricks 增量表与 SQL Server 增量表

Azure Databricks - 从 Gen2 Data Lake Storage 运行 Spark Jar

覆盖 Micronaut 测试中的依赖项

如何使用 `yarn` 覆盖嵌套依赖项?