覆盖数据块依赖
Overwrite Databricks Dependency
在我们的项目中,我们在 1.3.4 版本中使用 com.typesafe:config。根据最新的 release notes,集群上的 Databricks 已经提供了此依赖项,但版本非常旧(1.2.1)。
如何用我们自己的版本覆盖提供的依赖项?
我们使用maven,在我们的依赖项中我有
<dependency>
<groupId>com.typesafe</groupId>
<artifactId>config</artifactId>
<version>1.3.4</version>
</dependency>
因此,我们创建的 jar 文件应该包含较新的版本。
我通过上传 jar 文件创建了一个作业。 Job失败是因为找不到1.2.1版本后添加的方法,所以看起来我们提供的库被集群上的旧版本覆盖了。
最后,我们通过将以下内容添加到我们的 build.sbt
中,对相关的 类 进行了阴影处理,从而解决了这个问题
assemblyShadeRules in assembly := Seq(
ShadeRule.rename("com.typesafe.config.**" -> "shadedSparkConfigForSpark.@1").inAll
)
最后我们利用Sparks ChildFirstURLClassLoader解决了这个问题。该项目是开源的,所以你可以自己检查一下here and the usage of the method here。
但作为参考,这里是完整的方法。你需要提供一个你想用你自己的覆盖的 jar 序列,在我们的例子中它是类型安全配置。
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 文件,它也有一些中止逻辑。
Databricks 支持初始化脚本(集群范围或全局范围),因此您可以 install/remove 任何依赖项。详情在 https://docs.databricks.com/clusters/init-scripts.html.
在您的初始化脚本中,您可以删除位于数据块 driver/executor 类路径 /databricks/jars/
的默认 jar 文件,并在那里添加预期的文件。
在我们的项目中,我们在 1.3.4 版本中使用 com.typesafe:config。根据最新的 release notes,集群上的 Databricks 已经提供了此依赖项,但版本非常旧(1.2.1)。 如何用我们自己的版本覆盖提供的依赖项?
我们使用maven,在我们的依赖项中我有
<dependency>
<groupId>com.typesafe</groupId>
<artifactId>config</artifactId>
<version>1.3.4</version>
</dependency>
因此,我们创建的 jar 文件应该包含较新的版本。
我通过上传 jar 文件创建了一个作业。 Job失败是因为找不到1.2.1版本后添加的方法,所以看起来我们提供的库被集群上的旧版本覆盖了。
最后,我们通过将以下内容添加到我们的 build.sbt
中,对相关的 类 进行了阴影处理,从而解决了这个问题assemblyShadeRules in assembly := Seq(
ShadeRule.rename("com.typesafe.config.**" -> "shadedSparkConfigForSpark.@1").inAll
)
最后我们利用Sparks ChildFirstURLClassLoader解决了这个问题。该项目是开源的,所以你可以自己检查一下here and the usage of the method here。
但作为参考,这里是完整的方法。你需要提供一个你想用你自己的覆盖的 jar 序列,在我们的例子中它是类型安全配置。
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 文件,它也有一些中止逻辑。
Databricks 支持初始化脚本(集群范围或全局范围),因此您可以 install/remove 任何依赖项。详情在 https://docs.databricks.com/clusters/init-scripts.html.
在您的初始化脚本中,您可以删除位于数据块 driver/executor 类路径 /databricks/jars/
的默认 jar 文件,并在那里添加预期的文件。