在webapp-runner中运行的Heroku网络应用程序的API调用最终会失败,并且google.com

时间:2017-09-23 20:53:43

标签: java maven heroku guava webapp-runner

我正在使用webapp-runner对Heroku进行战争。我使用heroku-maven-plugin 1.2版通过以下命令部署应用程序:mvn heroku:deploy-war。最初,应用程序工作,所有端点返回有效的响应。但是,如果我允许应用程序空闲足够长的时间让Heroku将其置于休眠状态,然后调用调用番石榴的端点,我会收到NoSuchMethodError

2017-09-23T19:19:45.388865+00:00 app[web.1]: SEVERE: Servlet.service() for servlet [jersey-serlvet] in context with path [] threw exception [org.glassfish.jersey.server.ContainerException: java.lang.NoSuchMethodError: com.google.common.base.CharMatcher.ascii()Lcom/google/common/base/CharMatcher;] with root cause
2017-09-23T19:19:45.388866+00:00 app[web.1]: java.lang.NoSuchMethodError: com.google.common.base.CharMatcher.ascii()Lcom/google/common/base/CharMatcher;
2017-09-23T19:19:45.388867+00:00 app[web.1]:    at com.google.common.io.BaseEncoding$Alphabet.<init>(BaseEncoding.java:453)
2017-09-23T19:19:45.388868+00:00 app[web.1]:    at com.google.common.io.BaseEncoding$Base64Encoding.<init>(BaseEncoding.java:892)
2017-09-23T19:19:45.388869+00:00 app[web.1]:    at com.google.common.io.BaseEncoding.<clinit>(BaseEncoding.java:317)
...application specific stack trace

对同一API的所有后续调用都会在同一点生成NoClassDefFoundError

2017-09-23T19:22:24.454901+00:00 app[web.1]: SEVERE: Servlet.service() for servlet [jersey-serlvet] in context with path [] threw exception [org.glassfish.jersey.server.ContainerException: java.lang.NoClassDefFoundError: Could not initialize class com.google.common.io.BaseEncoding] with root cause
2017-09-23T19:22:24.454903+00:00 app[web.1]: java.lang.NoClassDefFoundError: Could not initialize class com.google.common.io.BaseEncoding 
...application specific stack trace

这些问题似乎表明番石榴罐在编译时存在但在运行时不存在。但是,我登录到web dyno并验证番石榴罐已包含在我的warfile

my-mbp:TrickServer me$ heroku ps:exec
Establishing credentials... done
Connecting to web.1 on ⬢ myapp...
~ $ cd target/
~/target $ ls
MyApp.war  dependency  mvn-dependency-list.log  tomcat.52079
~/target $ jar -tf MyApp.war
...lots of dependencies...
WEB-INF/lib/google-oauth-client-1.20.0.jar
WEB-INF/lib/gson-2.2.4.jar
WEB-INF/lib/guava-23.0.jar      <---guava
WEB-INF/lib/guava-jdk5-13.0.jar
...lots more dependencies...

我正在努力解释为什么端点在部署应用程序后立即工作但后来产生这些错误。对我来说,这种行为似乎表明,当我的应用程序从睡眠中醒来时,Heroku可能会提供不同的类路径,而不是最初运行时或Heroku正在移动/清理番石榴jar文件。

Procfile的内容:

web:    java $JAVA_OPTS -jar target/dependency/webapp-runner.jar --port $PORT --expand-war target/MyApp.war

在我的网络dyno上运行的Java进程:

~/target $ ps -ef | grep java
u30439       4     1  0 18:50 ?        00:00:44 java -Xmx300m -Xss512k -Dfile.encoding=UTF-8 -Duser.timezone=UTC -jar target/dependency/webapp-runner.jar --port 52079 target/MyApp.war
u30439      27     4  0 18:50 ?        00:00:00 bash --login -c java $JAVA_OPTS -jar target/dependency/webapp-runner.jar $WEBAPP_RUNNER_OPTS --port 52079 target/MyApp.war

更新1

