最近项目上线后访问量大的时候经常报这个错,查阅资料发现原因是服务器在发送响应之前关闭了连接,或者请求超时连接被关闭。最开始以为超时导致,加入了重试机制,每隔一秒重试一次,发现有很慢的sql的时候,服务器cpu突然加剧,所以重试不可行。

Tomcat 默认配置

可以看到springboot内置tomcat的默认超时时间是20秒

server:
  tomcat:
    # 当所有可能的请求处理线程都在使用中时,传入连接请求的最大队列长度
    accept-count: 100
    # 服务器在任何给定时间接受和处理的最大连接数。一旦达到限制,操作系统仍然可以接受基于“acceptCount”属性的连接。
    max-connections: 8192
    threads:
      # 工作线程的最小数量,初始化时创建的线程数
      min-spare: 10
      # 工作线程的最大数量 io密集型建议10倍的cpu数,cpu密集型建议cpu数+1,绝大部分应用都是io密集型
      max: 200
    # 连接器在接受连接后等待显示请求 URI 行的时间。
    connection-timeout: 20000
    # 在关闭连接之前等待另一个 HTTP 请求的时间。如果未设置,则使用 connectionTimeout。设置为 -1 时不会超时。
    keep-alive-timeout: 20000
    # 在连接关闭之前可以进行流水线处理的最大HTTP请求数量。当设置为0或1时,禁用keep-alive和流水线处理。当设置为-1时,允许无限数量的流水线处理或keep-alive请求。 
    max-keep-alive-requests: 100

解决方案

  • 将获取连接策略由默认的FIFO变更为LIFO,因为LIFO能够确保获取的连接最大概率是最近刚被用过的,也就是热点连接始终是热点连接,而始终用不到的连接就可以被回收掉,LRU的思想。

  • 因为ServerProvider, GateWayConsumer,尽可能保证Consumer先于Provider断开连接,设置max-idle-time的时间不大于connection-timeout

ConnectionProvider.builder("custom")
                .maxConnections(50)
                .maxIdleTime(Duration.ofSeconds(18))
                .lifo()
                .build()

结论

设置生产环境已经没有这个错误了,已经解决了这个问题,但不排除主要原因还是服务端响应太慢,才会导致连接被关闭。