-
Notifications
You must be signed in to change notification settings - Fork 41.1k
Provide fat jar aware implementations of Tomcat's Resource and ResourceSet to speed up resource loading from executable wars #16471
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
That's quite a dramatic performance penalty. We'll need to do some profiling to find out if the issue is in our |
Nothing that comes to mind based on this description. |
Thanks for the sample, @cdelgado83. I have reproduced the problem, but only when the application is packaged and run using With trace level logging enabled, I can see the following when running using
The processing performed for If two requests that load the same non-existent class are made in quick succession the second request is much quicker:
The second request is also much quicker if two requests for different classes are made in quick succession:
|
The slowness appears to be caused by There is a @markt-asf Does the above give you enough to have another look at this? |
Running from a packed WAR is always going to be slower because of the two layers of compression. Note that SpringBoot doesn't apply compression to the WAR. This enables access at a comparable speed to an unpacked WAR. Tomcat has to work with the more general case where the WAR may be compressed so Tomcat is slower.
|
Thanks, Mark. We're going to investigate implementing our own |
Thanks for the analysis ! When do you think this enhancement will be released officialy in Spring Boot ? |
It's quite low priority as the problem can be avoided by using |
Ok, thanks for the quick reply. Unfortunatelly, I have not mentionned that we are stuck to the war packaging. In fact, it seems that there is another issue while using the jar packaging: while handling an RPC call, GWT tries to load policy files (*.gwt.rpc) using:
Which returns null, and the process it then stopped. |
Please open a separate issue for that. If you provide a small sample that reproduces the problem, we'll take a look and see if it can be fixed. |
Thanks for the update, I just open a new issue regarding the problem of serialization policy file loading : #17240 |
Just have to say that this bug is still very much present in Spring-boot v. 2.1.5. We ran into it with old and large app migrated from .war .jsp kept us on war but I was able to work around this using .jar with the help of this --> https://github.com/hengyunabc/spring-boot-fat-jar-jsp-sample Ps for us performance was a DRASTIC drop since we perform at-runtime classloading for extensive plugin system |
How would one configure Tomcat to use the mentioned ExtractingRoot in Spring Boot? |
@Frettman You should be able to use a @Bean
TomcatServletWebServerFactory tomcatFactory() {
return new TomcatServletWebServerFactory() {
@Override
protected void postProcessContext(Context context) {
context.setResources(new ExtractingRoot());
}
};
} |
Thank you, @wilkinsona, this has worked perfectly. |
1 similar comment
Thank you, @wilkinsona, this has worked perfectly. |
There is one caveat to using the ExtractingRoot that one should be aware of: By default, every start of the application creates a new folder like tomcat.1150352738695762234.443 in the temp directory. This happens with or without the ExtractingRoot. But: With the ExtractingRoot the Jars will be extracted to a application-jars subdirectory of this temporary tomcat folder. And even though the Tomcat documentation states that this folder is removed when the application stops, this does not happen; at least not for me. So every start of your application will use up more space (unless your application is e.g. in a Docker container). To avoid that set the application property server.tomcat.basedir to any static folder. |
when running with JarLauncher the classical class loader loades all the classes in BOOT-INF, when running with WarLauncher the classical class loader loades all the classes in WEB-INF, so why is TomcatEmbeddedWebAppClassLoader needed? |
I believe that this has been addressed in Spring Boot 3.2 by #37452. |
I have conducted some tests with Spring Boot 3.2.0 and also with 3.3.4 and I can pretty much assure it's not fixed : I still see slow performances when launching the War with "java -jar" outside the IDE. And this goes away when using this code (based on the github example from @cdelgado83):
|
@jplandrain if you'd like us to investigate, please provide a minimal sample that reproduces the problem, some data that shows the slow performance in absolute terms, and some details of the hardware, JVM, etc that you have tested on. |
When packaging an spring-boot application as a WAR, the context class loader of a Rest Controller is very slow to load a class that does not exist.
The more dependencies the project have, the more the class loader is slow to respond.
You can test the problem with this simple project : it exposes a Rest Controller that tries to load a class with the context class loader, vs the normal class loader.
Here is the screenshot where I try to load an non existent class "toto":
It takes more than a second to respond.
With the classical class loader:
It is much more faster.
We encountered this problem while migrating a GWT application from a JBoss to a spring-boot with a tomcat embedded.
GWT tries to load non existent serialization classes, and with this class loader issue, the GUI is very slow : 10 seconds for a page to show up.
As a workaround, we are currently using Jetty embedded server instead, which uses only the classical class loader.
The text was updated successfully, but these errors were encountered: