是,Workerman响应慢可能由磁盘I/O引起:先看top中%wa>10%(尤其>30%且us/sy低),再用iostat -x 1确认await>20ms(SSD)或>50ms(HDD)、util>80%,结合pidstat -d -p和lsof -p定位日志、配置文件等同步I/O操作。

Workerman响应慢是否真由磁盘I/O引起?先看%wa和await
别急着改代码,先确认是不是磁盘拖了后腿。在服务器上跑 top -c,盯住右上角的 %wa(I/O wait)——如果长期 >10%,尤其飙到 30% 以上,而 CPU us/sy 却很低,基本可以锁定是 I/O 在卡住事件循环。
再补一刀:执行 iostat -x 1,重点看两列:await(平均每次I/O耗时)和 util(设备利用率)。SSD 上 await > 20ms、util > 80% 就说明磁盘已排队或过载;HDD 更敏感,await > 50ms 就得警觉。
注意:Workerman 是单线程事件循环,任何阻塞式文件读写(比如 file_get_contents、fopen + fread、未异步的日志写入)都会让整个 Worker 进程卡住,所有连接一起变慢,现象就是“长连接响应延迟升高”,但错误日志里却找不到明显报错。
哪些操作会偷偷触发同步磁盘I/O?
Workerman 里看似无害的代码,可能正在把事件循环拖进磁盘泥潭:
-
error_log()或未配置异步的 Monolog 日志处理器(如StreamHandler直写文件) - 用
file_put_contents($path, $data, FILE_APPEND)记操作日志,尤其没加LOCK_EX时,多个进程争抢会加剧阻塞 - 在
onMessage里调用json_decode(file_get_contents('config.json'))这类“配置热加载”逻辑 - Redis 或 MySQL 客户端用了同步驱动(如 phpredis 默认模式、PDO 没设
PDO::ATTR_EMULATE_PREPARES = false),且网络延迟高+重试多,间接放大磁盘刷写压力(如 swap 频繁)
这些操作不会报错,但会让 epoll_wait 返回间隔变长、甚至出现毫秒级空转(用 strace -p <pid> -e trace=epoll_wait,read,write</pid> 可验证)。
怎么验证是某个具体文件操作在拖慢?
用 pidstat -d -p <pid> 1</pid>(rd_bytes 或 wr_bytes 突增,就说明它干了重I/O的事。
更准的办法是结合 lsof -p <pid></pid> 查出进程打开的所有文件,重点关注:
- 日志文件路径(如
/var/log/workerman/app.log)是否被频繁write - 临时目录(如
/tmp)下是否有大量小文件读写 - 是否意外打开了数据库文件(如 SQLite)、锁文件(
.lock)并持续轮询
如果发现某个日志文件的 lsof 输出里有大量 DEL(删除中)或 REG(常规文件)状态,且 pidstat 显示写入量大,基本就是它。
Workerman场景下I/O优化的关键取舍点
不是所有I/O都能/都应该异步化。关键在于分清“必须等结果”和“可延后处理”:
- 日志:换用
SyslogHandler推给 rsyslog(它自带缓冲和批量刷盘),或用RotatingFileHandler配合max_files=0和buffer_size=1048576缓冲写入 - 配置加载:启动时一次性读入内存,运行期只读不写;若真需热更新,改用 Redis 存配置,避免每次 onMessage 都碰磁盘
- 临时数据:用
apcu_store()替代写临时文件;必要落盘时,走stream_socket_client('unix:///var/run/php-fpm.sock')转给独立 I/O 进程处理 - 绝对不能做:在
onMessage里sleep(1)等磁盘、或用shell_exec('cp ...')同步拷贝
最常被忽略的是:即使你用了异步日志库,如果底层 syslogd 配置了 $ActionFileDefaultTemplate RSYSLOG_FileFormat 并启用了磁盘队列,它仍可能成为瓶颈。查 /etc/rsyslog.conf 里有没有 $ActionQueue* 配置,以及磁盘空间是否充足。

















