RestTemplate请求重定向URL交替抛出ClientProtocolException问题排查
First, let's unpack why you're seeing this alternating success/failure pattern:
When using a connection pool, RestTemplate reuses existing HTTP connections instead of creating new ones for every request. Your target URL-A always returns a 302 redirect, and it's likely the server is closing the connection immediately after sending that 302 response (via a Connection: close header). The connection pool doesn't realize the connection is dead, so it tries to reuse it for the next request—resulting in the ProtocolException because the server isn't responding on that closed connection. After the failed request, the pool discards the bad connection and creates a new one for the next attempt, hence the back-and-forth between success and failure.
Debugging Steps to Confirm This
Enable Detailed HTTP Logging
Add these settings to yourapplication.propertiesto see exactly what's happening with connections and responses:logging.level.org.apache.http=DEBUG logging.level.org.springframework.web.client=DEBUGLook for logs mentioning "reusing connection" or "Connection: close" in the server response headers. This will confirm if the pool is reusing closed connections.
Test Without Connection Reuse
Temporarily configure your pool to only allow 1 connection and force validation on every borrow:poolingHttpClientConnectionManager.setMaxTotal(1); poolingHttpClientConnectionManager.setDefaultMaxPerRoute(1); poolingHttpClientConnectionManager.setValidateAfterInactivity(0); // Validate every timeIf the alternating failures stop, this confirms connection reuse is the root cause.
Inspect the 302 Response Headers
Use a tool like curl or Postman to send a request to URL-A and check the response headers. Look for:Connection: close: This tells the client the server is closing the connection after the response.Keep-Alive: If present, note the timeout value—this tells you how long the server will keep the connection open.
Fixes to Resolve the Issue
Here are two targeted fixes you can apply to your RestTemplate configuration:
1. Configure Connection Validation
Tell the connection pool to check if a connection is still alive before reusing it. Add this to your PoolingHttpClientConnectionManager setup:
poolingHttpClientConnectionManager.setValidateAfterInactivity(3000); // Validate after 3 seconds of inactivity
This ensures the pool only uses connections that are still open and responsive.
2. Handle Server Connection Closures Properly
Configure a custom ConnectionKeepAliveStrategy to respect the server's Connection and Keep-Alive headers, so the pool doesn't keep connections that the server has marked for closure. Update your HttpClient build code:
HttpClient client = HttpClients.custom() .setConnectionManager(poolingHttpClientConnectionManager) .setKeepAliveStrategy((response, context) -> { // Respect the server's Keep-Alive timeout if provided HeaderElementIterator iterator = new BasicHeaderElementIterator( response.headerIterator(HTTP.CONN_KEEP_ALIVE)); while (iterator.hasNext()) { HeaderElement element = iterator.nextElement(); String param = element.getName(); String value = element.getValue(); if (value != null && param.equalsIgnoreCase("timeout")) { try { return Long.parseLong(value) * 1000; // Convert seconds to ms } catch (NumberFormatException ignored) {} } } // Fallback to a reasonable default if no header is present return 30 * 1000; }) .build();
3. Disable Automatic Redirects (If You Don't Need Them)
If you don't need RestTemplate to follow the 302 redirect (since URL-A always returns 302), disable redirect handling entirely. This avoids any edge cases with redirects and connection reuse:
HttpClient client = HttpClients.custom() .setConnectionManager(poolingHttpClientConnectionManager) .disableRedirectHandling() .build();
Why Your Previous Fixes Didn't Work
- Removing the connection pool meant every request used a fresh connection, so you never hit the closed connection issue.
- Using
RestTemplateBuilderwithout configuring the underlying HttpClient's connection validation/keep-alive settings didn't address the root problem of stale connections in the pool.
内容的提问来源于stack exchange,提问作者Renil Joseph

