[英]API calls to Heroku web-app running in webapp-runner eventually fail with NoSuchMethodError then NoClassDefFoundError in google.common
I am running a war on Heroku using webapp-runner
. 我正在使用webapp-runner
在Heroku上进行战争。 I deploy the application using the heroku-maven-plugin
version 1.2 via the following command: mvn heroku:deploy-war
. 我通过以下命令使用heroku-maven-plugin
1.2版部署该应用程序: mvn heroku:deploy-war
。 Initially, the app works and all endpoints return valid responses. 最初,该应用正常运行,并且所有端点都返回有效响应。 However, if I allow the app to idle long enough for Heroku to put it to sleep and then invoke an endpoint which calls into guava I receive a NoSuchMethodError
: 但是,如果我允许该应用程序空闲足够长的时间以让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
All subsequent calls to the same API produce a NoClassDefFoundError
at the same point 随后对同一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
These issues seem to suggest that the guava jar is present at compile time but not present at runtime. 这些问题似乎表明,番石榴罐在编译时存在,但在运行时不存在。 However, I logged-in to the web dyno and verified that the guava jar was included in my warfile
但是,我登录了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...
I am struggling to explain why the endpoints work immediately after the app is deployed but later produce these errors. 我正在努力解释为什么端点在部署应用程序后立即工作,但后来又产生这些错误。 To me this behavior seems to suggest that Heroku is potentially supplying a different classpath when my app wakes up from sleep than when it is initially run or that Heroku is moving/cleaning up the guava jarfile. 对我而言,这种行为似乎表明,当我的应用从睡眠状态唤醒时,Heroku可能会提供与最初运行时不同的类路径,或者Heroku正在移动/清理番石榴jarfile。
Contents of my Procfile
: 我的Procfile
内容:
web: java $JAVA_OPTS -jar target/dependency/webapp-runner.jar --port $PORT --expand-war target/MyApp.war
Java Processes runnning on my web dyno: 我的Web 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
Update 1 更新1
Since I am invoking my webapp with the --expand-war
argument I also checked the jarfiles in the expanded directory to verify that guava was present. 由于我使用--expand-war
参数调用webapp,因此我还检查了扩展目录中的jarfiles以验证是否存在番石榴。 It is: 它是:
~/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...
Update 2 更新2
I added the following logic to the problematic web service to printout the classpath and the resources on it: 我向有问题的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());
}
The next time the error occurred I examined the logs and to my surprise found that the guava jar was present on the runtime classpath! 下次发生错误时,我检查了日志,令我惊讶的是,运行时类路径中存在番石榴罐!
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!!!
....
What is going on here? 这里发生了什么? How do I debug this? 我该如何调试?
You probably have two versions of guava or its related jars on your classpath. 您的类路径中可能有两个版本的番石榴或其相关的jar。 See NoSuchMethodError exception when using com.google.common.base.Splitter 使用com.google.common.base.Splitter时,请参阅NoSuchMethodError异常
After some debugging, I discovered that my program had two different versions of Guava on the classpath ( guava-23.0.jar
& guava-jdk5-13.0.jar
). 经过一些调试后,我发现我的程序在类路径上具有两个不同的Guava版本( guava-23.0.jar
和guava-jdk5-13.0.jar
)。 The debugging tip suggested here was necessary but not sufficient for me to get to the bottom of this. 此处建议的调试技巧是必要的,但不足以使我深入了解此问题。
When working with ClassLoaders it's important to remember that the getClassLoader
method defined in the .class
object returns a reference to the ClassLoader that originally loaded the class. 使用ClassLoaders时,请务必记住, .class
对象中定义的getClassLoader
方法返回对最初加载该类的ClassLoader的引用。 To find the duplicate jar, it was crucial to invoke classLoader.getResource("/com/google/common/base/CharMatcher.class")
on the same ClassLoader which loaded the class that later failed with the NoSuchMethodError
. 要找到重复的jar,至关重要的是,在加载了后来因NoSuchMethodError
失败的类的同一ClassLoader上调用classLoader.getResource("/com/google/common/base/CharMatcher.class")
。
For posterity, the specific dependency which caused the conflict was com.google.api-client
. 为了后代,引起冲突的特定依赖项是com.google.api-client
。 I resolved it by adding the following exclusion
to the dependency in my pom.xml
我通过在pom.xml
的依赖项中添加以下exclusion
来解决它
<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>
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.