Diagnosing and Optimizing Startup Performance of a Legacy Dubbo Service with JProfile
This article walks through a systematic investigation of a slow‑starting Dubbo service built on Spring 3.2, using JProfile to pinpoint costly class‑loading checks, disabling unnecessary annotation processing, and resolving thread‑waiting issues caused by RabbitMQ and other components, ultimately reducing deployment time by about 40%.
The author, an architect known as "架构君", examines a legacy Dubbo service that runs on Spring 3.2.x and integrates many components such as RabbitMQ, Kafka, Zookeeper, Redis, and Nacos, suffering from excessively long local startup times close to ten minutes.
To diagnose the problem, JProfile is employed to capture CPU usage and method‑level execution times, revealing that repeated reflective checks for AnnotatedElementUtils (a Spring 4 class) are performed even though the service runs on an older Spring version, causing unnecessary overhead.
By disabling the tryMergedAnnotation flag in the Nacos‑related bean post‑processors, the costly class‑loading path is eliminated. The required XML configuration changes are:
<bean id="annotationNacosInjectedBeanPostProcessor" class="com.alibaba.nacos.spring.beans.factory.annotation.AnnotationNacosInjectedBeanPostProcessor">
<property name="tryMergedAnnotation" value="false"/>
</bean>
<bean id="nacosValueAnnotationBeanPostProcessor" class="com.alibaba.nacos.spring.context.annotation.config.NacosValueAnnotationBeanPostProcessor">
<property name="tryMergedAnnotation" value="false"/>
</bean>After applying this tweak, startup time drops from roughly 730 seconds to about 462 seconds, a 40% improvement, but further delays remain.
Further investigation uncovers a thread‑waiting issue in SimpleMessageListenerContainer , where a CountDownLatch blocks the main thread while waiting for RabbitMQ to start. The stack trace shows the thread stuck in await(60000L, TimeUnit.MILLISECONDS) , leading to repeated retries.
private final CountDownLatch start = new CountDownLatch(1);
public FatalListenerStartupException getStartupException() throws TimeoutException, InterruptedException {
start.await(60000L, TimeUnit.MILLISECONDS);
return this.startupException;
}
public void run() {
SimpleMessageListenerContainer.this.redeclareElementsIfNecessary();
this.consumer.start();
this.start.countDown();
// ...
restart(this.consumer);
}The root cause is a connection timeout to RabbitMQ (configured for an internal Alibaba Cloud address). Switching to a reachable public address resolves the timeout, allowing the latch to be released.
With both the annotation check disabled and the RabbitMQ connection fixed, the final deployment time reaches about 95 seconds, bringing the service into an acceptable startup window despite its many integrated components.
The article concludes by sharing this troubleshooting workflow as a reference for similar performance‑tuning scenarios.
Java Architect Essentials
Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.