通过SSH触发Shell脚本启动的Python服务,4次连接后失败
核心问题定位
通过SSH远程触发启动的Python服务(端口12321),客户端前3次连接正常,第4次失败;但手动登录服务器启动服务无此问题,调整SSH存活参数后仍未解决。问题大概率出在SSH会话环境对服务进程的影响,或服务自身的连接/资源配置上。
排查步骤
查看服务日志
检查Python服务的运行日志,重点看第4次连接失败时的报错信息,比如是否出现Too many open files(文件描述符耗尽)、Connection reset by peer(连接被重置)或监听队列溢出的提示。对比进程环境差异
- 手动启动服务后,执行
ps auxe | grep <python进程PID>记录环境变量; - 通过SSH启动服务后,执行同样命令,对比两者的环境变量(如
PATH、LD_LIBRARY_PATH)和资源限制(如ulimit -n)是否一致。
- 手动启动服务后,执行
检查进程组归属
执行pstree -p <SSH会话的PID>,查看Python服务是否属于SSH会话的进程组。如果是,SSH会话结束时可能会向进程组发送信号,导致服务的资源被隐性限制。监听端口状态检查
执行ss -tulpn | grep 12321,查看:- 服务是否处于
LISTEN状态,且绑定了正确的IP(不要只绑定127.0.0.1); Recv-Q数值是否接近Send-Q(监听队列大小),如果是说明队列已满,新连接无法进入。
- 服务是否处于
修复方案
让服务脱离SSH会话环境
在ssh_remote_tools.sh中启动Python服务时,使用nohup或setsid将服务进程与SSH会话解绑,避免会话退出影响服务:# 使用nohup并输出日志 nohup python3 your_service_script.py > /var/log/remote_service.log 2>&1 & # 或使用setsid setsid python3 your_service_script.py &调大服务监听队列
如果Python服务使用socket.listen(),默认backlog值较小(通常为5),可以调大该值,比如:# 示例:将监听队列设置为10 server_socket.listen(10)这样能容纳更多等待处理的连接,避免前3次连接占满队列导致第4次失败。
统一资源限制与环境变量
在ssh_remote_tools.sh开头添加资源限制配置,确保与手动登录环境一致:# 调整文件描述符上限 ulimit -n 1024 # 手动设置必要环境变量,比如Python路径 export PATH=/usr/local/bin:$PATH检查服务连接泄漏
确认Python服务在处理完客户端连接后,是否正确关闭了socket。执行lsof -p <python进程PID>查看打开的文件描述符数量,如果连接次数增加后数量持续上升,说明存在连接泄漏,需要在代码中补充socket.close()逻辑。
内容的提问来源于stack exchange,提问作者Ponreegan G

