如何延长AsyncRestTemplate调用异步服务的超时时间?
问题描述
我用Spring框架开发了异步Web服务,同时用AsyncRestTemplate创建客户端调用这些服务。getForEntity方法返回ListenableFuture<ResponseEntity<T>>,正常情况下运行没问题,但当服务处理耗时较长时,isDone()会提前返回true,调用get()时还会抛出503错误:
"timestamp": "2018-05-29T22:42:26.978+0000", "status": 500, "error": "Internal Server Error", "message": "java.util.concurrent.ExecutionException: org.springframework.web.client.HttpServerErrorException: 503 null", "path": "/client/result"
我尝试在application.properties里设置spring.mvc.async.request-timeout=500000,但完全没效果。
服务端代码
配置类
@Configuration @EnableAsync public class ConfigurationClass { @Bean public Executor threadPoolTaskExecutor() { return new ThreadPoolTaskExecutor(); } }
控制器类
@RestController @RequestMapping("/server") public class ControllerClass { @GetMapping("/start") @Async public CompletableFuture<String> callService() throws InterruptedException{ Thread.sleep(100000L); return CompletableFuture.completedFuture("OK"); } }
客户端代码
@RestController @RequestMapping("/client") public class ControllerClass { private ListenableFuture<ResponseEntity<String>> entity; @GetMapping("/start") @Async public void callService() throws InterruptedException { AsyncRestTemplate restTemplate = new AsyncRestTemplate(); entity = restTemplate.getForEntity("http://localhost:8080/server/start", String.class); } @GetMapping("/state") public boolean getState() { try { return entity.isDone(); } catch (Exception e) { throw new RuntimeException(e); } } @GetMapping("/result") public ResponseEntity<String> getResult() { try { return entity.get(); } catch (Exception e) { throw new RuntimeException(e); } } }
我希望客户端能等待足够长的时间,直到服务端返回结果,请问该怎么解决?
首先得明确一个关键区别:你设置的spring.mvc.async.request-timeout是服务端处理异步请求的超时时间,和客户端AsyncRestTemplate的请求超时完全没关系,所以这个配置对你的问题起不到作用。要解决503错误,咱们得给客户端的AsyncRestTemplate单独配置超时参数。
1. 给AsyncRestTemplate配置自定义的请求工厂
AsyncRestTemplate默认用的SimpleClientHttpRequestFactory有默认的超时限制,咱们手动修改它的连接和读取超时:
先创建一个配置类,把AsyncRestTemplate做成Bean并配置超时:
@Configuration public class AsyncRestTemplateConfig { @Bean public AsyncRestTemplate asyncRestTemplate() { SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory(); // 连接超时设置为5秒(根据你的网络情况调整) factory.setConnectTimeout(5000); // 读取超时设置为500秒(比你服务端的100秒处理时间长就行) factory.setReadTimeout(500000); return new AsyncRestTemplate(factory); } }
然后在客户端控制器里注入这个Bean,不要每次都new一个AsyncRestTemplate:
@RestController @RequestMapping("/client") public class ControllerClass { private ListenableFuture<ResponseEntity<String>> entity; private final AsyncRestTemplate asyncRestTemplate; // 构造注入配置好的AsyncRestTemplate public ControllerClass(AsyncRestTemplate asyncRestTemplate) { this.asyncRestTemplate = asyncRestTemplate; } @GetMapping("/start") @Async public void callService() throws InterruptedException { entity = asyncRestTemplate.getForEntity("http://localhost:8080/server/start", String.class); } // state和result方法保持不变... }
2. 可选:用真正的异步HTTP客户端(Apache HttpClient)
如果你的项目里已经依赖了Apache HttpClient,推荐用HttpComponentsAsyncClientHttpRequestFactory,这是真正的异步实现,性能更好:
@Bean public AsyncRestTemplate asyncRestTemplate() { HttpComponentsAsyncClientHttpRequestFactory factory = new HttpComponentsAsyncClientHttpRequestFactory(); RequestConfig requestConfig = RequestConfig.custom() .setConnectTimeout(5000) .setSocketTimeout(500000) .build(); CloseableHttpAsyncClient client = HttpAsyncClients.custom() .setDefaultRequestConfig(requestConfig) .build(); factory.setHttpAsyncClient(client); return new AsyncRestTemplate(factory); }
3. 确保服务端的异步超时也足够长
虽然你设置的spring.mvc.async.request-timeout=500000是服务端的配置,但这个值也要大于你的服务端处理时间(100秒),它控制的是Spring MVC等待@Async方法返回的CompletableFuture完成的最长时间,避免服务端提前断开连接。
4. 优化客户端的get()调用
你现在用的entity.get()是无参的,会无限等待,但如果前面的超时配置没覆盖到,还是可能出问题。可以用带超时参数的get()方法主动控制等待时间:
@GetMapping("/result") public ResponseEntity<String> getResult() { try { // 等待500秒,和前面的读取超时保持一致 return entity.get(500, TimeUnit.SECONDS); } catch (Exception e) { throw new RuntimeException(e); } }
内容的提问来源于stack exchange,提问作者J. Abel

