I have built a web application that uses SpringBoot v1.3.6.RELEASE Tomcat 8.0.36 Java 1.8u101 on CentOS 7.2
The web application is also a SOAP client that calls out to another web application.(JAX-WS RI 2.2.9) If the applications remains idle for 15 seconds the first webservice call stalls for nearly 2 seconds. It appears that the stall happens in o.a.c.loader.WebappClassLoaderBase.
After idle 15 seconds
16:02:36.165 : Delegating to parent classloader org.springframework.boot.loader.LaunchedURLClassLoader@45283ce2
16:02:36.170 : Searching local repositories
16:02:36.170 : findResource(META-INF/services/javax.xml.soap.MetaFactory)
16:02:38.533 : --> Resource not found, returning null
16:02:38.533 : --> Resource not found, returning null
Next request no idle time
16:07:09.981 : Delegating to parent classloader org.springframework.boot.loader.LaunchedURLClassLoader@45283ce2
16:07:09.984 : Searching local repositories
16:07:09.985 : findResource(META-INF/services/javax.xml.soap.MetaFactory)
16:07:09.986 : --> Resource not found, returning null
16:07:09.986 : --> Resource not found, returning null
16:07:09.988 : findResources(META-INF/services
All above messages produced by o.a.c.loader.WebappClassLoaderBase and they are apparently being caused by ClientSOAPHandlerTube.processRequest which is from JAX-WS RI.
You'll notice the first call takes over 2 seconds but subsequent calls take only milliseconds. I'm wondering if anyone has experienced this behavior?
Possible solutions: Is it possible to change out the classloader used by tomcat in springboot to use ParallelWebappClassLoader
Or maybe this is a product of the reloadable flag on the classloader but I don't see how to change that flag in springboot.
When run using Jetty as the container this does not occur.
Final Solution: (thanks to 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);
}
});
}
};
}
Spring Boot aims to be production ready, by default. This means that it ships with useful defaults out of the box that may be overriden, if necessary. By default, Spring Boot provides an embedded Apache Tomcat build.
The default Embedded Web Servers in Spring-Boot is Tomcat , but you can easily change it to others.
Spring Boot web applications include a pre-configured, embedded web server by default. In some situations, though, we'd like to modify the default configuration to meet custom requirements. In this tutorial, we'll look at a few common use cases for configuring the Tomcat embedded server through the application.
The Embedded tomcat server has a set of default configurations, which makes them ready to use. However, we can use the properties or yaml files to configure embedded tomcat server and change the default settings.
Actually your findings are quite good and you have 90% answered your question already. These two facts:
show that it is going be a Tomcat-related problem because:
o.a.c.
stands for org.apache.catalina
You also observed, that the issue is happening after 15 seconds of idle time. This perfectly corresponds to Tomcat's default checkInterval
setting, which is:
The number of seconds between checks for modified classes and resources, if reloadable has been set to true. The default is 15 seconds.
So in short: currently your reloadable
flag is ON, and Tomcat tries to reload your classes which is handy during development, but unacceptable in any other case. The way to switch it off is not via Spring-boot though.
SOLUTION:
You need to locate your context.xml / server.xml where you will find your Context
defined like this:
<Context ... reloadable="true">
Remove the reloadable
flag, and you have solved the problem. The file itself can be either in $CATALINA_BASE/conf of $CATALINE_HOME/conf, but in reality these locations can be a bit tricky to find if you are using some IDE to manage Tomcat for you.
In case of embedded Tomcat with Spring-boot:
The class you can use to manipulate Tomcat settings is: EmbeddedServletContainerCustomizer
.
Through this you can add a TomcatContextCustomizer
(addContextCustomizers
) so that you can call setReloadable
on the context itself.
I do not see any reason for Spring-boot needing this flag on true.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With