当脚本需被他人调用、定时运行、带状态或频繁用curl触发时,即达服务化临界点;核心标志是协作方式变化——如运维监控、前端轮询、下游依赖返回,而非功能增多。

怎么判断脚本该拆成服务了
当 python script.py 开始需要别人调用、定时跑、带状态、或你发现自己在反复写 curl http://localhost:8000/trigger 时,就到了临界点。不是“功能变多”才要服务化,而是“协作方式变了”——比如运维要监控它、前端要轮询结果、另一个服务得等它返回才能继续。
常见错误现象:KeyboardInterrupt 频繁出现(手动 Ctrl+C 停止)、日志全打在终端里没法归集、修改一个参数就得改代码再重启。
- 真正触发服务化的信号是:有人开始问“这脚本能异步跑吗?”“能加个 API 吗?”“失败了怎么重试?”
- 如果只是本地数据清洗 + 一次性导出 Excel,别急着上 FastAPI ——
argparse+logging.basicConfig足够 - 注意兼容性陷阱:原脚本用
os.chdir()切工作目录?服务化后路径可能错乱,优先改用pathlib.Path(__file__).parent
用什么框架起步最不踩坑
别从零写 HTTP server,也别一上来就上 Celery + Redis。90% 的过渡场景,Flask 或 FastAPI 单文件启动就够了,关键是把原逻辑封装成可测试的函数。
使用场景差异:
立即学习“Python免费学习笔记(深入)”;
- 要快速暴露一个 POST 接口接收 JSON 并返回结构化结果 → 选
FastAPI(自动校验+文档) - 只做简单定时任务调度,或已有大量
requests/subprocess调用 →Flask更轻量,调试更直观 - 千万别在
@app.route里直接写 200 行原脚本逻辑 —— 提前抽成run_job(input_data: dict) -> dict
性能提示:单核 CPU 上,uvicorn 默认只起一个 worker;并发高时记得加 --workers 4,但先确认你的业务代码本身是不是线程安全。
环境变量和配置怎么管才不乱
脚本时代靠 config.py 或硬编码,服务化后必须剥离。所有外部依赖(数据库地址、API key、超时时间)都得走环境变量,否则部署到 Docker 或 K8s 会立刻崩。
容易踩的坑:
-
os.environ.get('DB_URL')返回None?没设环境变量,但程序没报错继续跑 → 在入口加assert os.environ.get('DB_URL'), 'DB_URL required' - 开发时用
.env文件,但生产环境禁止加载它 ——python-dotenv默认只在开发启用 - 敏感配置(如密钥)绝不能进 Git,用
pydantic.BaseSettings自动从环境变量加载,字段名全大写,类型自动转换
示例:class Settings(BaseSettings): TIMEOUT_SEC: int = 30; LOG_LEVEL: str = "INFO"
日志、错误、进程管理怎么落地
脚本打印 print("done") 没问题,服务化后必须用 logging,且日志要能被 systemd 或 Docker 捕获。错误不能吞掉,尤其 ConnectionError 和 JSONDecodeError 这类外部依赖失败。
- 避免
try: ... except: pass—— 至少except Exception as e: logger.exception("job failed") - Docker 中用
stdout输出日志,别写文件;用logging.StreamHandler()直接输出到控制台 - 进程别用
nohup python app.py &—— 改用gunicorn(Flask)或uvicorn(FastAPI)管理 worker 生命周期 - 健康检查接口必须有:
@app.get("/healthz")只返回{"status": "ok"},不查 DB 不发请求
复杂点在于:服务化不是换个启动方式,而是把原来“人驱动”的执行流,变成“事件/协议驱动”的状态机。很多坑不在代码里,在你怎么定义失败、谁负责重试、下游怎么感知超时。










