0. 凌晨 5 点的 404 警报

2026 年 1 月 2 日凌晨,在完成 M4 Mac mini 的开发环境整备后,我开始了基于 Go 的微服务 IM 系统——KamaChat 的部署。然而,在最后关头,一个工程难题挡在了面前:SPA(单页应用)路由刷新崩溃。

本文记录了从现象观察到进程级收割的完整排查过程,旨在为同样在 Apple Silicon 架构下进行混合开发的朋友提供避坑指南。


1. 现象描述:会“消失”的页面

在部署完成后,通过 Docker 容器内的 Apache 服务器访问系统,出现以下现象:

  • 正常: 点击页面内的按钮跳转(如从首页点击“登录”),页面通过 Vue 路由正常切换。
  • 崩溃:/login 路径下直接刷新浏览器,瞬间弹出 Apache 404 Not Found

2. 排查:服务端与客户端的“路径代沟”

核心原理

这是一个典型的 服务端路由 vs 客户端路由 冲突:

  • 客户端路由 (Vue/React): URL 的变化由 JavaScript 拦截并局部渲染,不向服务器请求新文件。
  • 服务端路由 (Apache/Nginx): 刷新页面时,浏览器会真实地向物理服务器请求 /login 这个文件。
  • 矛盾点: Apache 在 /var/www/html 下找不到名为 login 的实体文件,自然返回 404。

3. 解决:三步走

第一步:配置重写规则 (Logic Fix)

我通过修改 Apache 的 000-default.conf,强制让所有未命中物理文件的请求兜底到 index.html

1
2
# 兜底方案
ErrorDocument 404 /index.html

第二步:收割僵尸进程 (Process Hack)

在 Apple Silicon 模拟运行 amd64 镜像的环境下,我遇到了最头疼的问题:service apache2 restart 竟然失效了!通过 ps aux 我发现了大量带有 <defunct> 标志的僵尸进程

解决方案:
利用强制信号 kill -9 配合 docker restart,彻底重置容器的进程树(PID 1),确保配置被物理加载。

第三步:链路闭环 (Verification)

配合 Incognito (无痕模式)Cmd+Shift+R 强制刷新,成功排除了浏览器对 404 响应的缓存干扰。


4. 总结与思考

这次排查让我深刻意识到:“环境差异”是后端开发的家常便饭。 在 Apple Silicon 架构下,Rosetta 2 模拟层虽然强大,但也会在进程控制层面带来一些不可预见的副作用。

这告诉我们,不仅仅要会写业务逻辑,更要具备在面对问题时保持冷静、通过 error.log 和进程管理工具进行排查的能力。