正经做标注平台,没人愿意每天都敲 http://192.168.1.10:8080 这种地址去访问。Label Studio 装好只是第一步,能让团队稳定、安全、顺滑地访问才是真正考验运维功底的地方。我自己在服务器上折腾过几轮,踩了不少坑,把 Nginx 反向代理这套配置彻底梳理通了,这篇就把完整过程拆开讲清楚,包括为什么要做反代、配置里每个关键参数是干什么的、502 错误怎么一步步排查、子路径发布有哪些坑、以及最后怎么把 HTTPS 加上。
适合看这篇的人很明确:一是自己搭了 Label Studio,想发布给同事用;二是已经在用,但经常遇到 502、刷新 404、上传大文件 413 这类问题的;三是准备把标注平台正式上生产,想做正规域名 + HTTPS 访问的。下面这些内容都是我实际跑过的配置,直接照着做基本能成。
1. 为什么放着现成的8080端口不用,非要加一层Nginx
1.1 直接暴露IP加端口访问,问题比想象中多
很多人在服务器上跑起 Label Studio 后,第一反应就是把端口开出来给同事访问。这种方式在只有你自己一个人用的时候没问题,但凡超过两三个人,问题就开始冒头。
第一个问题是不好记。http://172.16.3.21:8080 这个地址你得反复提醒别人,而且很多公司内网的 IP 是 DHCP 分配的,重启一次服务器 IP 变了,所有人手上的地址全部失效。第二个问题是浏览器对非标准端口的体验很差,Chrome 和 Edge 会在地址栏明确提示这个连接不是私密连接,部分浏览器插件甚至默认拦截非 443 端口的访问,说明成本很高。第三个问题才是真正要命的——端口直接暴露意味着没有任何一层访问控制,任何人拿到这个 IP 都能访问你的标注系统,而标注数据往往是涉及隐私的核心资产。
这些问题不是 Label Studio 特有的,任何 web 服务直接裸奔都这样。解决办法就是在外面套一层 Nginx,把 80/443 作为统一入口,后面具体是哪个服务、哪个端口,都由 Nginx 来分发和屏蔽。
1.2 Nginx反向代理在这套架构里到底干了什么
网上解释反向代理的文章很多,讲得玄乎的也不少。其实用大白话说,Nginx 在中间扮演的是一个“前台接待”的角色。访客不需要知道 Label Studio 实际运行在哪个端口,只需要访问你给的前台地址(比如 http://label.example.com),前台根据规则找到对应的后端同事(8080 端口上跑着的 Label Studio 进程),把请求转过去,再把响应带回来。
这里要注意区分“正向代理”和“反向代理”。正向代理是替客户端去访问它自己访问不到的资源,服务的对象是“你”;反向代理是替后端服务器接收请求、转发消息,服务的对象是“后端服务”。我们这里用的是反向代理,客户端只认 Nginx 这一个入口,背后有几台服务器、跑在什么端口,对用户完全透明。
Nginx 在这个架构里还能顺手做很多事:终止 HTTPS 加密连接、统一的访问日志和错误日志、请求体大小限制、超时控制、WebSocket 升级支持、甚至可以在 Nginx 这一层做 IP 白名单和 Basic Auth 认证。这些都是 Label Studio 本身不擅长的事。
1.3 Label Studio 的自身特点决定了 Nginx 要特殊关照哪些点
Label Studio 不是那种简单的静态页面,它由前端 SPA 应用 + Python 后端 API 组成,默认监听 8080 端口。它有三个特点直接决定了 Nginx 的配置重点:
第一,前后端分离架构,前端需要加载大量 JS/CSS 静态资源,Nginx 代理时不能把静态资源的路径搞错,否则页面样式全丢。第二,它用 WebSocket 做实时通信,比如多人在线协作标注、任务状态推送、导入进度更新都依赖长连接,Nginx 默认的 HTTP 代理协议不会自动升级 WebSocket,需要显式配置。第三,标注工具必然涉及大量数据上传,一张标注图片几十 MB,一个 JSON 数据集动辄上百 MB,Nginx 默认的 1MB 请求体限制必须调大。
理解了这几个特点,后面的配置就有的放矢了——不是机械地抄配置,而是知道每一行在解决什么问题。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境准备:把Nginx和Label Studio先跑起来
2.1 安装Nginx,不同发行版两条命令的事
先安装 Nginx。Ubuntu/Debian 系用 apt:
bash复制sudo apt update
sudo apt install nginx -y
sudo systemctl enable --now nginx
CentOS/RHEL 系用 dnf(新版本)或者 yum(老版本):
bash复制sudo dnf install nginx -y
sudo systemctl enable --now nginx
安装完成后用 systemctl status nginx 确认状态是 active (running),然后本机 curl 一下:
bash复制curl http://127.0.0.1/
如果返回的是 Nginx 默认欢迎页的 HTML,说明基础服务已经跑起来了。这一步卡住的情况通常是 80 端口被其他进程占用,用 ss -lntp | grep :80 看一下端口状态就知道是谁占了。
2.2 把Label Studio跑起来,注意监听地址的选择
Label Studio 的启动方式有两种主流做法。一种是用 pip 直接安装后启动:
bash复制pip install label-studio
label-studio start label-studio --host 127.0.0.1 --port 8080
另一种是用 Docker:
bash复制docker run -it -p 8080:8080 -v $(pwd)/label-studio-data:/label-studio/data heartexlabs/label-studio:latest
这里有一个关键点建议从一开始就养成习惯:在准备用 Nginx 反代的情况下,Label Studio 启动时监听 127.0.0.1 就够了。因为 Nginx 和 Label Studio 在同一台机器上,Nginx 通过本机回环地址访问 8080 端口即可,不需要对外暴露。这样即使服务器防火墙忘了关 8080,外面也访问不到这个端口,等于多了一层保护。
启动之后验证一下后端是否正常:
bash复制ss -lntp | grep 8080
curl http://127.0.0.1:8080/
curl 返回 HTML 页面内容、ss 能看到监听状态,说明后端就绪。
2.3 为什么一定要先确认后端再动Nginx配置
这条经验是我踩过很多次坑之后总结出来的:排查顺序永远是自底向上。意思是先确认最底层的东西没问题,再往上查。后端服务没起来,你怎么改 Nginx 配置都是白搭,502 错误照旧。
实际操作中,建议先把后端跑通、能通过 curl http://127.0.0.1:8080/ 访问到页面,再开始写 Nginx 配置。如果一上来就写配置,一旦出现 502,你分不清是 Nginx 写错了还是后端没起来,排查效率极低。先把地基打好,后面每加一层配置、每改一行参数,都能立刻判断是谁的锅。
如果是通过 Docker 跑 Label Studio,额外注意端口映射的方向:-p 8080:8080 表示把容器内的 8080 端口映射到宿主机的 8080 端口,你 Nginx 里 proxy_pass 指向的是宿主机 127.0.0.1:8080,这个没问题。但如果之前映射成了 -p 18080:8080,那 Nginx 就要指向 18080,别写错了。
3. 反向代理配置逐项拆解:斜杠、请求头、WebSocket和上传限制
3.1 先给一份最小可用的反向代理配置
先别急着上复杂功能,一份最精简的配置长这样:
nginx复制server {
listen 80;
server_name label.example.com;
location / {
proxy_pass http://127.0.0.1:8080;
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/label-studio.conf 里,然后执行 sudo nginx -t 检查语法,再 sudo systemctl reload nginx 让配置生效。这时候通过 http://label.example.com 应该就能访问到 Label Studio 登录页了。
但这份配置只能算“能跑”,离“好用”还差好几个关键参数。下面逐个讲清楚为什么那些参数不能省。
3.2 proxy_pass 的写法:有没有斜杠天差地别
proxy_pass 后面接的 URL 末尾带不带 /,是 Nginx 新手最容易搞混的地方。我见过很多配置出问题,根源都是这里。
简单总结一下规律:
| 请求路径 | location 写法 | proxy_pass 写法 | 实际转发到后端的路径 |
|---|---|---|---|
/ |
location / |
http://127.0.0.1:8080 |
/ |
/projects/1 |
location / |
http://127.0.0.1:8080 |
/projects/1 |
/label/abc |
location /label/ |
http://127.0.0.1:8080 |
/label/abc(完整路径透传) |
/label/abc |
location /label/ |
http://127.0.0.1:8080/ |
/abc(location 匹配部分被替换为 /) |
核心规则是:proxy_pass 末尾不带斜杠时,请求的完整 URI 会原样转发给后端;末尾带斜杠时,location 匹配到的那段前缀会被根路径替换。
对于用根路径发布的场景,location / 搭配不带斜杠的 proxy_pass,行为最简单直观,推荐这么用。如果用了子路径发布(后面专门讲),斜杠的影响会被成倍放大,一定仔细核对。
3.3 请求头转发:后端怎么知道用户是从哪个域名来的
很多人在 proxy_pass 下面加了一堆 proxy_set_header,但不知道为什么要加。这四行请求头每一行都有它的用途:
Host $host:把客户端访问的域名原样传给后端。Label Studio 生成一些绝对链接(比如分享链接、导出链接)时会用到这个值。如果不设置,Nginx 默认会把127.0.0.1:8080作为 Host 传给后端,导致后端生成的链接全部指向内网地址。X-Real-IP $remote_addr:标记客户端真实 IP。没这一行的话,后端看到的 IP 永远是 Nginx 所在机器的 IP,Label Studio 里的用户登录日志、访问记录就失去了审计价值。X-Forwarded-For $proxy_add_x_forwarded_for:追加客户端 IP 到转发链。当中间有多层代理时,每一层的 IP 都会追加到这个头里,后端可以据此还原完整的链路。X-Forwarded-Proto $scheme:告诉后端客户端是通过 http 还是 https 访问。后面上了 HTTPS,这个头特别重要,否则后端可能误以为用户在走 http,生成回跳地址时把 https 链接变成 http。
配置里这四行一个都不要省。哪怕只是内网使用,也建议照抄,因为以后上 HTTPS、加域名、做用户审计都会用到。
3.4 WebSocket升级和数据上传限制:两个最容易翻车的环节
Label Studio 的好多功能依赖 WebSocket 长连接。默认情况下,Nginx 代理 HTTP 请求时,Connection 头会被设置为 close,这就把 WebSocket 的升级请求给掐断了。后果是页面能打开,但实时的功能全部异常,控制台里刷一堆 WebSocket 连接失败的报错。
在 location 块里加上两行:
nginx复制proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
第一行把 HTTP 协议版本升级到 1.1,WebSocket 依赖这个版本;后面两行是标准的 WebSocket 协议升级握手头。加上之后,标注任务的实时推送、多人协作的同步才能正常工作。
另外一个必调的参数是上传大小限制。Nginx 默认的 client_max_body_size 是 1MB,而标注任务里上传一张高清图片、一段视频,或者导入一个大的 JSON 标注文件,分分钟超过这个值。超了之后 Nginx 直接返回 413 Request Entity Too Large,页面根本传不了文件。
在 server 块里一层写入:
nginx复制client_max_body_size 100M;
如果你的团队经常处理几百 MB 的大文件,也可以调到 500M 或者更高。建议按实际业务需求来,不要一味贪大。
超时时间同样值得调整。默认的 proxy_read_timeout 是 60 秒,意思是 Nginx 等待后端返回数据的最大时间。Label Studio 在导入大型数据集、执行 AI 辅助标注模型推理时,后端处理可能超过 60 秒,这时候用户就会看到 504 Gateway Timeout。稳妥的做法是把读写超时都调到 300 秒:
nginx复制proxy_read_timeout 300s;
proxy_send_timeout 300s;
3.5 配置改完别急着reload,先检查语法
每次改完 Nginx 配置,一定要养成先检查语法再重载的习惯:
bash复制sudo nginx -t
这条命令会告诉你配置文件有没有错误、错误在哪个文件的哪一行。语法有问题直接 reload 会加载失败,更麻烦的是如果你手上有多个站点配置,一个错误可能导致整个 Nginx 服务起不来,影响面很大。
确认语法没问题后再:
bash复制sudo systemctl reload nginx
reload 是平滑重载,不会中断正在进行的请求,已经在处理的连接会继续走完,新的连接才走新配置。相比 restart,生产环境里一律用 reload。
4. 502 Bad Gateway排查全链路:从后端到防火墙一个不落
4.1 502的本质:Nginx拿到了请求,但没从后端拿到有效响应
502 是反向代理场景下最容易见的错误,也是最容易让新手心态爆炸的错误。其实它的含义非常明确:客户端到 Nginx 这一段通信是正常的,Nginx 成功接收了请求,但在它去连接后端服务、或者等待后端返回响应时失败了。
Nginx 的 error.log 是最直接的线索来源。默认位置在 /var/log/nginx/error.log,用以下命令实时盯着:
bash复制tail -f /var/log/nginx/error.log
常见的报错大概有这几种:
text复制connect() failed (111: Connection refused) while connecting to upstream
connect() failed (110: Connection timed out) while connecting to upstream
no live upstreams while connecting to upstream
看到 Connection refused,基本可以断定是后端端口没人监听,或者监听地址不对。看到 Connection timed out,则是网络层面连不上,大多是防火墙拦截或者跨主机访问时地址不可达。no live upstreams 说明配置了 upstream 组且组内所有后端都不可用。
4.2 第一步:后端服务到底活着没有
排查 502,我永远建议先向后端服务开刀,因为这一步最快、成本最低,而且能消除最大的变量。
bash复制curl -I http://127.0.0.1:8080/
如果这条命令返回了 HTTP 状态码和响应头,说明后端进程是正常工作的,问题大概率出在 Nginx 和后端的连接链路。如果 curl 直接报 Connection refused,说明后端进程挂了,或者监听端口变了,先解决后端问题再说。
有些时候 Label Studio 是用 systemd 管理的,用 systemctl status label-studio 能直接看到进程状态和最近日志。崩溃的原因五花八门,常见的内存不足被 OOM Kill、数据库连不上,这些信息在 systemd 日志里都能翻到。
4.3 第二步:监听地址和端口对不对
后端活着但 Nginx 还是 502,下一个怀疑对象就是监听地址。
用 ss -lntp | grep 8080 看输出结果:
text复制LISTEN 0 128 127.0.0.1:8080 0.0.0.0:*
注意 Local Address 列。如果显示的是 127.0.0.1:8080,说明这个服务只在本机回环接口上监听。这种情况 Nginx 也跑在同一台机器时没问题,Nginx 访问 127.0.0.1:8080 能连上。但如果 Nginx 在另一台机器上,需要转发到这台机器的 8080 端口,那就连不上,因为服务根本没有对外开放。
反过来,如果显示 0.0.0.0:8080,说明服务在所有网卡上监听,跨主机访问可用,但别忘了检查防火墙。
还有一个容易忽略的细节:Label Studio 启动时如果指定了 --host 0.0.0.0,会监听所有网卡;指定 --host 127.0.0.1,则只监听回环。Docker 方式时,别忘了先 docker ps 看一下端口映射关系,确认宿主机上到底是哪个端口对应容器内的 8080。
4.4 第三步:防火墙和SELinux,两个隐形拦截者
有时候后端监听没问题,Nginx 也在同一台机器上,但依然 502,而且 error.log 里报的不是 refused 而是 timeout,这时候该查防火墙了。
Ubuntu 上常见的是 ufw:
bash复制sudo ufw status
如果显示 80/tcp ALLOW、443/tcp ALLOW,说明 Nginx 的入口端口没问题。CentOS 上则可能是 firewalld:
bash复制sudo firewall-cmd --list-all
更隐蔽的坑在 CentOS/RHEL 的 SELinux。Nginx 默认在 SELinux 的 httpd_t 域里运行,这个域默认不允许向外建立网络连接。也就是说,Nginx 去连接 127.0.0.1:8080 这个行为,会被 SELinux 直接拦截,表现为连接被拒绝或超时,但防火墙却是放行的。
排查命令:
bash复制getenforce
如果输出 Enforcing,那就跑一下:
bash复制setsebool -P httpd_can_network_connect 1
这条命令把 httpd_can_network_connect 开关打开并持久化,Nginx 就能正常连接后端端口了。很多 CentOS 上装 Nginx 反代出问题的人,查了三天最后发现是 SELinux 的锅,真的不夸张。
4.5 第四步:看配置、看日志、抓包,三重确认
到了这一步,后端活着、监听正常、防火墙和 SELinux 都放行,还是 502,那就回头检查 Nginx 配置本身。
用 nginx -T 可以列出当前实际加载的全部配置:
bash复制sudo nginx -T | grep proxy_pass
重点核对 proxy_pass 的地址、端口是否正确。一个人脑子里记的配置和机器上实际生效的配置很可能不一样,特别是配置文件多、改了忘改的情况下,nginx -T 看到的一定是真实生效的。
如果还是不对,用 tcpdump 直接看网络包。比如监听回环接口的 8080 端口:
bash复制sudo tcpdump -i lo port 8080 -nn
当用浏览器访问页面触发请求时,观察是否有 SYN 包到达后端端口。没有包进来,说明 Nginx 根本没向后端发起连接,问题在配置或路由层面;有 SYN 但没有后续包,说明握手被后端拒了,大概率还是监听地址的问题。
4.6 症状相近但根因不同的错误别混在一起查
排查反向代理问题,最忌讳的就是把 502、504、404、413 混为一谈。这些错误码虽然都是“不正常状态”,但代表的环节完全不同:
| 状态码 | 含义 | 常见根因 |
|---|---|---|
| 502 | 后端不可达或响应无效 | 服务没起、端口错误、防火墙拦截 |
| 504 | 后端响应超时 | 后端处理慢、proxy_read_timeout 太短 |
| 404 | 路径不存在 | proxy_pass 斜杠规则、location 匹配错误 |
| 413 | 请求体过大 | client_max_body_size 未调大 |
遇到问题先看状态码再动手。502 就按上面这条链路查后端,504 就去调超时参数和查后端性能,404 就去核对路径和斜杠,413 就是一行配置的事。方向对了,五分钟就能解决。
5. 一台服务器多服务:子路径发布的正确姿势
5.1 什么时候需要子路径发布
很多团队只有一台服务器,上面可能同时跑着业务后台、监控面板、个人网盘,再算上 Label Studio,端口资源紧张,域名也没那么多。这种情况下,让 Label Studio 挂在某个域名下的一个子路径(比如 http://server.example.com/label-studio/),就成了一种现实的选择。
但子路径发布比根路径发布要复杂,核心原因是一个 Web 应用里所有的资源路径、API 路径、内链跳转,都要感知到一个“我并不是在根路径”的事实。如果只改 Nginx 加一个 location /label-studio/,不做其他配合,百分之百会遇到页面打不开、资源加载不全、导出链接 404 这类问题。
5.2 关键一步:让Label Studio知道自己挂在子路径下
好消息是 Label Studio 可以用命令行参数指定基础 URL。启动时加上:
bash复制label-studio start label-studio --host 127.0.0.1 --port 8080 --base-url http://server.example.com/label-studio
这里的 --base-url 就是告诉应用:用户最终是通过这个地址访问你的,所有自动生成的链接都要以这个为前缀。不同版本参数名可能略有差异,可以用 label-studio start --help 确认一下。
对应的 Nginx 配置是:
nginx复制location /label-studio/ {
proxy_pass http://127.0.0.1:8080/;
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;
}
注意这里 proxy_pass 末尾的 / 是必须的。它表示:当请求路径是 /label-studio/xxx 时,把 location 匹配到的 /label-studio/ 前缀去掉,把剩下的 /xxx 转发给后端。后端拿到的是去掉前缀后的路径,正好和应用 --base-url 设定的逻辑匹配。
如果这里漏了末尾的斜杠,Nginx 会把 /label-studio/xxx 原样转发给后端,后端在 /label-studio/xxx 这个路径上是没有路由的,直接返回 404。
5.3 子路径的坑与我的建议
子路径发布即便配置正确,后续运维也会遇到各种拐弯抹角的问题。比如浏览器缓存了旧路径前缀、某个页面里硬编码了绝对路径、第三方链接没有加前缀。这些问题排查起来非常消耗精力,而且一旦 Label Studio 升级,新的静态资源托管逻辑可能又会和前缀处理产生新的摩擦。
所以我的个人建议是:如果服务器上还有多余的 IP 或者可以解析子域名,优先用子域名而不是子路径。 子域名的配置本质上就是根路径反代,不需要处理任何前缀问题,省心得多。只有域名确实不够用,才考虑子路径方案。
如果实在要用子路径,记住一个铁律:Nginx 的 location、proxy_pass 的斜杠、Label Studio 的 --base-url,三者必须保持一致的逻辑。改一个就要同步检查另外两个。
6. 上HTTPS:让标注平台过浏览器这一关
6.1 为什么标注平台必须上HTTPS
你可能觉得内网用无所谓,但只要你的 Label Studio 被多人通过浏览器访问,浏览器就会直接把 “不安全” 三个字打在地址栏上。如果用户从公网访问,HTTP 流量在整个链路里都是明文传输,账号密码、标注内容、上传的原始数据全部裸奔。
标注平台涉及的数据敏感度通常很高,用 HTTP 发布在安全上完全不合格。另外,现在浏览器对 HTTP 密码框的警告越来越强硬,Chrome 直接在你的密码输入框上打上“此网站不安全”的显眼标识。用户第一印象就很差,这对工具在公司内部的推广非常不利。
好在用 Nginx 反代后,加 HTTPS 是非常自然的一步——证书在 Nginx 这一层终止,Label Studio 后端完全不用动。
6.2 用Certbot一键申请和配置,不用手动处理证书
Let's Encrypt 的免费证书已经非常成熟,配合 Certbot 的 Nginx 插件,一条命令就能完成申请和配置。
先安装 Certbot:
bash复制sudo apt install certbot python3-certbot-nginx -y
然后执行:
bash复制sudo certbot --nginx -d label.example.com
这条命令会自动完成 ACME 域名验证,申请证书成功后,自动修改 Nginx 配置,把 443 端口的 HTTPS server 块加上,证书路径配好,还会在 80 端口上把 HTTP 请求自动 301 跳转到 HTTPS。
前提条件有两个:一是域名 label.example.com 已经解析到你的服务器公网 IP;二是服务器的 80 端口可以被外网访问——ACME 验证机制需要 Let's Encrypt 的服务器访问到你的 80 端口来确认你对域名的控制权。
证书快到期时 Certbot 会自动续期,可以手动验证一下续期流程是否正常:
bash复制sudo certbot renew --dry-run
看到 Congratulations 相关的输出就说明续期链路没问题。
6.3 一份可以上生产的完整配置参考
把上面的内容整合起来,一份适合实际使用的配置大概是这个样子的:
nginx复制server {
listen 80;
server_name label.example.com;
# 无论访问哪个路径,都跳转到 HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl http2;
server_name label.example.com;
# 证书路径由 Certbot 自动配置,这里替换成自己的实际路径
ssl_certificate /etc/letsencrypt/live/label.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/label.example.com/privkey.pem;
# 上传体积上限,按需调整
client_max_body_size 100M;
location / {
proxy_pass http://127.0.0.1:8080;
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;
# WebSocket 支持
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# 超时时间
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
}
这份配置把 HTTP 流量全部 301 到 HTTPS、在 Nginx 层终止 SSL、反向代理本地 Label Studio、支持 WebSocket、允许大文件上传、拉长超时时间,基本覆盖了日常使用的全部场景。实际使用时把 server_name、证书路径换成你自己的即可。
配置完毕记得:
bash复制sudo nginx -t
sudo systemctl reload nginx
我自己在实际操作中的一个习惯是:始终先以 HTTP + IP 的方式跑通,再上域名,最后才上 HTTPS。 每增加一层就验证一次,别想着一步到位。这样做的好处是,一旦出现问题,你永远知道是哪个环节引入的,而不是对着一个完全陌生的报错无从下手。这套路径我走了很多遍,按顺序来,整个过程其实比想象中顺畅很多。
