Co-authored-by: chenshuobo <1119780489@qq.com> Reviewed-on: #38
5.7 KiB
美团券 ETL 定时任务 — 运维手册
对象:维护「美团 CPS 券抓取入库」这套定时任务的同事。 🔒 登录信息(服务器 IP / 账号密码 / DB 密码)见私密交接清单,不入库。 运行账号
cps无 sudo,本手册所有操作都不需要 root。
它是什么
每小时全量抓美团 CPS 券(北京)→ upsert 进 PostgreSQL 的 meituan_coupon 表,作为 App「销量最高 / 智能推荐」两个 tab 的数据源。一轮约 140 秒、~2700 条;靠 cps 用户的 crontab 常驻,关终端也照跑。
代码 / 文件在哪(都在生产服务器上)
| 项 | 路径 |
|---|---|
| 代码目录 | /opt/shaguabijia-app-server |
| 抓取脚本 | scripts/pull_meituan_coupons.py |
| Python(venv) | /opt/shaguabijia-app-server/.venv/bin/python |
| 配置(美团凭证 / DB 连接串) | /opt/shaguabijia-app-server/.env(MT_CPS_* / DATABASE_URL) |
| 日志 | /home/cps/meituan_etl.log |
| 运行锁 | /tmp/meituan_etl.lock(根治版)或 ~/data/.meituan_etl.lock(过渡版,见文末) |
| 定时配置 | cps 用户 crontab(crontab -l) |
| 入库表 | PostgreSQL shaguabijia 库的 meituan_coupon |
怎么看在不在跑 / 健康
crontab -l # 看定时任务(应有每小时第 17 分那条)
tail -n 20 ~/meituan_etl.log # 看最近几轮:本轮完成 / 入库条数 / 有无报错
# 查库存量与新鲜度(走 .env 连接串,不用输 DB 密码):
cd /opt/shaguabijia-app-server && .venv/bin/python -c "from sqlalchemy import text; from app.db.session import engine; print(engine.connect().execute(text('select count(*) total, count(*) filter (where sale_volume_num is not null) sales, max(last_seen) last_run from meituan_coupon')).one())"
健康标准:last_run 在最近 1–2 小时内、total 两三千、日志最近一轮是 本轮完成(不是 Permission denied / Traceback)。
怎么启动
定时任务已在 cps 的 crontab 里常驻,随系统 cron 服务自动运行,无需手动启动。若 crontab 被清空、要重新装回:crontab -e,粘下面这行存盘。
# 根治版(锁文件已改到 /tmp 后用这条,推荐):
17 * * * * cd /opt/shaguabijia-app-server && .venv/bin/python -m scripts.pull_meituan_coupons --once --prune-hours 24 >> /home/cps/meituan_etl.log 2>&1
手动立即跑一轮(不等下一个整点,比如刚发现库空):
cd /opt/shaguabijia-app-server && .venv/bin/python -m scripts.pull_meituan_coupons --once --prune-hours 24
前台跑、约 140 秒,出现 本轮完成 即成功。
怎么关闭(停用定时任务)
crontab -e
在那一行最前面加 # 注释掉(推荐,保留备查),存盘即停;要彻底删就删那一行。
⚠️ 别用
crontab -r——它会清空你所有 cron,不只这一条。
怎么终止(杀掉正在跑的那一轮)
pgrep -af pull_meituan_coupons # 查在跑的进程和 PID
pkill -f pull_meituan_coupons # 杀掉它(或 kill <PID>)
rm -f /tmp/meituan_etl.lock ~/data/.meituan_etl.lock # 清掉残留锁,否则下一轮可能"跳过本轮"
脚本有 30 分钟陈旧锁自动接管机制:即使没手动清锁,30 分钟后下一轮也会自愈;急着立刻重跑才需手动
rm锁。
调整频率
crontab -e 改那行最前面的 cron 表达式:
| 频率 | 表达式 |
|---|---|
| 每小时第 17 分(当前) | 17 * * * * |
| 每 30 分钟 | */30 * * * * |
| 每 2 小时 | 17 */2 * * * |
⚠️ 别调太密:一轮 ~140 秒,且要守美团限流(脚本已配速到 ~2–3 req/s,远低于 ~15 req/s 红线;402 内置退避重试)。每小时一轮足够,勿并行、勿调小页间 sleep。
脚本参数
--once:跑一轮(定时任务用这个)--loop --interval 600:常驻循环,每 600 秒一轮(调试用)--prune-hours N:清理超过 N 小时没再出现的旧券(默认 24;0=不清)- 锁文件位置可用环境变量
MEITUAN_ETL_LOCK=/path/to.lock覆盖
详见脚本头部注释:
head -30 scripts/pull_meituan_coupons.py
已知问题与根治(运行锁权限)
- 现象:cron 报
PermissionError: [Errno 13] ... 'data/.meituan_etl.lock',每轮抓不进数据、表为空。 - 根因:旧版脚本把运行锁建在代码目录的
data/下,而data/属 root、cps不可写(交接清单里"已给写权限"实际不成立)。 - 根治(已在代码层修复):脚本把锁默认改到
/tmp/meituan_etl.lock(任何账号可写),并支持MEITUAN_ETL_LOCK覆盖。部署该版本后,用上面「根治版」那条干净 crontab 即可。 - 过渡写法(根治版部署前的临时方案):crontab 里
cd /home/cps把锁落到家目录、再用PYTHONPATH让代码仍能 import:根治版上线后,把这条换回上面的「根治版」标准写法即可。17 * * * * cd /home/cps && PYTHONPATH=/opt/shaguabijia-app-server /opt/shaguabijia-app-server/.venv/bin/python -m scripts.pull_meituan_coupons --once --prune-hours 24 >> /home/cps/meituan_etl.log 2>&1
注意事项
- 只抓北京:脚本里
city_id硬编码,扩城市需改脚本。 - 表行数 > 单轮抓取数属正常:美团
product_view_sign会轮换,跨轮累积;查询侧有DISTINCT ON(dedup_key)按「品牌|名|价」去重,用户看到的不重复;旧券 24h 后被 prune 清掉。 - 别和 systemd 双跑:仓库
deploy/meituan-etl.{service,timer}是未来由 root 纳入 systemd 正规部署的备选,和本 user cron 二选一、别同时开(否则重复抓、浪费限流额度)。当前生产用 user cron。 - 改脚本 / 改部署:走 git + PR,让有 root 的人
/opt/deploy.sh部署(git reset+alembic upgrade+ 重启);cps账号改不了/opt下的代码。