网站备案说明,wordpress代码块,成都丁香人才网官网专区,阿里云成功备案的网站增加域名某个简单的 python 服务#xff0c;运行一段时间就崩溃。查看日志后发现有一个系统错误
OSError: Errno24 Too many open files要理解这个问题#xff0c;首先要理解什么是文件描述符#xff0c;可以参考我的另一篇文章(Linux 系统文件描述符#xff08;File Descriptor运行一段时间就崩溃。查看日志后发现有一个系统错误
OSError: Errno24 Too many open files要理解这个问题首先要理解什么是文件描述符可以参考我的另一篇文章(Linux 系统文件描述符File Descriptor小白级介绍).
简言之就是 linux 系统每个进程会维护一张文件描述符表而这个表是有大小限制的用来控制每个进程可以打开的最多文件描述符数量。
如果一个进程打开了过多的文件当再次尝试打开文件时就会因为系统限制产生 “Too many open files” 的错误。
1. 解决思路
问题产生时不是事前和事后而是事中查看当前进程打开了哪些文件描述符结合程序逻辑分析是正常情况还是异常情况正常情况查看和修改文件描述符数量限制异常情况 查看是否有未正确关闭的资源使用文件、网络、数据库是否代码异常导致过多的打开文件例如循环中打开了过多的文件
1.1 如何查看当前进程打开的文件描述符信息
# 查看指定进程打开的文件描述符
lsof -p pid# 统计打开的文件描述符数量
lsof -p pid | wc -l
详细参考
1.2 如何查看和修改单进程文件描述符数量限制
查看ulimit -n临时修改ulimit -n 数值仅作用于当前的 shell永久修改编辑文件 /etc/security/limits.conf按照如下格式编辑* soft nofile 65535
* hard nofile 65535第一列 * 表示适用于所有用户也可以指定具体用户名或用户组名使用 用户组名 第二列 soft/hard soft软限制用户可以临时超过这个限制但会收到警告hard硬限制是绝对的上限用户不能超过这个值soft 值不能超过 hard 值 第三列 nofile nofile 表示进程可以打开的最大文件描述符数量这是最常见的限制之一 。 第四列具体的限制数值
2. 我们的案例分析
分析打开的文件描述符
存在大量如下文件描述符
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
pt_main_t 761309 root 872u unix 0xffff8f8a0e827800 0t0 44797806 typeSTREAM
pt_main_t 761309 root 873u IPv4 44840653 0t0 TCP Ip136-Gpu3080Ti:5000-Ip136-Gpu3080Ti:40474 (CLOSE_WAIT)
pt_main_t 761309 root 874u unix 0xffff8f890929d000 0t0 44827096 typeSTREAM
pt_main_t 761309 root 875u unix 0xffff8f8909299000 0t0 44827097 typeSTREAM
pt_main_t 761309 root 876u a_inode 0,14 0 53 [eventpoll]
pt_main_t 761309 root 877u sock 0,8 0t0 44827098 protocol: TCP
pt_main_t 761309 root 878u a_inode 0,14 0 53 [eventpoll]
pt_main_t 761309 root 879u unix 0xffff8f8a486f3800 0t0 44844081 typeSTREAM
pt_main_t 761309 root 880u unix 0xffff8f8a486f3c00 0t0 44844082 typeSTREAM
pt_main_t 761309 root 881u unix 0xffff8f890bb65400 0t0 44781151 typeSTREAM
pt_main_t 761309 root 882u IPv4 44797807 0t0 TCP Ip136-Gpu3080Ti:5000-Ip136-Gpu3080Ti:53684 (CLOSE_WAIT)
pt_main_t 761309 root 883u a_inode 0,14 0 53 [eventpoll]
pt_main_t 761309 root 884u unix 0xffff8f89322da400 0t0 44837562 typeSTREAM
pt_main_t 761309 root 885u unix 0xffff8f89322da000 0t0 44837563 typeSTREAM
pt_main_t 761309 root 886u unix 0xffff8f890bb66000 0t0 44781152 typeSTREAM
pt_main_t 761309 root 887u IPv4 44781154 0t0 TCP Ip136-Gpu3080Ti:5000-Ip136-Gpu3080Ti:52002 (CLOSE_WAIT)
pt_main_t 761309 root 888u IPv4 44797845 0t0 TCP Ip136-Gpu3080Ti:5000-Ip136-Gpu3080Ti:47946 (CLOSE_WAIT)
pt_main_t 761309 root 889u a_inode 0,14 0 53 [eventpoll]
pt_main_t 761309 root 890u unix 0xffff8f89325a5800 0t0 44840654 typeSTREAM
pt_main_t 761309 root 891u unix 0xffff8f89325a1400 0t0 44840655 typeSTREAM
pt_main_t 761309 root 892u IPv4 44837817 0t0 TCP Ip136-Gpu3080Ti:5000-Ip136-Gpu3080Ti:38346 (CLOSE_WAIT)
pt_main_t 761309 root 893u a_inode 0,14 0 53 [eventpoll]
pt_main_t 761309 root 894u unix 0xffff8f89325a4800 0t0 44840657 typeSTREAM
pt_main_t 761309 root 895u unix 0xffff8f89325a6400 0t0 44840658 typeSTREAM
pt_main_t 761309 root 896u IPv4 44845300 0t0 TCP Ip136-Gpu3080Ti:5000-Ip136-Gpu3080Ti:41238 (CLOSE_WAIT)
pt_main_t 761309 root 897u a_inode 0,14 0 53 [eventpoll]
pt_main_t 761309 root 898u unix 0xffff8f8909298c00 0t0 44827142 typeSTREAM
pt_main_t 761309 root 899u unix 0xffff8f890929cc00 0t0 44827143 typeSTREAM
pt_main_t 761309 root 900u sock 0,8 0t0 44827144 protocol: TCP
pt_main_t 761309 root 901u a_inode 0,14 0 53 [eventpoll]
pt_main_t 761309 root 902u unix 0xffff8f8a0e09f800 0t0 44836864 typeSTREAM
pt_main_t 761309 root 903u unix 0xffff8f8a0e098800 0t0 44845057 typeSTREAM
pt_main_t 761309 root 904u IPv4 44824331 0t0 TCP Ip136-Gpu3080Ti:5000-Ip136-Gpu3080Ti:38008 (CLOSE_WAIT)
pt_main_t 761309 root 905u a_inode 0,14 0 53 [eventpoll]
pt_main_t 761309 root 906u unix 0xffff8f8909298000 0t0 44827145 typeSTREAM
pt_main_t 761309 root 907u unix 0xffff8f890929bc00 0t0 44827146 typeSTREAM
pt_main_t 761309 root 908u IPv4 44827147 0t0 TCP Ip136-Gpu3080Ti:5000-Ip136-Gpu3080Ti:40656 (CLOSE_WAIT)我们发现这里大概有 3 类文件描述符
pt_main_t 761309 root 905u a_inode 0,14 0 53 [eventpoll] 这是一个 eventpoll 事件触发pt_main_t 761309 root 906u unix 0xffff8f8909298000 0t0 44827145 typeSTREAM 这是 unix 套接字用于进程间通信pt_main_t 761309 root 908u IPv4 44827147 0t0 TCP Ip136-Gpu3080Ti:5000-Ip136-Gpu3080Ti:40656 (CLOSE_WAIT) 这是一个未被正确关闭的 tcp 请求本地 5000 端口是我们的服务端口之所以是本地请求是因为我们做了一个内网穿透所以是本地请求本地
我们的服务作用是接收图片打开后调用模型处理然后返回处理结果。 但是代码中并未直接调用 PIL.Image 对象的 close 方法关闭资源。 同时我观察到线上会有超时客户端主动断开链接的行为。
所以关于上面的问题我有这样一个猜测
Flask 默认使用的是 Werkzeug 提供的 WSGI 服务器它是一个同步的服务器不直接支持异步处理。当一个请求到达时,Flask 会为该请求创建一个新的 eventloop(事件循环)来处理该请求。这就解释了为什么在文件描述符中会看到 eventloop 的出现。每个请求都有自己独立的 eventloop用于处理该请求的异步任务。未关闭的 Image 对象与连接断开在我的代码中当通过 Image.open() 打开图片时,会创建一个 Image 对象,它占用了一定的资源,如内存和文件描述符。如果在处理完图片后没有显式地调用 image.close() 关闭 Image 对象,那么这些资源会一直保持打开状态。在对象销毁时会触发资源关闭但是这依赖于垃圾回收。个人推测不知道是否靠谱 当客户端因为某些原因(如超时、网络问题等)断开连接时服务器端的请求处理可能还未完成特别是在处理大图片或多个图片时。如果此时 Image 对象还未关闭那么与该请求相关的 eventloop 和文件描述符(如之前的 5000 端口相关的文件描述符)也无法得到及时释放。导致了资源泄漏并且随着时间的推移和请求的增多这种情况会变得越来越严重。连接的 CLOSE_WAIT 状态当客户端主动关闭连接时服务器端会收到一个 FIN 包表示客户端已经完成了数据发送。服务器端接收到 FIN 包后会发送一个 ACK 包给客户端同时将连接状态改为 CLOSE_WAIT表示服务器端正在等待关闭连接。 如果服务器端的请求处理还未完成并且 Image 对象和相关资源还未释放那么连接会一直保持在 CLOSE_WAIT 状态直到服务器端完成处理并关闭连接。 上面的分析是我事后的推理正确性还有待确认不过最近没什么时间。
当时我改了如下两个地方这个问题就没有了
手动关闭 Image 对象调用 close 方法将 aiohttp.ClientSession 设置为全局对象这是官方文档的建议。
但并没有尝试将 Werkzeug 替换为支持异步处理的 Web 框架如 Sanic、FastAPI 等。
总结
一定要记得释放文件资源网络连接、文件、图片等不要瞬时大量使用文件资源