Spring WebFlux接口异常:首次请求正常后续请求挂起问题排查
Hey Justin, let's break down why your /route/accountInformation endpoint works once then gets stuck indefinitely. The root issue is a critical mistake in how you're handling reactive streams in your getAccountInformation() method:
String queryParams = "recvWindow=50000×tamp=" + responseServerTime.block().getServerTime();
The Problem with block() in WebFlux
Spring WebFlux relies on a small pool of non-blocking threads to handle requests. When you call block(), you force that thread to pause and wait synchronously for the Mono<ResponseServerTime> to finish.
- The first request uses up one of these limited threads, holding it hostage until the Binance time API responds.
- Once all threads in the pool are stuck on blocking calls, there are no threads left to process new requests. That's exactly why subsequent calls hang forever.
Blocking operations completely break the reactive, non-blocking model WebFlux is built on—they're one of the biggest pitfalls for new reactive developers.
The Fix: Go Fully Reactive
We need to rewrite getAccountInformation() to use reactive operators instead of blocking. Here's the corrected version:
public Mono<AccountInformation> getAccountInformation() { return binanceServerTimeApi.getServerTime() // Process server time reactively once it's available .map(responseServerTime -> { String queryParams = "recvWindow=50000×tamp=" + responseServerTime.getServerTime(); String signature = HmacSHA256Signer.sign(queryParams, secret); return queryParams + "&signature=" + signature; }) // Chain the account info request once we have the signed params .flatMap(signedQuery -> { // Use UriBuilder for safer, cleaner URL construction return this.webClient.get() .uri(uriBuilder -> uriBuilder .path("api/v3/account") .query(signedQuery) .build()) .accept(MediaType.APPLICATION_JSON) .retrieve() .bodyToMono(AccountInformation.class); }) .log(); }
Key Improvements:
- Replace
block()withmap(): We process the server time asynchronously once it arrives, no thread blocking involved. - Use
flatMap()for WebClient calls: SincewebClient.get()returns aMono,flatMap()lets us chain it seamlessly to the previous reactive stream. - Safer URL building:
UriBuildereliminates errors from manual string concatenation (like missing?or invalid characters).
Quick Thread Safety Check
Double-check that HmacSHA256Signer.sign() is thread-safe. If it uses mutable state (like a non-thread-safe MessageDigest instance), it could cause weird bugs in a reactive environment. If it's not thread-safe, create a new instance per call or use a thread-local to handle it safely.
Why This Fix Works
Now the entire flow is non-blocking:
- The server time request runs asynchronously, freeing the thread to handle other requests while waiting.
- When the time comes back, we generate the signature and trigger the account info request without pausing any threads.
- Threads are never stuck waiting, so your app can handle concurrent requests smoothly.
After making these changes, your endpoint should work reliably for every request, not just the first one.
内容的提问来源于stack exchange,提问作者Justin

