01 上线架构与请求流程
1. 开发环境与生产环境
当前工程以开发学习为主,常见启动方式是:
ts-node server.ts
开发时可以让工具实时编译 TypeScript,方便修改和调试。生产环境更推荐:
TypeScript 源码
→ tsc 编译
→ JavaScript 文件
→ PM2 启动 Node.js
例如:
npm run build
pm2 start dist/server.js --name nloop-api
这让“构建是否成功”和“服务是否启动”成为两个明确步骤。
2. 为什么不能只使用 node dist/server.js
下面当然可以启动服务:
node dist/server.js
但直接启动有几个运维问题:
- 当前终端退出后,进程可能结束;
- 应用崩溃后不会由普通 shell 自动恢复;
- 不方便统一查看状态和日志;
- Linux 重启后不会自动启动;
- 更新服务时缺少固定操作入口。
PM2 就是为管理这些长期运行的进程而存在。
3. PM2、Nginx 和 systemd 各自负责什么
PM2
启动 Node.js 应用
记录进程状态
收集 stdout/stderr
应用崩溃后重启
执行 stop/restart/reload
保存待恢复的进程列表
PM2 不是 HTTP 反向代理,也不是 TypeScript 编译器。
Nginx
监听公网 80/443
返回前端静态文件
把 /api/ 转发到 Express
终止 HTTPS
记录访问日志和代理错误
systemd
Linux 启动
→ systemd 启动 PM2 daemon
→ PM2 恢复保存过的 Node.js 应用
在常见 Linux 发行版中,Nginx 本身也由 systemd 管理。
4. 一次请求发生了什么
访问前端:
GET https://example.com/
→ Nginx
→ /var/www/frontend/index.html
访问 API:
GET https://example.com/api/users
→ Nginx :443
→ http://127.0.0.1:8080/api/users
→ Express Router
→ JSON 响应
→ Nginx 返回给浏览器
浏览器不需要知道 Express 实际监听 8080。
5. 为什么 Express 建议只监听本机地址
const host = "127.0.0.1";
const port = 8080;
app.listen(port, host, () => {
console.log(`Server is listening on ${host}:${port}`);
});
如果 Nginx 与 Express 位于同一台服务器,Express 通常只需要接受本机代理请求。公网只开放 80/443,可以减少不必要的入口。
如果写成:
app.listen(8080, "0.0.0.0");
则 Express 会监听所有网络接口。是否能从公网访问还取决于防火墙和云安全组,但新人阶段没有必要额外暴露这个端口。
6. 数据库由谁管理
PM2 管理的是 Node.js 应用进程。MySQL、Redis 通常分别由它们自己的 systemd 服务、云数据库或容器平台管理:
PM2
└─ Express / Worker
systemd 或云服务
├─ MySQL
├─ Redis
└─ Nginx
不要使用 PM2 启动 MySQL。
7. 第一版目录结构
先使用容易理解的目录:
/var/www/nloop/current/
├── dist/
├── node_modules/
├── package.json
├── package-lock.json
├── ecosystem.config.js
└── .env
日志先使用 PM2 默认目录:
/home/deploy/.pm2/logs/
Nginx 日志通常位于:
/var/log/nginx/
等首次上线稳定后,再引入 releases/ 和 current 软链接。
8. 最小上线成功标准
node dist/server.js可以启动;curl http://127.0.0.1:8080/health成功;- PM2 显示应用为
online; - Nginx 配置通过
nginx -t; - 通过域名可以访问 API;
- 可以找到 PM2 和 Nginx 日志;
- 服务器重启后应用可以恢复。
不要在第一天追求“全套高可用”。先保证每一层都能单独验证。
复盘题
- PM2、Nginx 和 systemd 分别管理什么?
- 为什么 PM2 显示
online不代表公网一定可以访问? - 为什么同机部署时 Express 可以只监听
127.0.0.1? - 浏览器访问
/api/users时,经过了哪些组件? - MySQL 为什么通常不由 PM2 管理?