簡體   English   中英

在webapp-runner中運行的對Heroku Web-app的API調用最終失敗,並出現google.common中的NoSuchMethodError和NoClassDefFoundError

[英]API calls to Heroku web-app running in webapp-runner eventually fail with NoSuchMethodError then NoClassDefFoundError in google.common

我正在使用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正在移動/清理番石榴jarfile。

我的Procfile內容:

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

我的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

更新1

由於我使用--expand-war參數調用webapp,因此我還檢查了擴展目錄中的jarfiles以驗證是否存在番石榴。 它是:

~/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());
    }

下次發生錯誤時,我檢查了日志,令我驚訝的是,運行時類路徑中存在番石榴罐!

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!!!
....

這里發生了什么? 我該如何調試?

您的類路徑中可能有兩個版本的番石榴或其相關的jar。 使用com.google.common.base.Splitter時,請參閱NoSuchMethodError異常

經過一些調試后,我發現我的程序在類路徑上具有兩個不同的Guava版本( guava-23.0.jarguava-jdk5-13.0.jar )。 此處建議的調試技巧是必要的,但不足以使我深入了解此問題。

使用ClassLoaders時,請務必記住, .class對象中定義的getClassLoader方法返回對最初加載該類的ClassLoader的引用。 要找到重復的jar,至關重要的是,在加載了后來因NoSuchMethodError失敗的類的同一ClassLoader上調用classLoader.getResource("/com/google/common/base/CharMatcher.class")

為了后代,引起沖突的特定依賴項是com.google.api-client 我通過在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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM