We have 40+ spring boot apps and when we try to start all of them together parallel, it takes about 9 to 10 minutes. And we notice that CPU usage is always 100% throughout this entire duration.
After all apps come up successfully and registered with Eureka, CPU usage is back to normal (on average ~30-40% CPU usage after startup).
It seems each spring boot app is taking at least about 15-20 seconds to startup, which we are not happy with since application is relatively small to start with.
We also disabled spring boot auto-configuration so to make sure only required "matching" classes are loaded at start up by spring boot. And we only gained about 1 or 2 seconds at startup after this change.
We seem to have enough system resources with 8 core CPUs and 32 gb of memory on this VM.
Spring boot version is 1.3.6.RELEASE.
Is it something to do with Spring boot? Because even when we startup single spring boot app it spikes CPU to 70-80% usage. Your help is very much appreciated!
This is more of how many beans and Auto Configurations that get executed while the application being started.
For even a simple web application along with JPA, there is a webcontainer and its thread pools, DataSources initializations and many more supporting beans and auto configurations that need to get initialized. These are some serious resource taking actions and they all are rushed at the start of the application to get application booted as soon as possible.
Given that you are starting 40+ apps like these simultaneously, the server will have to pay its toll.
There are ways you can improve the application boot time.
Remove unnecessary modules and bean definitions from your application. Most common mistake a developer makes is to include a spring-boot-starter-web when the application doesn't even need a web environment. Same goes for other starter modules.
Make use of Conditional Bean definitions with the use of #ConditionalOnMissingBean #ConditionalOnProperty #ConditionalOnClass #ConditionalOnBean #ConditionalOnMissingClass #ConditionalOnExpression. This might backfire if you make spring to check for beans with lots of conditions.
Make use of spring profiles. If you don't want a specific set of beans not to be part of that running instance you can group them into a profile and enable them or disable them
Configure initial number of threads a web container can have. Same goes for Datasources. Initiate your pool with only required number of active threads.
Using lazy-initialization for beans by annotating your classes or beans with #Lazy. This annotation can be per bean or against an entire #Configuration.
If that doesn't satisfy your needs, you can always throttle the CPU usage per process with commands like nice or cputools.
Here is an article around cputools.
Related
I am trying to improve performance of medium tests in Spring Boot.
I am using the Spring Boot - testcontainers library.
For an individual test this works really well, with a few annotations I can get access to kafka, zookeeper, and schema-registry. These are full services so it takes a few seconds to start everything up, all together setup takes about 40 seconds. The test accurately recreates a realistic deployment, it's beautifully simple.
This would be fine if it just happened once but it happens every time a Spring Context is created. That means every test that uses #MockBean incurs that 40 second cost.
I've tried refactoring into a single TestConfiguration class and referencing that. I've looked into using ContextHierarchy but I think that means I'll lose all of the Spring Boot niceties and I'll need to recreate the context (which means it won't look exactly like the context created by the production app).
Is there a better way to do this?
Spring framework already took care of this scenario.
There is a concept of caching the application context for test class/classes.
See the documentation.
Few lines from the documentation:
The Spring TestContext framework stores application contexts in a
static cache. This means that the context is literally stored in a
static variable. In other words, if tests run in separate processes,
the static cache is cleared between each test execution, which
effectively disables the caching mechanism.
So essentially you need to structure your code or context configuration in such a way that you use cached context in your desired test cases.
But use this capability wisely, if not thought through properly this could lead to undesired side-effects
I have a monolithic Spring MVC application consists of about 1,000 beans and it will cost about two minutes to startup.
Now I am researching to find out why it startup too slow. I added a BeanFactoryPostProcessor to record the launch time and use ApplicationListener to listen to the ContextRefreshedEvent and record the time that the ApplicationContext has refreshed. Then the result shows that the application takes about 80 seconds to finish initializing the ApplicationContext.
After reviewing the code, I found there are two many circular dependencies in the code.
I am wondering if it is the circular dependencies that cause the ApplicationContext start too slow? What I can do to speed up the startup time?
The approaches I have tried include:
Check the #PostConstruct to find out if it is asynchronous.
Adjust the -Xmx and -Xms options.
Add lazy-init to the beans.
Seems not working.
Any help will be appreciated.
I assume you are using Spring Boot and then you are implicitly using the annotation component scan. So, Spring will scan each class in order to create Bean. A possible solution could be use #ComponentScan("packageToScan") instead of #ComponentScan.
However, I do not know your goal but think if you really need to speed up the startup.
I would like to ask why does the spring classloader load java classes multiple times when <context:load-time-weaver aspectj-weaving="on" /> is used in xml config?
I can see spring is using
org.springframework.context.support.ContextTypeMatchClassLoader$ContextOverridingClassLoader
classloader, which, as I read in documentation, creates new classloader instance for each loaded class. In our current project this results in 11 loaded classes of the same type - 1 using parent classloader and 10 more using ContextOverridingClassLoader (each loaded in its own). What could be the cause of this? If we startup many applications in parallel, these duplicate classes eat up too mach permgen memory (resulting in crash). We could just increase permgen memory of course, but I was curious if there is anything else to do.
As soon as I remove this configuration parameter, spring loads all classes only once. I checked this using -XX:+TraceClassLoading VM option and heapdumps.
We are using Spring 3.2.4 and AspectJ 1.7.4
Update:
After I upgraded to Spring 4.2.1, each class is now loaded 15 times. Could it be somehow connected to spring aspects?
We ended up calling GC after application context initialization, thus reducing the amount of used memory during parallel startup of many applications (a good tradeoff to longer application startup) as each application cleans up after init.
I have a Spring-WS web service that has three issues:
Slow startup time
Slow generation of the dynamic WSDL
Heavy usage of PermGen (app has to be 1.6 compatible)
Currently, the spring-ws-servlet.xml file has several <context:component-scan> elements for autowired dependencies. Two of these scan nearly everything in two external libraries containing Hibernate DAO and Entity classes. Similarly, the Hibernate session factory bean scans a large number of entities from these two libraries.
So, my questions:
Obviously, we would see at least some performance improvement by limiting the scope of the <context:component-scan> elements. But really, would it be that much?
Similarly, would I see improvements by limiting the scope of what Entities are scanned by the session factory?
Making these changes will NOT be a quick process (alter code, test, etc). Therefore, if anyone can add their wisdom, I would greatly appreciate it.
Actually I am developing a spring ws application on Google Cloud and I also have the same problem with slow start up time. The biggest difference that I have notice was when I have moved to aspectj compile time weaving using aspectj-maven-plugin. If you haven't done this yet try this one. The result may be vary depends on your code and deployment environment. On the cloud every file operation is much slower so this may be a reason why this work for me so well.
I've 2 applications each one uses different spring application context configuration on the same JVM, and every time i tries to run both of them together i found a problem that the last one configuration always overrides the previous one so the spring context loaded with the last one configuration, any advices how to overcome this, by letting every application runs with it's configuration without affected by other spring context.