由 async-http-client 引起的 Spark 中的 IllegalAccessError

IllegalAccessError in Spark caused by async-http-client

上下文: 我正在使用 this library 处理将数据写入 InfluxDB 的 Spark 流作业。这里是环境。

相关依赖项:

"org.apache.spark" %% "spark-core" % "2.1.0" % "provided",
"org.apache.spark" %% "spark-streaming" % "2.1.0" % "provided",
"org.apache.spark" %% "spark-streaming-kafka-0-8" % "2.1.0",
"com.paulgoldbaum" %% "scala-influxdb-client" % "0.5.2" // which uses "org.asynchttpclient" % "async-http-client" % "2.0.24"

一切都可以编译并且 运行 在我的本地计算机上正常,但是当我将程序集 jar 提交到 Spark 集群时,我在驱动程序中收到此错误:

Exception in thread "main" java.lang.reflect.InvocationTargetException
  at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
  at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  at java.lang.reflect.Method.invoke(Method.java:498)
  at org.apache.spark.deploy.worker.DriverWrapper$.main(DriverWrapper.scala:58)
  at org.apache.spark.deploy.worker.DriverWrapper.main(DriverWrapper.scala)
Caused by: java.lang.IllegalAccessError: tried to access field io.netty.handler.ssl.JdkSslContext.SUPPORTED_CIPHERS from class io.netty.handler.ssl.NettySslPackageAccessor
  at io.netty.handler.ssl.NettySslPackageAccessor.jdkSupportedCipherSuites(NettySslPackageAccessor.java:24)
  at org.asynchttpclient.config.AsyncHttpClientConfigDefaults.defaultEnabledCipherSuites(AsyncHttpClientConfigDefaults.java:85)
  at org.asynchttpclient.DefaultAsyncHttpClientConfig$Builder.<init>(DefaultAsyncHttpClientConfig.java:635)
  at org.asynchttpclient.DefaultAsyncHttpClient.<init>(DefaultAsyncHttpClient.java:67)
  at com.paulgoldbaum.influxdbclient.HttpClient.<init>(HttpClient.scala:21)
  at com.paulgoldbaum.influxdbclient.InfluxDB$.connect(InfluxDB.scala:16)
  ...

如果我删除写入 InfluxDB 的代码,问题就会消失。

我四处看看后了解到 class io.netty.handler.ssl.NettySslPackageAccessor 实际上属于 async-http-client 库。访问 io.netty.handler.ssl.JdkSslContext.

中的受保护成员似乎是一种 hack-class

这个问题我纠结了几天。我要让它工作的解决方案是将 async-http-client 覆盖到不包含违规代码的早期版本。

dependencyOverrides ++= Set("org.asynchttpclient" % "async-http-client" % "2.0.12")

问题: 为什么 IllegalAccessError 只发生在集群上而不是在我的本地 运行 上?有没有更好的方法来解决这个问题?

如果我的SBT可以正常编译,那么应该没有这样的IllegalAccessError,所以这意味着我的本地代码和集群代码之间存在差异,这可能是provided spark dependencies,但是和集群是同一个版本。

我可以保留原样,但如果可以使用较新的版本会更好。或者至少我想了解为什么会出现此问题并在将来避免它。

我今天 运行 遇到了同样的问题,并在 github 上找到了 this issue,这解释了问题。基本上你在使用 Spark 时有多个 ClassLoader

and io.netty.handler.ssl.NettySslPackageAccessor and io.netty.handler.ssl.JdkSslContext are loaded by different ClassLoaders.

If this is the case, the attempt access package-private static field JdkSslContext.SUPPORTED_CIPHERS will fail with an IllegalAccessError since the package-private fields are 'scoped' at the ClassLoader level.

哦,你的解决方案也对我有用,谢谢。

这是因为您的类路径中同时包含 io.netty:nettyorg.asynchttpclient:async-http-client。如果您想使用 netty 和 assync-http-client,请将以下依赖项添加到您的 gradle(类似于 Maven pom.xml)构建脚本:

compile 'org.asynchttpclient:async-http-client:2.0.38' compile 'org.asynchttpclient:async-http-client-netty-utils:2.0.38'