Springboot 嵌入式 Tomcat 类装填器速度慢

2022-09-04 23:00:40

我已经构建了一个使用SpringBoot v1.3.6.RELEASE Tomcat 8.0.36 Java 1.8u101的Web应用程序 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 : 查找资源(元INF/服务)

上述所有消息都由o.a.c.loader.WebappClassLoaderBase生成,它们显然是由来自JAX-WS RI的ClientSOAPHandlerTube.processRequest引起的。

您会注意到第一次调用需要 2 秒以上,但后续调用仅需几毫秒。我想知道是否有人经历过这种行为?

可能的解决方案:是否可以将tomcat在springboot中使用的类加载器更改为使用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);
                }
            });
        }
    };
}

答案 1

实际上,您的发现非常好,您已经有90%的人回答了您的问题。这两个事实:

  1. “似乎停滞发生在o.a.c.loader.WebappClassLoaderBase中”
  2. “当使用Jetty作为容器运行时,这种情况不会发生。

表明这将是一个与Tomcat相关的问题,因为:

  1. o.a.c.代表org.apache.catalina
  2. 您的代码在另一个容器上运行良好。(码头)

您还观察到,在空闲时间 15 秒后出现问题。这完全符合Tomcat的默认设置,即:checkInterval

检查已修改的类和资源之间的秒数(如果可重装)已设置为 true。默认值为 15 秒。

简而言之:目前你的标志是ON,Tomcat试图重新加载你的类,这在开发过程中很方便,但在任何其他情况下都是不可接受的。关闭它的方法不是通过弹簧启动。reloadable

解决方案:
您需要找到您的上下文.xml/服务器.xml,您将找到您的定义,如下所示:Context

<Context ... reloadable="true">

删除该标志,您就解决了问题。文件本身可以位于$CATALINA_BASE/conf of $CATALINE_HOME/conf 中,但实际上,如果您使用某些IDE为您管理Tomcat,则这些位置可能有点难以找到。reloadable

如果嵌入了带有弹簧靴的Tomcat:

可用于操作 Tomcat 设置的类是:。EmbeddedServletContainerCustomizer

通过这种方式,您可以添加一个(),以便您可以调用上下文本身。TomcatContextCustomizeraddContextCustomizerssetReloadable

我看不出Spring-boot有任何理由需要这个标志在true上。


答案 2

推荐