【发布时间】:2017-01-07 03:10:19
【问题描述】:
我已经构建了一个使用 SpringBoot v1.3.6.RELEASE 雄猫 8.0.36 Java 1.8u101 在 CentOS 7.2 上
Web 应用程序也是调用另一个 Web 应用程序的 SOAP 客户端。(JAX-WS RI 2.2.9)如果应用程序保持空闲 15 秒,则第一个 Web 服务调用会停止近 2 秒。似乎停顿发生在 o.a.c.loader.WebappClassLoaderBase 中。
空闲 15 秒后
16:02:36.165 : 委托给父类加载器 org.springframework.boot.loader.LaunchedURLClassLoader@45283ce2
16:02:36.170 : 搜索本地存储库
16:02:36.170:findResource(META-INF/services/javax.xml.soap.MetaFactory)
16:02:38.533 : --> 找不到资源,返回 null
16:02:38.533 : --> 找不到资源,返回 null
下一个请求没有空闲时间
16:07:09.981 : 委托给父类加载器 org.springframework.boot.loader.LaunchedURLClassLoader@45283ce2
16:07:09.984 :搜索本地存储库
16:07:09.985:findResource(META-INF/services/javax.xml.soap.MetaFactory)
16:07:09.986 : --> 找不到资源,返回 null
16:07:09.986 : --> 找不到资源,返回 null
16:07:09.988 : findResources(META-INF/services
以上所有消息均由 o.a.c.loader.WebappClassLoaderBase 产生,它们显然是由来自 JAX-WS RI 的 ClientSOAPHandlerTube.processRequest 引起的。
您会注意到第一次调用需要 2 秒以上,但后续调用只需要几毫秒。 我想知道是否有人经历过这种行为?
可能的解决方案: 是否可以将springboot中tomcat使用的类加载器改成使用ParallelWebappClassLoader
或者这可能是类加载器上可重新加载标志的产物,但我不知道如何在 springboot 中更改该标志。
当使用 Jetty 作为容器运行时,不会发生这种情况。
最终解决方案:(感谢 Gergely Bacso)
@Bean
public EmbeddedServletContainerCustomizer servletContainerCustomizer() {
return new EmbeddedServletContainerCustomizer() {
@Override
public void customize(ConfigurableEmbeddedServletContainer container) {
if (container instanceof TomcatEmbeddedServletContainerFactory) {
customizeTomcat((TomcatEmbeddedServletContainerFactory) container);
}
}
private void customizeTomcat(TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory) {
tomcatEmbeddedServletContainerFactory.addContextCustomizers(new TomcatContextCustomizer() {
@Override
public void customize(Context cntxt) {
cntxt.setReloadable(false);
}
});
}
};
}
【问题讨论】:
-
看起来有一个缓存因 15 秒的空闲时间而失效,因此
findResource调用必须再次搜索整个类路径。我不知道该缓存可能在哪里,因为不清楚是什么生成了您在上面共享的输出。重现该问题的完整示例将在这里有所帮助。我还要看看正在寻找META-INF/services/javax.xml.soap.MetaFactory资源的任何东西。为每个请求执行查找似乎没有必要,因为结果非常不太可能发生变化。 -
@AndyWilkinson 是的,这正是我的想法。我查看了 WebappClassLoaderBase 并没有看到任何缓存。我也同意你的观点,每个请求的查找似乎都是浪费的。也许我会尝试 CXF 而不是 Metro,看看是否会发生同样的事情。
-
在迁移过程中分析了一些严重的类加载器性能问题以添加 Spring Boot 和嵌入式 Tomcat 后,我遇到了这个问题。
reloadable现在似乎默认设置为 false,但是,对于遇到此问题的任何其他人,存在类似的类加载器问题,仅在将应用程序打包为 WAR 时适用:github.com/spring-projects/spring-boot/issues/16471 .那张票上的suggested solution 为我修好了。
标签: java spring tomcat spring-boot jax-ws