由于我使用--expand-war参数调用我的webapp,因此我还检查了展开目录中的jar文件以验证guava是否存在。它是:

~/target/tomcat.55320/webapps/expanded/WEB-INF/lib $ ls
...dependencies...
google-oauth-client-1.20.0.jar
gson-2.2.4.jar
guava-23.0.jar
guava-jdk5-13.0.jar
...more dependencies...

更新2

我在有问题的Web服务中添加了以下逻辑,以打印出类路径及其上的资源:

logger.info("System Classpath: " + System.getProperty("java.class.path"));
logger.info("Runtime Classes...");
    ClassLoader cl = UserService.class.getClassLoader();
    URL[] urls = ((URLClassLoader) cl).getURLs();
    for(URL url: urls){
        logger.info(url.getFile());
    }

下次出现错误时,我检查了日志,我惊讶地发现guava jar存在于运行时类路径中!

2017-09-24T12:07:40.843438+00:00 app[web.1]: [heroku-exec] ERROR: Could not connect to proxy:
2017-09-24T12:07:40.844145+00:00 app[web.1]: [heroku-exec] ERROR: Too many reconnect attempts. Waiting 30 seconds...
2017-09-24T12:07:52.671620+00:00 app[web.1]: Sep 24, 2017 12:07:52 PM org.myorg.server.web.services.MyService authenticate
2017-09-24T12:07:52.671631+00:00 app[web.1]: INFO: System Classpath: target/dependency/webapp-runner.jar
2017-09-24T12:07:52.671931+00:00 app[web.1]: Sep 24, 2017 12:07:52 PM org.myorg.server.web.services.MyService authenticate
2017-09-24T12:07:52.671932+00:00 app[web.1]: INFO: Runtime Classes...
2017-09-24T12:07:52.672277+00:00 app[web.1]: Sep 24, 2017 12:07:52 PM org.myorg.server.web.services.MyService authenticate
2017-09-24T12:07:52.672279+00:00 app[web.1]: INFO: /app/target/tomcat.28304/webapps/expanded/WEB-INF/classes/
....
2017-09-24T12:07:52.690304+00:00 app[web.1]: Sep 24, 2017 12:07:52 PM org.myorg.server.web.services.MyService authenticate
2017-09-24T12:07:52.690306+00:00 app[web.1]: INFO: /app/target/tomcat.28304/webapps/expanded/WEB-INF/lib/google-oauth-client-1.20.0.jar
2017-09-24T12:07:52.690501+00:00 app[web.1]: Sep 24, 2017 12:07:52 PM org.myorg.server.web.services.MyService authenticate
2017-09-24T12:07:52.690503+00:00 app[web.1]: INFO: /app/target/tomcat.28304/webapps/expanded/WEB-INF/lib/guava-23.0.jar <--- Guava!!!
....

这里发生了什么?我该如何调试呢?

2 个答案:

答案 0 :(得分:0)

您的类路径上可能有两个版本的番石榴或其相关的jar。见NoSuchMethodError exception when using com.google.common.base.Splitter

答案 1 :(得分:0)

经过一些调试后,我发现我的程序在类路径上有两个不同版本的Guava(guava-23.0.jar&amp; guava-jdk5-13.0.jar)。调试提示建议here是必要的,但不足以让我深究这一点。

使用ClassLoader时,务必记住getClassLoader对象中定义的.class方法返回对最初加载类的ClassLoader的引用。要查找重复的jar,在同一个ClassLoader上调用classLoader.getResource("/com/google/common/base/CharMatcher.class")至关重要,该类加载了后来因NoSuchMethodError而失败的类。

对于子孙后代,导致冲突的具体依赖是com.google.api-client。我通过向exclusion

中的依赖项添加以下pom.xml来解决此问题
<dependency>
    <groupId>com.google.api-client</groupId>
    <artifactId>google-api-client</artifactId>
    <version>1.22.0</version>
    <exclusions>
        <exclusion>
            <groupId>com.google.guava</groupId>
            <artifactId>guava-jdk5</artifactId>
        </exclusion>
    </exclusions>
</dependency>