Files
game_server/TIMEOUT_AND_BLOCKING_ISSUES_ANALYSIS.md

7.3 KiB
Raw Permalink Blame History

超时和阻塞问题分析与解决方案

问题概述

经过全面检查,发现以下几个可能导致超时和阻塞的问题:

1. WebClient 连接池配置缺失 ⚠️ 高优先级

问题描述

ScriptClient.java 中的 WebClient 没有配置底层 HTTP 客户端的连接池参数,可能导致:

  • 连接数耗尽,新请求被阻塞
  • 连接泄漏
  • 超时设置未生效

当前代码

this.webClient = WebClient.builder()
        .baseUrl(baseUrl)
        .exchangeStrategies(ExchangeStrategies.builder()
                .codecs(cfg -> cfg.defaultCodecs().maxInMemorySize(2 * 1024 * 1024))
                .build())
        .build();

解决方案

需要配置 Reactor Netty HttpClient

HttpClient httpClient = HttpClient.create()
        .option(ChannelOption.CONNECT_TIMEOUT_MILLIS, connectTimeoutMs)
        .responseTimeout(Duration.ofMillis(readTimeoutMs))
        .doOnConnected(conn -> 
            conn.addHandlerLast(new ReadTimeoutHandler(readTimeoutMs, TimeUnit.MILLISECONDS))
                .addHandlerLast(new WriteTimeoutHandler(connectTimeoutMs, TimeUnit.MILLISECONDS)))
        .wiretap(true); // 开启日志追踪

this.webClient = WebClient.builder()
        .baseUrl(baseUrl)
        .clientConnector(new ReactorClientHttpConnector(httpClient))
        .exchangeStrategies(ExchangeStrategies.builder()
                .codecs(cfg -> cfg.defaultCodecs().maxInMemorySize(2 * 1024 * 1024))
                .build())
        .build();

2. Tomcat 线程池配置缺失 ⚠️ 高优先级

问题描述

Spring Boot 内嵌 Tomcat 使用默认线程池配置,可能不足以处理高并发场景。

解决方案

application.yml 中添加:

server:
  port: 18080
  tomcat:
    threads:
      max: 200                    # 最大工作线程数
      min-spare: 20               # 最小空闲线程数
    max-connections: 10000        # 最大连接数
    accept-count: 100             # 队列长度
    connection-timeout: 20000     # 连接超时(ms)
  shutdown: graceful              # 优雅关闭
  
spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s  # 关闭超时

3. WebFlux 配置优化 ⚠️ 中优先级

问题描述

如果项目使用了 WebFlux需要配置 Reactor 线程池。

解决方案

application.yml 中添加:

spring:
  webflux:
    # WebFlux 相关配置(如果使用)
    base-path: /
  reactor:
    # Reactor 线程池配置
    netty:
      pool:
        type: elastic  # 使用弹性线程池

4. 数据库连接池优化 ⚠️ 中优先级

当前配置

hikari:
  maximum-pool-size: 50
  minimum-idle: 10
  connection-timeout: 30000      # 30秒
  idle-timeout: 600000           # 10分钟
  max-lifetime: 1800000          # 30分钟

问题

  • connection-timeout 设置为 30 秒过长,可能导致请求堆积
  • MySQL 锁等待超时设置为 30 秒也偏长

建议优化

hikari:
  maximum-pool-size: 80           # 增加连接池大小
  minimum-idle: 20                # 增加最小空闲连接
  connection-timeout: 10000       # 降低到10秒
  idle-timeout: 300000            # 5分钟
  max-lifetime: 1800000           # 30分钟
  leak-detection-threshold: 30000 # 30秒检测连接泄漏从60秒降低
  validation-timeout: 3000        # 降低到3秒

同时优化 MySQL 会话参数:

datasource:
  url: jdbc:mysql://...?sessionVariables=innodb_lock_wait_timeout=10,wait_timeout=300,interactive_timeout=300

5. 异步线程池拒绝策略优化 ⚠️ 中优先级

当前配置

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

问题

CallerRunsPolicy 会让调用线程(通常是 HTTP 线程)执行任务,可能导致 HTTP 线程池阻塞。

建议

根据业务需求选择:

方案 A丢弃策略不重要的任务

executor.setRejectedExecutionHandler(new ThreadPoolExecutor.DiscardOldestPolicy());

方案 B增加队列并监控重要任务

executor.setQueueCapacity(500);  // 从100增加到500
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
// 添加拒绝处理监控

6. 事务超时优化 ⚠️ 中优先级

问题

部分事务方法可能执行时间过长,持有数据库连接。

建议

  1. 为不同类型的事务设置不同超时:
@Transactional(timeout = 5)   // 快速查询/更新
@Transactional(timeout = 15)  // 批量操作
@Transactional(timeout = 30)  // 复杂业务逻辑
  1. 避免在事务中调用外部 HTTP 接口:
// ❌ 不好的做法
@Transactional
public Mono<Result> process() {
    // 事务内调用外部 HTTP
    return externalService.call().map(data -> {
        repository.save(data);  // 持有连接时间过长
    });
}

// ✅ 推荐做法
public Mono<Result> process() {
    // 先调用外部接口
    return externalService.call()
        .flatMap(data -> {
            // 再开启事务保存
            return Mono.fromCallable(() -> saveInTransaction(data))
                .subscribeOn(Schedulers.boundedElastic());
        });
}

@Transactional(timeout = 5)
private Result saveInTransaction(Data data) {
    return repository.save(data);
}

7. 添加请求超时和监控 ⚠️ 中优先级

建议配置

application.yml 中添加:

spring:
  mvc:
    async:
      request-timeout: 60000  # 60秒异步请求超时

management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics,threaddump,heapdump
  metrics:
    enable:
      tomcat: true
      hikaricp: true
      jvm: true

8. 日志优化建议

添加性能监控日志:

logging:
  level:
    reactor.netty: INFO                    # Reactor Netty 日志
    io.netty: INFO                         # Netty 日志
    com.zaxxer.hikari: INFO                # HikariCP 日志
    org.springframework.web.reactive: INFO # WebFlux 日志

实施优先级

立即实施(高优先级)

  1. 配置 WebClient 连接池和超时
  2. 配置 Tomcat 线程池
  3. 优化数据库连接池参数

短期实施(中优先级)

  1. 优化异步线程池拒绝策略
  2. 检查并优化事务边界
  3. 添加性能监控端点

长期优化

  1. 添加分布式追踪(如 Sleuth + Zipkin
  2. 添加断路器(如 Resilience4j
  3. 考虑使用 Redis 缓存热点数据

监控建议

1. 添加健康检查

@Component
public class CustomHealthIndicator implements HealthIndicator {
    @Override
    public Health health() {
        // 检查数据库连接
        // 检查外部服务
        // 检查线程池状态
        return Health.up().build();
    }
}

2. 添加性能指标

@Timed(value = "api.calls", description = "API调用耗时")
public Mono<Result> apiMethod() {
    // ...
}

3. 监控关键指标

  • HTTP 线程池使用率
  • 数据库连接池使用率
  • 异步线程池队列长度
  • 外部接口响应时间
  • 事务执行时间

总结

主要问题集中在:

  1. HTTP 客户端配置不完整 - 导致连接管理问题
  2. 服务器线程池未配置 - 默认值可能不适合生产环境
  3. 数据库连接和事务管理 - 超时设置偏保守
  4. 缺少监控和告警 - 难以及时发现问题

建议按优先级逐步实施优化措施。