Python脚本命令行运行正常 经PHP exec调用启动异常排查
问题诱因与排查方案
核心可能诱因
- 运行身份权限不匹配:命令行手动启动脚本时,继承的是当前登录用户的全部权限、环境变量、资源访问资格;但PHP通过
exec()触发时,默认以Web服务(php-fpm/Apache/Nginx)的运行用户身份执行,该用户通常权限受限,既没有你个人用户的环境配置、文件读写权限,也可能不在数据库的访问白名单内,甚至没有网络访问权限。 - 运行上下文不一致:手动启动时工作目录是脚本所在的
project_x/src/,PHP调用时工作目录通常是Web根目录或者exe所在的project_x/exec/,如果脚本里用了相对路径读配置、导模块,会直接找不到目标资源;同时非交互环境下标准输出、标准错误不会直接打印,你之前靠print输出的数据库连接错误根本看不到。 - multiprocessing兼容问题:Windows环境下multiprocessing默认用
spawn模式启动子进程,子进程会重新导入整个主脚本,如果逻辑没有严格收敛在if __name__ == "__main__"块内、或者子进程继承不到必要的环境参数,很容易出现子进程启动死锁、程序卡住不退出的问题;如果是打包后的exe,spawn模式下的路径解析错误还会导致子进程反复重启。另外你当前代码里数据库连接是在父进程创建的,这类连接对象不能跨进程复用,子进程拿到失效连接直接报错,被你写的裸except吞掉后就会返回False。 - 数据库连接逻辑缺陷:你当前的代码即使捕获到数据库连接错误,也没有中断流程,会继续拿着
None值的connection对象去执行pd.read_sql_query,直接触发异常;如果ID列表读取失败返回空值,pool.map处理空序列时在部分Python版本下也会出现卡住的问题。 - 日志权限问题本质也是身份差异:Web运行用户没有你个人用户目录的写权限,所以你之前写日志会报权限错,拿不到有效报错信息。
排查步骤
- 第一步先解决可观测性问题,不要裸吞异常:
把日志直接写到系统临时目录(Windows对应C:\Windows\Temp,Linux/macOS对应/tmp),所有异常必须记录完整栈信息,不要只写个空except返回False,参考修改:
包括数据库连接、SQL查询、进程池初始化的所有环节,只要有异常全往这个日志文件里写,不要用import traceback import os LOG_PATH = "/tmp/get_data_error.log" if os.name != "nt" else "C:\\Windows\\Temp\\get_data_error.log" def wrapper_fun(task_id): try: calculations(task_id) except Exception: with open(LOG_PATH, "a", encoding="utf-8") as f: f.write(f"处理ID {task_id} 失败:\n{traceback.format_exc()}\n==========\n") return Falseprint,非交互环境下print的内容不会被留存。 - 第二步校验运行环境差异:
在脚本最开头加日志,打印当前工作目录os.getcwd()、当前运行用户、全量环境变量,和你手动命令行启动时打印的结果做对比,只要有不一致的地方先对齐,比如路径问题就全改成绝对路径,环境变量缺失就显式在脚本里写死,不要依赖继承。 - 第三步修复multiprocessing逻辑问题:
- 所有进程池初始化、任务分发的逻辑必须严格放在
if __name__ == "__main__"块内,Windows下显式指定进程启动模式,避免子进程递归启动:if __name__ == "__main__": multiprocessing.set_start_method("spawn" if os.name == "nt" else "fork") pool = multiprocessing.Pool(processes=os.cpu_count()) # 后续逻辑 - 禁止在父进程创建数据库连接后传给子进程使用,数据库连接必须在每个子进程的任务函数内部单独创建、单独释放,避免跨进程复用连接导致的死锁、失效问题。
- 所有进程池初始化、任务分发的逻辑必须严格放在
- 第四步验证数据库连通性:
单独写一段连接测试逻辑,连接失败直接把错误码、错误信息全打日志,重点核对:数据库是否允许Web运行用户所在的IP/主机连接、账号密码是否硬编码正确(不要从相对路径的配置文件里读)、本地socket连接是否对Web用户开放权限。 - 第五步跳过exe中转排查:
先不要用exe做调用层,直接在PHP的exec里写全Python解释器的绝对路径、脚本的绝对路径,比如exec("C:\Python310\python.exe D:\project_x\src\get_data.py"),排除exe本身的权限、路径解析问题,等脚本跑通了再套exe。
额外提醒:你贴的代码里有明显的变量错误,定义的密码变量是
passt,连接的时候传的是user_password,手动跑如果没问题的话大概率是贴代码时的笔误,但也要核对下实际运行的代码里有没有这类变量名写错、pool未定义就调用的低级错误。
内容的提问来源于stack exchange,提问作者Beepo
相关产品推荐
相关产品推荐

