Hystrix超时后run()仍执行,如何返回后续请求响应?
Problem Statement
I've created a class by inheriting HystrixCommand, configured request timeout using HystrixCommandProperties.Setter().withExecutionIsolationThreadTimeoutInMilliseconds(int milliSec), and overridden the fallback method that triggers on timeout. Here's my code:
@Override protected JSONObject run() throws Exception { // Execute HTTP call final HttpEntity<String> httpEntity = new HttpEntity<>(httpHeaders); ResponseEntity<String> response = restTemplate.exchange(requestUrl, HttpMethod.GET, httpEntity, JSONObject.class) .getBody(); System.out.println(response); return response; } @Override protected JSONObject getFallback() { final JSONObject json = new JSONObject(); json.put("status", "900"); json.put("message", "Request Failed"); return json; }
When a timeout occurs, the fallback response is returned as expected. However, I noticed that the HTTP request executed in run() still returns a response later, and I have no way to return this late response to the client. Is there any way to handle and return this subsequent response?
Solution Options
Since HTTP follows a strict request-response model, once you've sent the fallback response to the client, you can't modify that response to include the late-arriving result. But you can handle the late response in background processes, or use asynchronous communication to notify the client later. Here are two practical approaches:
1. Async Execution with Background Processing
Modify the run() method to execute the HTTP call asynchronously. This way, when the timeout triggers the fallback, the async task continues running in the background. You can then process the late response (e.g., cache it, log it, or store it in a database) and optionally notify the client via an asynchronous channel.
@Override protected JSONObject run() throws Exception { // Wrap the HTTP call in an async task CompletableFuture<JSONObject> requestFuture = CompletableFuture.supplyAsync(() -> { try { final HttpEntity<String> httpEntity = new HttpEntity<>(httpHeaders); ResponseEntity<String> rawResponse = restTemplate.exchange(requestUrl, HttpMethod.GET, httpEntity, String.class); JSONObject response = new JSONObject(rawResponse.getBody()); // Handle the late response here (e.g., cache, log, or trigger a notification) processLateResponse(response); return response; } catch (Exception e) { throw new RuntimeException("HTTP call failed", e); } }); try { // Wait for the result within the Hystrix timeout window return requestFuture.get( getProperties().getExecutionIsolationThreadTimeoutInMilliseconds(), TimeUnit.MILLISECONDS ); } catch (TimeoutException e) { // Timeout triggers fallback, but the async task keeps running throw new HystrixTimeoutException(); } } // Method to handle the late-arriving response private void processLateResponse(JSONObject response) { // Example: Store in cache for client polling cache.put("late-response-key", response); // Or send a WebSocket/SSE message to the client if connected // websocketService.sendUpdate(response); System.out.println("Late response processed: " + response); }
How this works:
- The HTTP call runs in a separate thread via
CompletableFuture.supplyAsync(). - The main thread waits for the result only within the Hystrix timeout period. If it times out, it throws
HystrixTimeoutExceptionto trigger the fallback. - The async task continues executing, and when the response arrives,
processLateResponse()handles it. If you need to get this to the client, you can use:- Client polling: Let the client periodically check a cache endpoint for the updated result.
- WebSocket/SSE: Push the result to the client in real-time if they're connected.
2. Use HystrixObservableCommand for Multi-Result Emission
If your client can handle multiple responses (e.g., using streaming or event-driven clients), you can switch to HystrixObservableCommand instead of HystrixCommand. This allows you to emit both the fallback response and the late-arriving result.
public class MyObservableHystrixCommand extends HystrixObservableCommand<JSONObject> { private String requestUrl; private HttpHeaders httpHeaders; private RestTemplate restTemplate; // Constructor with necessary dependencies public MyObservableHystrixCommand(String requestUrl, HttpHeaders httpHeaders, RestTemplate restTemplate) { super(HystrixCommandGroupKey.Factory.asKey("MyGroup")); this.requestUrl = requestUrl; this.httpHeaders = httpHeaders; this.restTemplate = restTemplate; } @Override protected Observable<JSONObject> construct() { return Observable.create(subscriber -> { // Async HTTP call CompletableFuture.runAsync(() -> { try { final HttpEntity<String> httpEntity = new HttpEntity<>(httpHeaders); ResponseEntity<String> rawResponse = restTemplate.exchange(requestUrl, HttpMethod.GET, httpEntity, String.class); JSONObject response = new JSONObject(rawResponse.getBody()); subscriber.onNext(response); subscriber.onCompleted(); } catch (Exception e) { subscriber.onError(e); } }); }).timeout(getProperties().getExecutionIsolationThreadTimeoutInMilliseconds(), TimeUnit.MILLISECONDS); } @Override protected Observable<JSONObject> resumeWithFallback() { // Create fallback response JSONObject fallback = new JSONObject(); fallback.put("status", "900"); fallback.put("message", "Request Failed"); // Emit fallback first, then merge with the original observable (if it completes later) return Observable.just(fallback) .mergeWith(construct().onErrorResumeNext(Observable.empty())); } }
How this works:
- The
construct()method creates an Observable that emits the HTTP response when it arrives. - The
resumeWithFallback()method emits the fallback immediately on timeout, then merges with the original Observable to emit the late response if it eventually completes. - Your client needs to subscribe to this Observable and handle both the fallback and the late response (e.g., using RxJava or a streaming client).
Key Takeaways
- You can't modify an already sent HTTP response to include a late result, but you can process the result in the background.
- For client-facing updates, use asynchronous communication patterns like WebSocket/SSE or client-side polling.
HystrixObservableCommandis useful if you need to emit multiple results, but it requires changes to both your server and client code.
内容的提问来源于stack exchange,提问作者Arunkumar Papena

