Laravel Octane 在 Docker + WSL2 中页面加载缓慢,如何定位?
结论
不能仅凭这些数据,就把 2 到 10 秒的页面加载时间认定为 Docker + WSL2 的正常开销。首先要确认日志中的 total_ms 覆盖了哪些阶段,以及浏览器记录的 2 到 10 秒对应的是主文档请求、某个 Livewire 请求,还是整个页面的加载过程。
这些指标的计时范围可能不同,不能直接相减,再把差值归因于 Docker 或 WSL2。现有信息还不足以确定根因,可以沿请求链路逐段排查。
先确认浏览器慢在哪个阶段
打开 DevTools 的 Network 面板,禁用浏览器缓存后刷新页面,分别检查主文档、静态资源和 Livewire 请求。
重点查看:
- 主文档的 Timing:时间主要花在排队、连接、等待首字节,还是下载响应上。
- 后续请求:Dashboard 是否在 HTML 返回后继续发出请求,是否有接口或资源请求持续数秒。
- 请求依赖:是否有请求必须等前一个请求结束才能开始。
如果主文档只用了几百毫秒,而页面最后一个请求数秒后才结束,可以沿瀑布图查找拖慢页面的请求。对于 Filament、Livewire 相关页面,也要区分初始文档和后续交互请求。
如果 Network 中的请求已经结束,页面仍迟迟不可交互,再用 Performance 面板检查 JavaScript 执行和渲染开销。
用 curl 测量单个 HTTP 请求
先在 WSL2 中测试出现问题的同一个页面。以下使用 8080 作为示例端口,执行前请替换为实际地址、端口和路径:
URL='http://127.0.0.1:8080/'
curl --silent --show-error \
--output /dev/null \
--write-out 'status=%{http_code}\ndns=%{time_namelookup}s\nconnect=%{time_connect}s\ntls=%{time_appconnect}s\npretransfer=%{time_pretransfer}s\nfirst_byte=%{time_starttransfer}s\ntotal=%{time_total}s\n' \
"$URL"
这些时间大多是从请求开始计算的累计时间,不能直接相加:
time_namelookup:名称解析完成时间。time_connect:连接建立完成时间。time_appconnect:TLS 握手完成时间;普通 HTTP 请求不涉及该阶段。time_pretransfer:准备开始传输的时间。time_starttransfer:收到首字节的时间。time_total:整个请求完成的时间。
time_starttransfer - time_pretransfer 可以帮助观察发送请求后到收到首字节之间的等待,其中仍包含请求传输、排队和服务端处理,不能直接等同于 PHP 执行时间。
注意检查 HTTP 状态码。如果页面需要登录,而 curl 实际拿到的是重定向或登录页,就无法与浏览器里的 Dashboard 请求比较。可以用 DevTools 的“Copy as cURL”复制对应请求,在本地复现时保留必要的登录状态。
比较 Windows、WSL2 和容器内的结果
对同一路径做三组对照:
- 从 Windows 请求对外暴露的地址。
- 从 WSL2 请求该服务的实际入口。
- 从应用容器内请求 Octane 的实际监听地址和端口。
容器内测试取决于是否安装了 curl,以及 Octane 的监听配置。不要假定容器内端口与宿主机映射端口相同。
比较时尽量保持路径、登录状态、Host 和协议一致;如果必须绕过代理或改用 HTTP,应记录这些差异。
| 对照结果 | 优先检查方向 |
|---|---|
| Windows 慢,WSL2 和容器内快 | Windows 到服务的访问路径、名称解析、代理或端口转发 |
| 对外入口慢,容器内直连快 | 入口代理、转发链路、连接与请求等待 |
| 容器内直连也慢 | Octane 请求排队、应用处理、依赖访问或资源竞争 |
| curl 快,浏览器页面慢 | 请求是否一致,以及后续资源、Livewire 请求和前端执行 |
这些结果可以帮助缩小排查范围,但仍需要相应的计时证据,不能单凭一组现象确定具体组件有问题。
核对服务端日志的计时范围
total_ms 是日志字段名。它是否代表完整的 HTTP 请求耗时,需要检查计时的实现位置。
例如,如果计时从某个 middleware 开始,可能没有覆盖请求进入应用前的等待;如果生成响应对象后就结束计时,也可能没有覆盖后续发送响应的阶段。流式响应还需要单独核对实际输出发生的位置。
建议为一次请求关联唯一标识,并比较:
- 浏览器或 curl 的请求耗时。
- 入口代理的请求耗时与 upstream 耗时,如果当前部署包含代理且支持这些指标。
- 应用最外层可观测位置记录的耗时。
还要核实 database_ms: 0 的含义。它可能表示没有发生被统计到的数据库查询,也可能表示统计没有覆盖实际连接或调用路径。仅凭这个值,不能完全排除 Microsoft SQL Server 相关等待。
根据测量结果再调整配置
如果确认等待发生在应用侧,可以继续检查:
- Worker 是否繁忙:4 个 workers 本身不能证明配置合适。先比较单请求与多个并发请求;若并发时明显变慢,再检查 worker 占用、CPU、内存及 Docker/WSL2 资源限制。
- OPcache 是否在实际运行环境生效:应在应用容器中执行
php -i。修改配置后,还要确保运行中的 Octane 进程已经加载新配置。 - 外部调用是否进入计时:Redis、SQL Server、文件访问和 HTTP 调用都应在实际请求路径上测量,不能因为服务位于本地就排除等待。
- 静态资源如何提供:如果慢的是 CSS、JavaScript 或图片,检查它们的服务路径、资源大小和传输耗时。
项目已经放在 /data/www/,并把 vendor/ 移到了 named volume。若怀疑挂载性能,下一步应检查容器的实际挂载来源和相关文件访问耗时。
接下来可以收集同一次请求的 Network Timing、curl 分阶段耗时,以及应用计时的起止位置,再判断应调整网络入口、Octane 处理链路,还是页面中的具体请求。
备注:内容仅供参考。
