当出现 504 错误码时,表示请求超时,服务器无法及时响应请求,需要检查下应用是否有什么耗时的操作,比如是否出现了 SQL 慢查询、是否接口发生死循环、是否出现死锁等,同时需要关注服务器系统负载高不高。
网络异常
接口原本好好的,突然出现超时,最常见的原因可能是网络出现异常,比如:偶然的网络抖动,或者是带宽被占满了。
所以对于有些高并发的请求场景,需要评估是否增加服务器带宽。
线程池满了
API 接口有时为了性能考虑,可能会使用线程池异步查询数据,然后把查询结果进行汇总,最后再返回。
但如果用户请求太多,线程池中已有的线程处理不过来,线程池会把多余的请求,放到队列中排队,等待空闲线程去处理。
如果队列中排队的任务非常多,某次 API 请求一直在等待,没办法得到及时处理,就会出现接口超时。
这时候,可以考虑是否是核心线程数设置的太小了,或者有多种业务场景共用了同一个线程池。
数据库死锁
假设 API 接口中通过某个 id 更新某条数据时,正好线上在手动执行一个批量更新数据的 SQL 语句。
该 SQL 语句在一个事务当中,并且刚好也在更新那条数据,此时可能就会出现死锁的情况。
当死锁发生时,数据库会将一个事务暂停,直到与其互相竞争资源的事务释放资源。这样就会导致 API 接口的更新数据操作长时间被数据库锁住,无法及时返回数据,从而出现接口超时的情况。
为了避免这种情况发生,可以考虑以下几个解决方案。
传入参数太多
有时候偶尔的一次接口超时,是由于参数传入太多导致的。
例如:根据 id 集合批量查询分类接口,如果传入的 id 集合数据量不多,那么不会出现性能问题。毕竟 id 是分类表的主键,可以走主键索引,数据库的查找速度是非常快的。
但如果接口调用方,一次性传入几千个,甚至几万个 id,批量查询分类,也可能会出现接口超时。
因为数据库在执行 SQL 语句前,会评估一下耗时情况,查询条件太多,有可能会判断走全表扫描会更快。
这种情况下 SQL 语句可能会不走索引,从而出现接口超时。
因此在设计批量接口的时候,建议要限制传入的参数集合的大小,如果超过设置的最大参数集合的大小,则接口直接返回失败,并进行友好提示。
超时时间设置过短
通常情况下,建议在调用远程 API 接口时,设置连接超时时间和读超时时间,并且可以动态配置。
连接超时时间指的是在建立网络连接时等待的时间。当客户端尝试连接到服务器时,如果服务器在指定的时间内没有响应,那么连接将被视为超时。这可以是由于网络故障、服务器负载过高或其他原因导致的延迟。连接超时时间的目的是确保在合理时间内建立连接,以防止客户端一直等待而无法进行后续操作。
读超时时间指的是在接收数据时等待的时间。当客户端从服务器请求数据时,如果在指定的时间内未收到任何数据,那么读操作将被视为超时。这可能是由于网络故障、服务器响应延迟或其他原因导致的数据获取延迟。读超时时间的目的是确保客户端能够在合理时间内获取到所需的数据,以避免无限期地等待。
这样做可以防止在调用远程 API 接口时,万一出现性能问题,响应时间很长,避免把服务拖挂的情况。
比如:调用的远程 API 接口,要 100 秒才能返回数据,而设置的超时时间是 100 秒。假设此时 1000 个请求去调用该接口,会导致线程池很快被占满,导致整个服务暂时不可用,至少新的请求过来时是无法及时响应的。
所以需要设置超时时间,同时超时时间也不能设置太长。
一次性返回数据太多
死循环或无限递归
SQL 语句没走索引
服务 OOM
本地 debug 模式
比如:本地开发时直连测试数据库并调用某个 API 接口的业务逻辑,同时开启 debug 模式,在更新某条数据时,假设此时他人也在操作更新相同的数据,就有可能会出现数据库死锁。
参考资料