1、背景引入
这个接口是用户提现的接口,大概每天 12:30 14:30 大概有几百人同时发起提现请求,然后这些人属于同一公司。
然后我们服务进行一系列的校验,比如账户余额、请求参数啥的,人、公司都加了分布式锁,然后校验通过后就是生成任务,再去调用支付中心去扣款。
2、问题介绍
某天中午的时候,企业微信一监控群突然收到 14 条消息的告警,显示提现接口请求超时,因为这个接口设置的超时时间是 30s,说明这8 条请求超过 30s 了。
正常这个接口,统计了以往的响应耗时,其实最长也就 2s 内返回了,基本上都是 1s 内返回了。因为这个是老系统,这个超时设置暂不去研究是否合适。
3、问题排查
3.1 排查所有可能出现问题的地方
1、然后就去查看了线上日志,看 skywalking,发现当天有 324 条提现请求,大概有 14 条接口请求超时,还有一些的数据请求耗时在 10s、20s 以上,所以这些肯定是不正常的
2、看了机器的 cpu、内存、网络负载都很正常,没有明显有波动。
3、然后又看了 db 的慢查统计,也没有慢查。
4、 jstack、gc 都看了没有问题
所有的都看了,就是没有发现任何可能存在的问题.因为这个过程很长,里面打印了很多日志,分阶段统计了每个阶段的耗时,每个阶段也没有明显的问题。
然后就这样持续了 2 天。还是一样的问题,还是同样超时。每天都会有大概 10 条左右的超时
3.2 发现不一样的日志
直到第 3 天的时候发现了一个连接获取不到的异常。如下:
Caused by: java.sql.SQLTransientConnectionException: RalphPool - Connection is not available, request timed out after 10049ms,
从日志里可以看出来,获取 db 连接的时候,10s 还没有获取到连接,因为 hikari 默认获取连接超时时间设置的是 10s.
看到这个消息然后就有点怀疑,是不是连接池配置的有问题。
3.3 分析连接池配置
来看一下配置:
spring.datasource.hikari.minimum-idle = 10
spring.datasource.hikari.maximum-pool-size = 40
# 就是初始化的时候有 10 个连接,然后最大是 40 个。
然后就分析了这几天的连接信息:
看了日志,发现最大的连接数没有超过 40 个的, 最多 30 几个,所以连接数不应该是瓶颈呀。
但是有个问题就是,初始化连接数是 10 个,当一大批请求瞬间涌进的时候,10 个肯定是不够用的,会瞬时添加足够的连接去处理请求。所以想可不可以把初始化连接数加大一下。
还有下面的问题,同一个方法里的上下行的代码,中间有 6s 的耗时。猜肯定应该也是数据库连接获取的耗时。
然后看一下 Hikari 官方配置的一些说明:
hikari 官方配置也建议,minimum-idle 不要配置,或者跟 maximum-pool-size 保持一致。如果有高峰期的需要的话。
https://github.com/brettwooldridge/HikariCP
minimumIdle
This property controls the minimum number of idle connections that HikariCP tries to maintain in the pool. If the idle connections dip below this value and total connections in the pool are less than maximumPoolSize, HikariCP will make a best effort to add additional connections quickly and efficiently.
However, for maximum performance and responsiveness to spike demands, we recommend not setting this value and instead allowing HikariCP to act as a fixed size connection pool. Default: same as maximumPoolSize
但是,为了最大性能和响应峰值需求,我们建议不要设置这个值,而是允许HikariCP作为固定大小的连接池。默认值:与maximumPoolSize相同
所以基于此,我们把 Hikari 的配置调整了一下。初始化的大小数,调整为 30.调整后的配置如下:
spring.datasource.hikari.minimum-idle = 30
spring.datasource.hikari.maximum-pool-size = 200
修改配置后,上线第 2 天的时候,一切正常,而且最大响应时间不超过 2 s.至此问题得到解决。
4、总结
连接池的配置,正常 10 个都是够用的,但是遇到高峰期的时候,瞬时过来几百个请求,然后 10 个就明显不够,然后此时连接池就会去创建连接,创建连接的过程是很耗时的,所以就可能会导致连接使用紧张,再加上,本身代码里有锁,查询比较多,所以连接就会释放的慢,所以引发了连锁反应.
所以如果有瞬时很高的请求的时候,要基于业务来调整相应的数据库连接池配置。