spring boot超时控制

Spring Boot 项目中,如果你需要调用外部接口但又不希望前端等待过长时间(如有超时限制),可以在后端设置调用超时时间。常见的方法包括使用 RestTemplateWebClient 配合超时控制,或者异步调用配合 Timeout 限制。以下是几种常用方式:

1 RestTemplate

1.1 RestTemplate基本信息

RestTemplateSpring 提供的 同步阻塞式 HTTP 客户端,用于在服务之间发起 RESTful API 调用(GETPOSTPUT 等)。

特性 是否支持 说明
异步请求 ❌ 不支持 同步阻塞
响应式编程支持 ❌ 不支持 不能与 Mono/Flux 联动
性能(高并发) ❌ 一般 线程资源浪费多
编程风格 ✅ 简单 类似早期 Java HttpClient
状态 ☠️ 已被官方标记为“不推荐继续使用” 建议迁移到 WebClient

❗ RestTemplate 被标记为过时原因:
Spring 官方文档建议:

👉 “RestTemplate is in maintenance mode. For new development, prefer WebClient.”

  • 维护模式,仅修 bug,不再增加新特性;
  • 无法满足异步、非阻塞、高并发应用场景;
  • 不支持响应式链式编程(无法和 WebFlux 配合使用);

✅ 推荐替代者:WebClient
WebClient 支持:

能力 是否支持 示例
异步非阻塞 ✅ 支持 .subscribe() / .block()
响应式链式组合 ✅ 支持 .flatMap() / .zip()
多线程复用 ✅ 支持 Reactor Netty 驱动
适合高并发场景 ✅ 非常适合 API 聚合、网关服务、微服务

✅ 总结

工具 是否阻塞 响应式支持 官方推荐状态
RestTemplate ✅ 阻塞 ❌ 无 ☠️ 已过时(维护模式)
WebClient ❌ 非阻塞 ✅ 完全支持 ✅ 推荐

1.2 RestTemplate实现超时控制

所以,如果是新项目的话就不要使用 RestTemplate 了,对于旧的项目,可以使用下面的逻辑进行优化。

✅ 1. 配置类(Config)

@Configuration
public class HttpClientConfig {

    @Bean
    public RestTemplate restTemplate() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        factory.setConnectTimeout(3000); // 连接超时 3 秒
        factory.setReadTimeout(3000);    // 读取超时 3 秒
        return new RestTemplate(factory);
    }
}

✅ 2. 服务类(Service)使用注入的 Bean 发起请求

@Service
public class ApiService {

    @Autowired
    private RestTemplate restTemplate;

    public String callExternalApiWithRestTemplate() {
        try {
            return restTemplate.getForObject("https://httpbin.org/delay/2", String.class);
        } catch (RestClientException e) {
            return "调用超时或异常:" + e.getMessage();
        }
    }
}

✅ 3. 控制器(Controller)调用服务

@RestController
@RequestMapping("/api")
public class ApiController {

    @Autowired
    private ApiService apiService;

    @GetMapping("/rest")
    public String useRestTemplate() {
        return apiService.callExternalApiWithRestTemplate();
    }
}

2 WebClient

在1中已经对比了 RestTemplateWebClient ,这里就不多赘述了。
要注意,WebClient 属于响应式编程(Reactive Programming),容器是不能使用 Tomcat 的,一般是 Netty,所以一般的业务服务中使用的场景也比较少,主要用在网关之类的功能服务中。下面是代码示例。

📦 1. 引入依赖(pom.xml)

<dependencies>
    <!-- 响应式 Web 框架 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>

    <!-- 可选:Reactor 工具类 -->
    <dependency>
        <groupId>io.projectreactor</groupId>
        <artifactId>reactor-tools</artifactId>
    </dependency>
</dependencies>

✅ 2. 配置类(Config)

@Configuration
public class HttpClientConfig {
    @Bean
    public WebClient webClient() {
        return WebClient.builder()
                .clientConnector(new ReactorClientHttpConnector(
                        HttpClient.create()
                                  .responseTimeout(Duration.ofSeconds(3))
                ))
                .build();
    }
}

✅ 3. 服务类(Service)使用注入的 Bean 发起请求

@Service
public class ApiService {
    @Autowired
    private WebClient webClient;

    public Mono<String> callExternalApiWithWebClient() {
        return webClient.get()
                .uri("https://httpbin.org/delay/2")
                .retrieve()
                .bodyToMono(String.class)
                .timeout(Duration.ofSeconds(3)) // 可选,再加一层逻辑超时
                .onErrorReturn("WebClient 请求超时或出错");
    }
}

✅ 4. 控制器(Controller)调用服务

@RestController
@RequestMapping("/api")
public class ApiController {

    @Autowired
    private ApiService apiService;

    @GetMapping("/webclient")
    public Mono<String> useWebClient() {
        return apiService.callExternalApiWithWebClient();
    }
}

🔍 补充说明

  • 传统 @RestController 仍然可以用,但方法要返回 Mono 或 Flux 才能非阻塞运行
  • 如果返回 String、List 等同步类型,其实还是阻塞执行,无法发挥 WebFlux 的优势

🎯 Spring Boot 2.7.x 响应式适合哪些场景?

  • 高并发 API 聚合服务(比如网关、BFF)
  • IO 密集型服务(外部 API、数据库请求)
  • 实时数据推送(如 SSE、WebSocket)
  • 替代线程池阻塞逻辑,实现节省线程资源

3 @Async + CompletableFuture

@Async
public CompletableFuture<String> callExternalApiAsync() {
    // 调用外部接口逻辑
    return CompletableFuture.completedFuture("response");
}

// 控制超时
String result;
try {
    result = callExternalApiAsync().get(3, TimeUnit.SECONDS);
} catch (TimeoutException e) {
    // 超时处理逻辑
}

🚫 在同一个类里自己调用自己的 @Async 方法是没有用的,原因是 Spring@Async 是通过 代理机制(AOP)实现的,你自己调自己会绕过代理。

4 ExecutorService

ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(() -> {
    // 外部接口调用逻辑
    return "response";
});

try {
    String result = future.get(3, TimeUnit.SECONDS);
} catch (TimeoutException e) {
    // 超时处理
}
executor.shutdown();
posted @ 2025-04-16 09:41  大唐冠军侯  阅读(964)  评论(0)    收藏  举报