You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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,参考修改:
    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 False
    
    包括数据库连接、SQL查询、进程池初始化的所有环节,只要有异常全往这个日志文件里写,不要用print,非交互环境下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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 01:04:08