Java案例如何处理请求超时?

wen java案例 13

本文目录导读:

Java案例如何处理请求超时?

  1. Spring Boot/WebFlux 异步请求超时
  2. RestTemplate HTTP 请求超时
  3. WebClient 响应式超时
  4. Servlet 容器配置(Tomcat)
  5. Spring Cloud OpenFeign 超时
  6. 数据库查询超时
  7. 全局超时异常处理
  8. 最佳实践建议

在Java中处理请求超时通常涉及以下几个方面,具体实现取决于你使用的框架和技术栈,以下是几种常见场景及处理方案:

Spring Boot/WebFlux 异步请求超时

使用 @Async 配合 Future

import java.util.concurrent.*;
@Service
public class AsyncService {
    @Async
    public Future<String> slowOperation() {
        // 模拟耗时操作
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return new AsyncResult<>("完成");
    }
}
// 控制器中处理超时
@RestController
public class TimeoutController {
    @Autowired
    private AsyncService service;
    @GetMapping("/timeout-demo")
    public String handleTimeout() {
        Future<String> future = service.slowOperation();
        try {
            // 设置2秒超时
            return future.get(2, TimeUnit.SECONDS);
        } catch (TimeoutException e) {
            future.cancel(true);
            return "请求超时";
        } catch (Exception e) {
            return "处理异常:" + e.getMessage();
        }
    }
}

使用 CompletableFuture

@GetMapping("/completable-timeout")
public String handleCompletableTimeout() {
    CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "完成";
    });
    try {
        return future.orTimeout(2, TimeUnit.SECONDS)
                     .exceptionally(ex -> "请求超时")
                     .get();
    } catch (Exception e) {
        return "处理失败";
    }
}

RestTemplate HTTP 请求超时

@Configuration
public class RestTemplateConfig {
    @Bean
    public RestTemplate restTemplate() {
        SimpleClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory();
        // 连接超时
        factory.setConnectTimeout(3000);
        // 读取超时
        factory.setReadTimeout(5000);
        return new RestTemplate(factory);
    }
}
// 使用
@Service
public class HttpService {
    @Autowired
    private RestTemplate restTemplate;
    public String callExternalApi() {
        try {
            return restTemplate.getForObject(
                "https://example.com/slow-api", 
                String.class
            );
        } catch (ResourceAccessException e) {
            return "外部服务超时:" + e.getMessage();
        }
    }
}

WebClient 响应式超时

@Service
public class ReactiveService {
    public Mono<String> callWithTimeout() {
        return WebClient.create("https://example.com")
            .get()
            .uri("/slow-api")
            .retrieve()
            .bodyToMono(String.class)
            .timeout(Duration.ofSeconds(3))  // 整体超时
            .onErrorResume(TimeoutException.class, e -> 
                Mono.just("请求超时(响应式)"))
            .onErrorResume(Exception.class, e -> 
                Mono.just("其他错误:" + e.getMessage()));
    }
}
// 控制器
@RestController
public class WebClientController {
    @Autowired
    private ReactiveService service;
    @GetMapping("/reactive-timeout")
    public Mono<String> handleReactiveTimeout() {
        return service.callWithTimeout();
    }
}

Servlet 容器配置(Tomcat)

application.yml

server:
  tomcat:
    connection-timeout: 3000    # 连接超时(毫秒)
    max-connections: 10000      # 最大连接数
    max-threads: 200           # 最大线程数
  servlet:
    session:
      timeout: 30m             # Session超时

全局 Filter 处理

@Component
public class TimeoutFilter implements Filter {
    @Override
    public void doFilter(ServletRequest request, ServletResponse response, 
                        FilterChain chain) throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        long timeout = 5000; // 5秒
        // 设置请求超时
        httpRequest.startAsync();
        httpRequest.getAsyncContext().setTimeout(timeout);
        chain.doFilter(request, response);
    }
}

Spring Cloud OpenFeign 超时

配置类

feign:
  client:
    config:
      default:
        connectTimeout: 3000    # 连接超时
        readTimeout: 5000       # 读取超时
        loggerLevel: basic
      specific-service:        # 特定服务配置
        connectTimeout: 5000
        readTimeout: 10000

代码配置

@Configuration
public class FeignConfig {
    @Bean
    public Request.Options options() {
        return new Request.Options(
            3000, // 连接超时
            TimeUnit.MILLISECONDS,
            5000, // 读取超时
            TimeUnit.MILLISECONDS,
            true // 跟随重定向
        );
    }
}
@FeignClient(name = "example-service", configuration = FeignConfig.class)
public interface ExampleClient {
    @GetMapping("/api/data")
    String getData();
}

数据库查询超时

JDBC 超时设置

@Configuration
public class DataSourceConfig {
    @Bean
    public DataSource dataSource() {
        HikariConfig config = new HikariConfig();
        config.setConnectionTimeout(3000);    // 连接超时
        config.setConnectionTestTimeout(3000); // 连接测试超时
        config.setMaxLifetime(1800000);       // 连接最大生命周期
        return new HikariDataSource(config);
    }
}
// JPA 查询超时设置
@Repository
public class UserRepository {
    @PersistenceContext
    private EntityManager entityManager;
    public List<User> findUsersWithTimeout() {
        Query query = entityManager.createQuery("SELECT u FROM User u");
        query.setHint("javax.persistence.query.timeout", 5000); // 5秒
        return query.getResultList();
    }
}

全局超时异常处理

@ControllerAdvice
public class GlobalExceptionHandler {
    @ExceptionHandler(TimeoutException.class)
    @ResponseStatus(HttpStatus.REQUEST_TIMEOUT)
    @ResponseBody
    public Map<String, Object> handleTimeout(TimeoutException e) {
        Map<String, Object> result = new HashMap<>();
        result.put("code", 408);
        result.put("message", "请求超时");
        result.put("timestamp", System.currentTimeMillis());
        return result;
    }
    @ExceptionHandler(ResourceAccessException.class)
    @ResponseStatus(HttpStatus.REQUEST_TIMEOUT)
    @ResponseBody
    public Map<String, Object> handleResourceTimeout(ResourceAccessException e) {
        Map<String, Object> result = new HashMap<>();
        result.put("code", 408);
        result.put("message", "外部服务超时:" + e.getMessage());
        return result;
    }
}

最佳实践建议

  1. 分层设置超时:对不同层级设置不同的超时时间(网络层>服务层>数据库层)
  2. 合理设置超时值
    • 连接超时:3000-5000ms
    • 读取超时:5000-10000ms
    • 业务处理超时:根据业务复杂度设定
  3. 使用断路器:结合 Hystrix/Resilience4j 防止雪崩效应
  4. 超时后处理
    • 返回默认值或缓存数据
    • 异步补偿处理
    • 记录日志用于监控
// Resilience4j 断路器示例
@CircuitBreaker(name = "externalService", fallbackMethod = "fallback")
@TimeLimiter(name = "externalService", fallbackMethod = "fallback")
public CompletableFuture<String> callExternalService() {
    return CompletableFuture.supplyAsync(() -> {
        // 调用外部服务
        return restTemplate.getForObject("...", String.class);
    });
}
public CompletableFuture<String> fallback(TimeoutException e) {
    return CompletableFuture.completedFuture("服务超时,使用降级数据");
}

选择哪种方案取决于你的应用架构、性能要求和业务场景,建议在生产环境中结合监控系统(如Prometheus)来动态调整超时参数。

抱歉,评论功能暂时关闭!