先用 lsof -i :9501 或 netstat -tulnp | grep :9501 查看占用进程,确认是否为残留 hyperf、php 服务或调试工具;优先执行 php bin/hyperf.php stop 或 kill -usr2 优雅终止,无效再 kill -9 ,开发中可临时修改 config/autoload/server.php 中的 port 配置。

Hyperf 启动报 Address already in use 怎么快速定位占用端口的进程
Hyperf 默认监听 0.0.0.0:9501,启动失败时最常见的错误是:ERROR: bind() failed. Address already in use。这不是配置问题,而是端口被其他进程占着——可能是上次没关干净的 php bin/hyperf.php start,也可能是另一个项目、调试器或僵尸 worker 进程。
直接杀端口最有效,但别用 kill -9 盲杀。先确认是谁在用:
-
lsof -i :9501—— 查 9501 端口(把 9501 换成你实际报错的端口) - 若提示
command not found,macOS 上运行brew install lsof,Ubuntu/Debian 用sudo apt install psmisc(提供fuser),CentOS/RHEL 用yum install psmisc - 输出里重点关注
PID和COMMAND列,确认是不是php或hyperf相关进程
如何安全终止占用端口的 PHP 进程
找到 PID 后别急着 kill -9,Hyperf 的 master 进程有优雅退出机制,优先尝试软终止:
-
kill -USR2 <pid></pid>—— 发送 USR2 信号,Hyperf master 会主动关闭所有 worker 并退出(仅对 Hyperf 主进程有效) -
kill <pid></pid>—— 普通 TERM 信号,多数情况也能正常收尾 - 只有当进程无响应、
ps aux | grep php仍显示它时,才用kill -9 <pid></pid> - 如果
lsof显示多个 PID(比如主进程 + 多个 worker),通常只杀主进程(COMMAND列为php且没有[worker]后缀的那个)即可,其余会自动退出
为什么 php bin/hyperf.php start 杀不干净?常见残留场景
Hyperf 使用 Swoole 多进程模型,异常退出(如 Ctrl+C 中断、SSH 断连、OOM 被系统 kill)容易留下孤儿进程,尤其是:
- 子进程变成 init 的子进程(PPID=1),
ps aux | grep php可能看不到,但lsof -i :9501仍能查到 - 使用
nohup php bin/hyperf.php start &启动后未记录 PID,下次启动前忘了清理 - Docker 容器重启失败,宿主机上残留了旧容器映射的端口绑定(此时需
docker ps -a+docker rm -f) - Swoole task 进程卡死,导致主进程无法完全释放端口(可加
--task-worker-num=0临时排除)
避免反复踩坑:启动前自动检测 + 清理脚本建议
手动查杀太麻烦,可在项目根目录加个简易检查脚本(比如 bin/start-safe):
#!/bin/bash
PORT=${1:-9501}
if lsof -ti:$PORT > /dev/null; then
echo "Port $PORT occupied, killing..."
kill $(lsof -ti:$PORT) 2>/dev/null || true
sleep 0.5
fi
exec php bin/hyperf.php start
然后运行 chmod +x bin/start-safe && bin/start-safe。注意:lsof -ti 只输出 PID,适合管道传递给 kill;但生产环境慎用自动 kill,建议保留人工确认环节。
真正麻烦的不是端口冲突本身,而是残留进程背后暴露的异常退出路径——比如没处理 SIGTERM、没写 onWorkerExit 回调、或 Swoole 配置里 max_request 设得太小导致频繁重启失败。这些才是长期稳定运行的关键点。











