Files

91 lines
5.7 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 美团券 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` |
## 怎么看在不在跑 / 健康
```bash
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` 在最近 12 小时内、`total` 两三千、日志最近一轮是 `本轮完成`(不是 `Permission denied` / Traceback)。
## 怎么启动
定时任务已在 `cps` 的 crontab 里常驻,**随系统 cron 服务自动运行,无需手动启动**。若 crontab 被清空、要重新装回:`crontab -e`,粘下面这行存盘。
```cron
# 根治版(锁文件已改到 /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
```
**手动立即跑一轮**(不等下一个整点,比如刚发现库空):
```bash
cd /opt/shaguabijia-app-server && .venv/bin/python -m scripts.pull_meituan_coupons --once --prune-hours 24
```
前台跑、约 140 秒,出现 `本轮完成` 即成功。
## 怎么关闭(停用定时任务)
```bash
crontab -e
```
在那一行最前面加 `#` 注释掉(**推荐**,保留备查),存盘即停;要彻底删就删那一行。
> ⚠️ 别用 `crontab -r`——它会**清空你所有 cron**,不只这一条。
## 怎么终止(杀掉正在跑的那一轮)
```bash
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:
```cron
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` 下的代码。