375 lines
18 KiB
Markdown
375 lines
18 KiB
Markdown
|
|
# 部署手册(AGENTS.md 的详细参考篇)
|
|||
|
|
|
|||
|
|
> 本文件由 `AGENTS.md` 的「部署与维护」整段搬迁而来(**内容逐字保留,未删改**)。
|
|||
|
|
> 搬迁原因:`AGENTS.md` 超过工作区指令预算(65,536 B)会被按头部截断,尾部不加载。
|
|||
|
|
>
|
|||
|
|
> **判据与最致命的几条已留在 `AGENTS.md`**:改线上前先读那里的「部署前必看」+
|
|||
|
|
> 「部署注意事项」摘要;本文件给出**完整步骤、nginx 全文、以及全部 11 条注意事项**。
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 部署与维护
|
|||
|
|
|
|||
|
|
### SSH连接
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 外网IP(推荐)
|
|||
|
|
echo y | plink -ssh root@118.31.119.206 -pw "Hbhyg731024@"
|
|||
|
|
|
|||
|
|
# ZeroTier内网IP
|
|||
|
|
echo y | plink -ssh root@10.8.0.252 -pw "Hbhyg731024@"
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 数据库连接
|
|||
|
|
|
|||
|
|
⛔ **不要再用 `10.8.0.252` 连库** —— 服务器 nginx `stream{}` 转发已于 2026-09-20 移除,
|
|||
|
|
`10.8.0.252:3306/6379`(以及 `127.0.0.1` 回环)**都连不到了**(实测 ✅)。
|
|||
|
|
**所有本地数据库操作一律先起 SSH 隧道**(`pwsh tools/dev-tunnel.ps1`),再连 `127.0.0.1:13306/16379`。
|
|||
|
|
|
|||
|
|
**MySQL(通过 SSH 隧道):**
|
|||
|
|
```bash
|
|||
|
|
# 先起隧道(另一个终端常驻)
|
|||
|
|
pwsh tools/dev-tunnel.ps1
|
|||
|
|
|
|||
|
|
# 命令行
|
|||
|
|
mysql -h 127.0.0.1 -P 13306 -u root -p
|
|||
|
|
# 密码:Hbhyg731024@
|
|||
|
|
# 测试环境库:pms_dev
|
|||
|
|
# 正式环境库:pms
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# Python(Agent 常用方式)—— 默认走隧道,可用环境变量指向别处
|
|||
|
|
import os, pymysql
|
|||
|
|
host = os.environ.get('PMS_MYSQL_HOST', '127.0.0.1')
|
|||
|
|
port = int(os.environ.get('PMS_MYSQL_PORT', '13306'))
|
|||
|
|
conn = pymysql.connect(host=host, port=port, user='root', password='Hbhyg731024@', database='pms') # 正式库
|
|||
|
|
conn = pymysql.connect(host=host, port=port, user='root', password='Hbhyg731024@', database='pms_dev') # 测试库
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Redis(通过 SSH 隧道):**
|
|||
|
|
```bash
|
|||
|
|
# 命令行
|
|||
|
|
redis-cli -h 127.0.0.1 -p 16379 -a "Hbhyg731024@"
|
|||
|
|
# 测试环境:select 6
|
|||
|
|
# 正式环境:select 5
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
```python
|
|||
|
|
# Python(Agent 常用方式)—— 默认走隧道,可用环境变量指向别处
|
|||
|
|
import os, redis
|
|||
|
|
host = os.environ.get('PMS_REDIS_HOST', '127.0.0.1')
|
|||
|
|
port = int(os.environ.get('PMS_REDIS_PORT', '16379'))
|
|||
|
|
r = redis.Redis(host=host, port=port, password='Hbhyg731024@', db=5) # 正式环境
|
|||
|
|
r = redis.Redis(host=host, port=port, password='Hbhyg731024@', db=6) # 测试环境
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**数据库工具 `tools/db.py`(需先起隧道):**
|
|||
|
|
```bash
|
|||
|
|
python tools/db.py tables # 列出所有表
|
|||
|
|
python tools/db.py count # 统计各表数据量
|
|||
|
|
python tools/db.py query "SELECT * FROM users" # 查询
|
|||
|
|
python tools/db.py exec "UPDATE users SET ..." # 写入
|
|||
|
|
```
|
|||
|
|
> **`tools/db.py` 默认连测试库 `pms_dev`**(可用 `PMS_MYSQL_DB=pms` 切生产,切换时会打印醒目警告);
|
|||
|
|
> 连接失败会打印「连接被拒:请先运行 `pwsh tools/dev-tunnel.ps1` …」并 exit 2。
|
|||
|
|
|
|||
|
|
### Nginx配置
|
|||
|
|
|
|||
|
|
> ⚠️ **下面是"骨架节选",不是线上全文** —— 线上配置还包含(2026-09 实测):
|
|||
|
|
> 安全响应头(`Strict-Transport-Security` / `X-Content-Type-Options` / `X-Frame-Options` /
|
|||
|
|
> `Referrer-Policy` / `Content-Security-Policy-Report-Only`)、
|
|||
|
|
> **限流 location**(`= /api/pms/auth/login`、`= /api/pms/auth/challenge`、`/api/pms/cabinets/abstract/`,配合
|
|||
|
|
> `/etc/nginx/nginx.conf` 里的 `limit_req_zone auth_login_zone / auth_challenge_zone / uploads_zone / scan_abstract_zone`)、
|
|||
|
|
> `location /uploads/`(免认证同源直出 + 限流 + `expires 30d`)、生产侧 `location = /pms { return 301 /pms/; }`、
|
|||
|
|
> 测试侧 `location /h5/`。
|
|||
|
|
> **改动前先拉线上真实文件为准**:
|
|||
|
|
> `plink -ssh root@118.31.119.206 "cat /etc/nginx/conf.d/app.anzhizhichong.com.conf"`。
|
|||
|
|
> 仓库内 `deploy/nginx/` 是**三份配置的镜像**(`nginx.conf` 主配置 + `app`/`test` 两份 conf.d,
|
|||
|
|
> 均与线上**逐字节一致**)—— 改线上前先比对,见「部署注意事项」第 10 条。
|
|||
|
|
|
|||
|
|
**正式环境** (`/etc/nginx/conf.d/app.anzhizhichong.com.conf`):
|
|||
|
|
```nginx
|
|||
|
|
server {
|
|||
|
|
listen 80;
|
|||
|
|
server_name app.anzhizhichong.com;
|
|||
|
|
return 301 https://$server_name$request_uri;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
server {
|
|||
|
|
listen 443 ssl http2;
|
|||
|
|
server_name app.anzhizhichong.com;
|
|||
|
|
|
|||
|
|
ssl_certificate /etc/nginx/ssl/app.anzhizhichong.com.pem;
|
|||
|
|
ssl_certificate_key /etc/nginx/ssl/app.anzhizhichong.com.key;
|
|||
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|||
|
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
|||
|
|
|
|||
|
|
# 产品介绍页
|
|||
|
|
root /data/landing;
|
|||
|
|
index index.html;
|
|||
|
|
|
|||
|
|
location /doc/ {
|
|||
|
|
proxy_pass http://127.0.0.1:3080/;
|
|||
|
|
proxy_set_header Host $host;
|
|||
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|||
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|||
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|||
|
|
client_max_body_size 500m;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# PMS API 入口(微服务统一前缀 /api/{服务})
|
|||
|
|
location /api/pms/ {
|
|||
|
|
proxy_pass http://127.0.0.1:9000/api/;
|
|||
|
|
proxy_set_header Host $host;
|
|||
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|||
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|||
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# PMS管理前端
|
|||
|
|
location /pms/ {
|
|||
|
|
alias /data/pms/web/;
|
|||
|
|
try_files $uri $uri/ /pms/index.html;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 微信回调(免认证)
|
|||
|
|
location /api/wechat/ {
|
|||
|
|
proxy_pass http://127.0.0.1:9000;
|
|||
|
|
proxy_set_header Host $host;
|
|||
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|||
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|||
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**测试环境** (`/etc/nginx/conf.d/test.anzhizhichong.com.conf`):
|
|||
|
|
```nginx
|
|||
|
|
server {
|
|||
|
|
listen 80;
|
|||
|
|
server_name test.anzhizhichong.com;
|
|||
|
|
return 301 https://$server_name$request_uri;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
server {
|
|||
|
|
listen 443 ssl http2;
|
|||
|
|
server_name test.anzhizhichong.com;
|
|||
|
|
|
|||
|
|
ssl_certificate /etc/nginx/ssl/test.anzhizhichong.com.pem;
|
|||
|
|
ssl_certificate_key /etc/nginx/ssl/test.anzhizhichong.com.key;
|
|||
|
|
ssl_protocols TLSv1.2 TLSv1.3;
|
|||
|
|
ssl_ciphers HIGH:!aNULL:!MD5;
|
|||
|
|
|
|||
|
|
root /data/pms_dev;
|
|||
|
|
index index.html;
|
|||
|
|
|
|||
|
|
# PMS API 入口(微服务统一前缀 /api/{服务})
|
|||
|
|
location /api/pms/ {
|
|||
|
|
proxy_pass http://127.0.0.1:3000/api/;
|
|||
|
|
proxy_set_header Host $host;
|
|||
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|||
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|||
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
location /pms/ {
|
|||
|
|
try_files $uri $uri/ /pms/index.html;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 微信回调(免认证)
|
|||
|
|
location /api/wechat/ {
|
|||
|
|
proxy_pass http://127.0.0.1:3000;
|
|||
|
|
proxy_set_header Host $host;
|
|||
|
|
proxy_set_header X-Real-IP $remote_addr;
|
|||
|
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
|||
|
|
proxy_set_header X-Forwarded-Proto $scheme;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> 测试环境SSL证书与正式环境共用(test是app证书的SAN域名),部署时复制pem+key即可。
|
|||
|
|
|
|||
|
|
### 部署步骤
|
|||
|
|
|
|||
|
|
> ✅ **推荐用一键脚本(在本地开发机执行)**:
|
|||
|
|
> ```bash
|
|||
|
|
> pwsh deploy/deploy-prod.ps1 # 完整部署:构建 → 校验产物 → 服务器备份 → 传 .new → 原子替换 → 重启 → 验收
|
|||
|
|
> pwsh deploy/deploy-prod.ps1 -SkipBuild # 跳过构建,只重发现有产物
|
|||
|
|
> pwsh deploy/deploy-prod.ps1 -DryRun # 演练,不做任何改动
|
|||
|
|
> pwsh deploy/deploy-prod.ps1 -SkipWeb # 只发后端
|
|||
|
|
> ```
|
|||
|
|
> 脚本会自动做:产物路径校验、**上传后 `chmod +x`**、**传 `.new` 再原子 `mv`**(避开 ETXTBSY)、
|
|||
|
|
> `nginx -t` 后才 reload、验收(服务/端口/域名/ERROR 数)、以及**用不存在的 IMEI 探测上报消费链路**
|
|||
|
|
> (不碰真实设备数据),最后打印回滚方法。
|
|||
|
|
>
|
|||
|
|
> ⛔ **`deploy/deploy-prod.sh` 与 `deploy/deploy-test.sh` 已废弃**(执行即报错退出):
|
|||
|
|
> 它们在 `target/release/` 找产物、会安装重复的 `pms-api-prod` 单元、并会用本地 `.env`
|
|||
|
|
> 覆盖线上 `.env`(历史上该文件带行内注释,会让生产起不来)。下面手写步骤仅供参考。
|
|||
|
|
|
|||
|
|
**1. 本地编译**
|
|||
|
|
```bash
|
|||
|
|
# 后端(Rust)— Windows 交叉编译 Linux 目标
|
|||
|
|
cd software/api-server
|
|||
|
|
cargo zigbuild --release --target x86_64-unknown-linux-gnu
|
|||
|
|
|
|||
|
|
cd software/device-server
|
|||
|
|
cargo zigbuild --release --target x86_64-unknown-linux-gnu
|
|||
|
|
|
|||
|
|
# 前端(React 管理端)
|
|||
|
|
cd software/web
|
|||
|
|
npm run build
|
|||
|
|
|
|||
|
|
# 微信小程序用户端(uni-app)
|
|||
|
|
cd software/h5-uniapp
|
|||
|
|
npm run build:mp-weixin
|
|||
|
|
# 产物在 dist/build/mp-weixin/,用微信开发者工具导入上传
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**2. 传输到服务器**
|
|||
|
|
|
|||
|
|
> 产物名以 `Cargo.toml` 的 `[package] name` 为准:**api-server → `pms-api`**
|
|||
|
|
> (`software/api-server/Cargo.toml:2`)、**device-server → `pms-device-server`**
|
|||
|
|
> (`software/device-server/Cargo.toml:2`)。⛔ 旧文档写的 `device-server` 是**产物名写错**,
|
|||
|
|
> 在 `target/.../release/` 下根本没有这个文件。
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 传输后端二进制(测试环境)
|
|||
|
|
scp software/api-server/target/x86_64-unknown-linux-gnu/release/pms-api root@118.31.119.206:/data/pms_dev/api-server/
|
|||
|
|
# ⚠️ 测试环境的落地文件名目前是 device-server(见 deploy/pms-device-test.service:11
|
|||
|
|
# 的 ExecStart=/data/pms_dev/device-server/device-server),与 Cargo.toml 的
|
|||
|
|
# pms-device-server 不一致 —— 这是既存事实,照它传,别改名(改名要同步改单元)
|
|||
|
|
scp software/device-server/target/x86_64-unknown-linux-gnu/release/pms-device-server root@118.31.119.206:/data/pms_dev/device-server/device-server
|
|||
|
|
|
|||
|
|
# 传输后端二进制(正式环境)—— 生产落地名就是 Cargo.toml 产物名
|
|||
|
|
scp software/api-server/target/x86_64-unknown-linux-gnu/release/pms-api root@118.31.119.206:/data/pms/api-server/
|
|||
|
|
scp software/device-server/target/x86_64-unknown-linux-gnu/release/pms-device-server root@118.31.119.206:/data/pms/device-server/
|
|||
|
|
|
|||
|
|
# 传输前端静态文件
|
|||
|
|
scp -r software/web/dist/* root@118.31.119.206:/data/pms_dev/web/ # 测试环境
|
|||
|
|
scp -r software/web/dist/* root@118.31.119.206:/data/pms/web/ # 正式环境
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
> 服务器实测(2026-09,`ls -la` + `ss -lntp`):
|
|||
|
|
> `/data/pms/api-server/pms-api`(:9000)、`/data/pms/device-server/pms-device-server`(:9001)、
|
|||
|
|
> `/data/pms_dev/api-server/pms-api`(:3000)、`/data/pms_dev/device-server/device-server`(:3001)。
|
|||
|
|
|
|||
|
|
#### 部署注意事项(最容易踩的坑,逐条都栽过)
|
|||
|
|
|
|||
|
|
1. **上传后必须 `chmod +x`** —— 否则 systemd 报 `203/EXEC`,服务起不来。
|
|||
|
|
2. **运行中的二进制不可直接覆盖**(Linux `ETXTBSY`)—— 正确姿势:
|
|||
|
|
传到 `xxx.new` → `chmod +x` → `systemctl stop` → `mv -f xxx.new xxx` → `systemctl start`。
|
|||
|
|
(`deploy-prod.ps1` 已内置这套流程。)
|
|||
|
|
3. **`.env` 不支持行内注释** —— `PORT=9000 # 注释` 会让配置解析失败、服务起不来;
|
|||
|
|
注释必须**独占一行**。(历史上用带行内注释的本地 `.env` 覆盖线上,导致生产起不来。)
|
|||
|
|
4. **`exports` 目录必须预置、且属主为 `pms`** —— api-server 以 `User=pms` **降权运行**
|
|||
|
|
(`deploy/pms-api-*.service` 的 `User=pms`/`Group=pms`),它**无法在 `/data/pms`(root 属主)
|
|||
|
|
下自建目录**;`exports`(异步导出落盘)缺失或属主不对 → 导出任务直接失败。
|
|||
|
|
实测正确属主:`drwxr-xr-x pms pms /data/pms/api-server/exports`(`uploads` 同理)。
|
|||
|
|
5. **`worker_shutdown_timeout 30s`**(`/etc/nginx/nginx.conf` 第 2 行)—— 调短会让正在传输的
|
|||
|
|
响应被中断;**改 nginx 配置前先 `nginx -t`**,通过后再 `systemctl reload nginx`。
|
|||
|
|
6. **stream 转发已移除(2026-09-20)—— 不要再给开发机开库端口** —— 主配置
|
|||
|
|
`/etc/nginx/nginx.conf` 里原来那个 `stream{}`(MySQL 3306 / Redis 6379 → 阿里云 RDS/Redis)
|
|||
|
|
**已整块删除**;服务器不再监听 3306/6379(`10.8.0.252:3306/6379` 实测已不可连)。
|
|||
|
|
⛔ **不要为了给开发机开库通路而在服务器上重新加 `stream{}` / socat / 任何端口转发** ——
|
|||
|
|
开发机要连库一律走 **SSH 隧道**(`pwsh tools/dev-tunnel.ps1`,见「本地开发环境」);
|
|||
|
|
服务侧(`/data/pms`、`/data/pms_dev`)本来就直连阿里云,也不需要转发层。
|
|||
|
|
**历史原因(务必记住)**:`stream{}` 全配置**只能有一个**、且只能在主配置里 ——
|
|||
|
|
历史上曾在 `conf.d/*.conf` 里又写了一个,nginx 以
|
|||
|
|
`"stream" directive is duplicate` **直接启动失败、整站不可用**(含生产域)。
|
|||
|
|
所以:conf.d 下只放 `http` 上下文的 `server {}`;**主配置里现在应当完全没有 `stream{}`**
|
|||
|
|
(若哪天又看到它,先查清是谁加的)。
|
|||
|
|
7. **单元必须带 `EnvironmentFile=`** —— 现行二进制强制要求环境变量,缺它起不来:
|
|||
|
|
`EnvironmentFile=/data/pms/api-server/.env` / `/data/pms/device-server/.env`(测试环境同理)。
|
|||
|
|
8. **端口/库号串了就是事故** —— 生产 `:9000/:9001` + db5/`pms`;测试 `:3000/:3001` + db6/`pms_dev`。
|
|||
|
|
部署后务必核对 `ss -lntp` 与域名返回码(见「部署架构」的判断方法)。
|
|||
|
|
9. **Redis 实时键结构变更后必须清理旧键** —— 见「踩过的坑」第 6 条(无 TTL 的旧键会被当成实时值)。
|
|||
|
|
10. **nginx 主配置也有仓库镜像,改线上前必须先比对** —— `deploy/nginx/nginx.conf`
|
|||
|
|
(**与线上 `/etc/nginx/nginx.conf` 逐字节一致**,纯 LF、100 行、3733 字节、
|
|||
|
|
md5 `82f660d8ebd3299c89edf55483277266` —— 2026-09-20 移除 `stream{}` + 订正 stream 注释后重新对齐)。
|
|||
|
|
`deploy/nginx/` 下三份分别是:
|
|||
|
|
- `nginx.conf` ←→ 线上 `/etc/nginx/nginx.conf`(**主配置**:`limit_req_zone` / `worker_shutdown_timeout`;
|
|||
|
|
⚠️ 已无 `stream{}` —— 见第 6 条,别再加回去)
|
|||
|
|
- `app.anzhizhichong.com.conf` ←→ 线上 `/etc/nginx/conf.d/app.anzhizhichong.com.conf`
|
|||
|
|
- `test.anzhizhichong.com.conf` ←→ 线上 `/etc/nginx/conf.d/test.anzhizhichong.com.conf`
|
|||
|
|
|
|||
|
|
**为什么必须有主配置镜像**:`limit_req_zone` 定义在**主配置**的 `http{}` 里,而 `conf.d/*.conf` 的
|
|||
|
|
`location` 引用了这些 zone。**只照仓库的 conf.d 重建 nginx,zone 就没了 ⇒ `nginx -t` 直接失败、整站起不来。**
|
|||
|
|
(历史已踩过 `stream{}` 重复、行内注释等同类问题。)
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 改线上前:先确认线上与仓库镜像一致(不一致就先搞清是谁改的)
|
|||
|
|
plink -batch -ssh root@118.31.119.206 -pw "<pwd>" "md5sum /etc/nginx/nginx.conf /etc/nginx/conf.d/*.conf"
|
|||
|
|
md5sum deploy/nginx/nginx.conf deploy/nginx/*.conf # 纯 LF,可直接比对
|
|||
|
|
# 逐行比对(推荐,能看出差在哪)
|
|||
|
|
diff <(plink -batch -ssh root@118.31.119.206 -pw "<pwd>" "cat /etc/nginx/nginx.conf") deploy/nginx/nginx.conf
|
|||
|
|
```
|
|||
|
|
改完线上后**把仓库镜像同步更新**(双向一致),否则下次比对全是噪声。
|
|||
|
|
11. **新增域名/新增路径要限流,按这个顺序落(三步缺一不可)**:
|
|||
|
|
1. **主配置 `http{}` 加 zone**(`deploy/nginx/nginx.conf` + 线上 `/etc/nginx/nginx.conf`):
|
|||
|
|
```nginx
|
|||
|
|
limit_req_zone $binary_remote_addr zone=<name>_zone:10m rate=<N>r/m;
|
|||
|
|
```
|
|||
|
|
⛔ zone 名要唯一;`limit_req_status 429;` 只需在主配置写一次(已在)。
|
|||
|
|
> 现有 zone(2026-09 实测 4 个):`auth_login_zone` 10r/m、`auth_challenge_zone` 20r/m、
|
|||
|
|
> `uploads_zone` 120r/m、**`scan_abstract_zone` 30r/m** —— 只挂在
|
|||
|
|
> `location /api/pms/cabinets/abstract/`(**免认证**的扫码查设备入口,无 token 可枚举 `10-%08d`,
|
|||
|
|
> 是真实攻击面)。
|
|||
|
|
> ⚠️ **`/api/cabinets/scan/{abstract_id}` 不在这个 zone 里**(前缀 `abstract/` 匹配不到 `scan/`)——
|
|||
|
|
> 它**需登录**、无 token 恒 `401`(与 id 是否存在无关),**不构成枚举信号,故无需限流**。
|
|||
|
|
> 实测:`scan` 同 IP 60 连发 = `401`×60 / `429`×0;`abstract` 同法 = `404`×23 / `429`×37。
|
|||
|
|
2. **对应的 `conf.d/*.conf` 里给那个 location 挂 `limit_req`**:
|
|||
|
|
```nginx
|
|||
|
|
location = /api/pms/<path> {
|
|||
|
|
limit_req zone=<name>_zone burst=N nodelay;
|
|||
|
|
proxy_pass http://127.0.0.1:<port>/api/<path>;
|
|||
|
|
...
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
⛔ **`limit_req` 引用的 zone 必须在主配置里存在**,否则 `nginx -t` 报
|
|||
|
|
`unknown limit_req_zone "<name>_zone"` 而拒绝启动。
|
|||
|
|
3. **先 `nginx -t`,通过后才 reload**:`nginx -t && systemctl reload nginx`。
|
|||
|
|
⛔ 不要跳过 `-t` 直接 `restart` —— 配置错会让**整站**(含生产域)起不来。
|
|||
|
|
4. 把线上改动同步回 `deploy/nginx/` 的对应文件(见第 10 条),并在报告里注明。
|
|||
|
|
|
|||
|
|
> 💡 **经验(写限流文档时必守)**:写"某 zone 覆盖某某路径"时,**必须按 `location` 的前缀语义核对,
|
|||
|
|
> 并附实测 `429` 的路径清单** —— 不要按 zone 的名字推断它管谁
|
|||
|
|
> (`scan_abstract_zone` 这名字本身就极易让人以为它管 `scan`,而它的 `location` 只有
|
|||
|
|
> `/api/pms/cabinets/abstract/`;`scan/` 落在无 `limit_req` 的通用 `location /api/pms/`)。
|
|||
|
|
|
|||
|
|
**2.1 微信小程序上传**
|
|||
|
|
|
|||
|
|
小程序构建产物 `dist/build/mp-weixin/` 不上传到服务器,而是通过微信开发者工具上传:
|
|||
|
|
|
|||
|
|
1. 打开微信开发者工具
|
|||
|
|
2. 导入项目目录 `software/h5-uniapp/dist/build/mp-weixin`
|
|||
|
|
3. 填入 AppID(已配置在 `manifest.json`)
|
|||
|
|
4. 预览/真机调试 → 上传体验版/正式版
|
|||
|
|
|
|||
|
|
**3. 服务器重启服务**
|
|||
|
|
```bash
|
|||
|
|
# SSH到服务器
|
|||
|
|
echo y | plink -ssh root@118.31.119.206 -pw "Hbhyg731024@"
|
|||
|
|
|
|||
|
|
# 正式环境
|
|||
|
|
systemctl restart pms-api
|
|||
|
|
systemctl restart pms-device
|
|||
|
|
|
|||
|
|
# 重启Nginx
|
|||
|
|
systemctl restart nginx
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 服务管理
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
# 查看服务状态
|
|||
|
|
systemctl status pms-api # 正式环境API
|
|||
|
|
systemctl status pms-device # 正式环境TCP
|
|||
|
|
systemctl status nginx
|
|||
|
|
|
|||
|
|
# 查看日志
|
|||
|
|
journalctl -u pms-api -f
|
|||
|
|
journalctl -u pms-device -f
|
|||
|
|
tail -f /var/log/nginx/error.log
|
|||
|
|
```
|