【问题标题】:Running fat jar vs sbt run运行 fat jar vs sbt run
【发布时间】:2021-09-05 12:48:12
【问题描述】:

我有一个运行以下代码的 scala 程序。

1.    public static HttpCredentialProvider loadHttpCredentialProviders(String name) {
2.       ServiceLoader<HttpCredentialProvider> providers = ServiceLoader.load(HttpCredentialProvider.class, BasicAuthCredentialProvider.class.getClassLoader());
3.        Iterator var2 = providers.iterator();
4.
5.        HttpCredentialProvider provider;
6.        do {
7.            if (!var2.hasNext()) {
8.                throw new ConfigException("HttpCredentialProvider not found for " + name);
9.            }
10.
11.           provider = (HttpCredentialProvider)var2.next();
12.       } while(!provider.getScheme().equalsIgnoreCase(name));
13.
14.       return provider;
15.   }

我将使用sbt assembly 创建一个 uber/fat jar。然后我将使用命令java -jar app.jar。结果如下异常

Caused by: org.apache.kafka.common.config.ConfigException: HttpCredentialProvider not found for BASIC
    at io.confluent.security.auth.client.provider.BuiltInAuthProviders.loadHttpCredentialProviders(BuiltInAuthProviders.java:56)
    at io.confluent.security.auth.client.rest.RestClient.<init>(RestClient.java:117)
    at io.confluent.security.auth.client.rest.RestClient.<init>(RestClient.java:95)
    at io.confluent.kafka.clients.plugins.auth.token.TokenUserLoginCallbackHandler.configure(TokenUserLoginCallbackHandler.java:67)
    at io.confluent.kafka.clients.plugins.auth.token.AbstractTokenLoginCallbackHandler.configure(AbstractTokenLoginCallbackHandler.java:86)
    at org.apache.kafka.common.security.authenticator.LoginManager.<init>(LoginManager.java:60)
    at org.apache.kafka.common.security.authenticator.LoginManager.acquireLoginManager(LoginManager.java:105)
    at org.apache.kafka.common.network.SaslChannelBuilder.configure(SaslChannelBuilder.java:161)

上面的错误是从line 8抛出的。

我将执行sbt run,我没有发现任何错误。该应用程序运行良好。

我尝试过的事情:

  1. 由于区别在于sbt runjava -jar,因此 jar 可能不包含相关类 (BasicAuthCredentialProvider)。我验证了jar -tf 事实上确实存在这些类。如果该类不存在,我会怀疑是 ClassNotFound 异常。
  2. 远程调试使用JAVA_OPTS="-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005" sbt runjava -jar -agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005 app.jar 运行应用程序的两种模式。我的想法是找出实际发生的情况。我在这里得到了一些有趣的结果。我单步执行了代码,发现这一行
ServiceLoader<HttpCredentialProvider> providers = ServiceLoader.load(HttpCredentialProvider.class, BasicAuthCredentialProvider.class.getClassLoader());

调用ServiceLoader.load 处理一些反射

public static <S> ServiceLoader<S> load(Class<S> service,
                                            ClassLoader loader)
{
    return new ServiceLoader<>(Reflection.getCallerClass(), service, loader);
}

这正是我被阻止的地方。我想了解导致问题的原因,但我不确定如何通过反射问题进行调试(如果是问题所在)。运行sbt run 时,代码会执行与上述代码存在于同一类中的另一个函数。此函数称为loadBasicAuthCredentialProvider。但是,该函数永远不会被java -jar 调用。这告诉我反射在这里发挥了一些作用。

  1. 我还将 JAVA 版本从 8 切换到 11。那里也没有 Bueno。

如果有人有其他想法,我可以尝试调试这个问题,那就太好了!

谢谢!

【问题讨论】:

    标签: java scala reflection sbt uberjar


    【解决方案1】:

    我想通了!修复是在装配的合并策略中。它看起来像这样:

    assemblyMergeStrategy in assembly := {
      case PathList("META-INF", xs@_*) => MergeStrategy.discard
      case _ => MergeStrategy.first
    }
    

    我们需要确保为服务添加了 META-INF。所以它需要看起来像这样。

    assemblyMergeStrategy in assembly := {
      case PathList("META-INF", "services", xs@_*) =>
        MergeStrategy.first
      case PathList("META-INF", xs@_*) =>
        MergeStrategy.discard
      case x =>
        MergeStrategy.first
    }
    

    我通过KAFKA-8340找到了答案!

    观察该调用加载的服务不包括 JAR 中包含的 META-INF/services/... 文件中描述的那些 在插件目录中

    【讨论】:

      猜你喜欢
      • 2018-08-26
      • 2018-09-24
      • 2020-02-04
      • 1970-01-01
      • 2018-02-09
      • 2016-12-15
      • 2014-09-19
      • 2015-04-12
      • 2019-01-10
      相关资源
      最近更新 更多