Why My Spring Boot API Was 4× Slower and How Explicit Path Mapping Fixed It
A new developer refactored a Play‑based API to Spring Boot, encountered severe throughput loss due to implicit suffix path matching, profiled the bottleneck with jvisualvm and wrk, and restored performance by declaring request‑mapping suffixes explicitly, boosting QPS from under 1k to over 9k.
Background
The author joined a company and was tasked with refactoring an existing Play framework API into Spring Boot. The system required high single‑machine throughput, so Spring Boot was chosen for its rapid development capabilities.
Initial Implementation
The new API needed to return JSON or XML depending on the request suffix, and return an error for other or missing suffixes. Two approaches were considered:
Specify supported suffixes directly in @RequestMapping, e.g.,
@RequestMapping(value = {"/ping.json", "/ping.xml"}, method = RequestMethod.GET).
Leave the suffix out of @RequestMapping and enable suffix pattern matching via a WebMvcConfigurer implementation that configures configurePathMatch and configureContentNegotiation, plus an interceptor for invalid suffixes.
The author chose the second, more flexible method.
Performance Problem
During testing, the refactored API only achieved 700–800 QPS on a 2‑core, 4 GB machine, far below the required 2 000 QPS. Various web containers (Tomcat, Jetty, Undertow) and configuration tweaks failed to improve the numbers.
Attempts to use asynchronous request handling also yielded no benefit because Undertow (and Tomcat 7+) already operate with non‑blocking I/O.
Profiling and Diagnosis
The author used jvisualvm to monitor threads and discovered that Undertow’s I/O threads delegate work to a task pool, similar to Netty. Further investigation with wrk (
wrk -t 10 -c 500 -d 15s --latency http://127.0.0.1:2551/ping.json) showed a baseline QPS of 1 715 for a no‑logic endpoint.
Thread dumps revealed that DispatcherServlet.doDispatch consumed 64.2 % of the processing time, with most of the cost spent in getMatchingCondition during request‑mapping lookup.
Root Cause: Implicit Suffix Matching
Spring stores the paths from @RequestMapping in a map. When a request includes a suffix (e.g., /ping.json) that is not explicitly listed, the framework iterates over all registered paths (over 500 in this project) to find a match, causing significant overhead.
With 300 interfaces and more than 500 paths, each request incurred this costly traversal.
Solution and Results
By explicitly declaring the suffixes in the mapping annotation, the framework can perform a direct map lookup. After changing the annotation to
@RequestMapping(value = {"/ping.json", "/ping.xml"}, method = RequestMethod.GET)and re‑running the benchmark, QPS rose to 9 384 – a more than four‑fold improvement that met the production requirement.
Additional Observations
The author notes other factors that can affect performance, such as logging configuration, integrated Swagger UI, and Actuator endpoints. While some overhead is acceptable for needed functionality, unnecessary features should be trimmed.
These findings provide practical guidance for developers facing similar Spring Boot performance bottlenecks.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
IT Architects Alliance
Discussion and exchange on system, internet, large‑scale distributed, high‑availability, and high‑performance architectures, as well as big data, machine learning, AI, and architecture adjustments with internet technologies. Includes real‑world large‑scale architecture case studies. Open to architects who have ideas and enjoy sharing.
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.
