小米php面试考swoole重在实战避坑:global变量非协程安全、sleep()会阻塞进程、连接池不解决sql慢、热更新类加载冲突、协程使用边界需精准判断。

直接说结论:小米 PHP 面试里问 Swoole,从来不是考你能不能跑通 swoole_http_server,而是看你有没有踩过坑、能不能分辨“伪协程”和真协程、知不知道哪些写法会让常驻进程悄悄吃光内存。
为什么 global 变量在 onRequest 里每次都是初始值?
因为 Swoole Worker 进程是常驻的,但每个请求回调(如 onRequest)运行在独立协程中,global 变量不是协程安全的——它属于进程级作用域,但协程之间不共享执行上下文里的变量状态。更关键的是,Swoole 默认启用多 Worker 进程,global 在不同进程间完全隔离。
- 现象:
global $counter = 0; $counter++每次请求都输出1 - 替代方案:用
Swoole\Table存共享计数,或用static变量(仅限单 Worker + 单线程模式,生产环境慎用) - 真正该用的地方:配置类实例、连接池对象——它们本就该复用,而不是靠
global“凑合”
Co::sleep() 和 sleep() 混用会卡死进程?
会。前者是协程让出控制权,后者是整个进程休眠。只要在一个协程里调了 sleep(1),整个 Worker 进程就停 1 秒,所有其他协程全部阻塞——这和 PHP-FPM 的阻塞行为没区别,完全废掉了 Swoole 的并发能力。
- 必须只用
Co::sleep()、Co::usleep()等协程友好的等待函数 - 第三方 SDK 如果内部用了
sleep()或file_get_contents()(未开启协程 Hook),要主动用Swoole\Coroutine::create()包一层并确认是否被 Hook - 检查是否启用协程 Hook:
Co::set(['hook_flags' => SWOOLE_HOOK_ALL]),但注意SWOOLE_HOOK_CURL在某些版本有兼容问题
数据库查询为什么还是慢?明明用了连接池
连接池只解决“建连开销”,不解决 SQL 本身慢、没索引、事务锁表、或者协程内串行调用的问题。常见误区是以为开了 mysql->set(['pool' => true]) 就万事大吉。
- 典型错误:在一个协程里连续写
$db->query(...); $db->query(...);—— 这仍是串行,没利用协程并发 - 正确做法:用
Co::create()并发发起多个查询,或用go()启动多个协程分别查 - 注意连接池大小:默认
max_connections = 64,如果并发协程数超限,会排队等连接,反而比不用池还慢 - 别忽略驱动层:PDO 默认不支持协程,必须用
Swoole\Coroutine\MySQL或think-swoole封装后的协程版 DB 类
热更新后部分请求报 Class not found?
这是 Swoole 常驻内存最典型的副作用:Worker 进程加载的类定义不会随文件变更自动刷新。即使你 kill -USR1 重载,已加载的类仍留在内存里,而新请求可能加载新版本类——导致类定义冲突或找不到旧类。
- 根本原因:
opcache.revalidate_freq = 0时,PHP 不检查文件修改时间;Swoole 又不主动清 opcache - 临时解法:部署时加
opcache_reset()到 reload 脚本,或设opcache.revalidate_freq = 1(仅开发) - 长期方案:用
max_request限制 Worker 处理请求数,到阈值后自动退出重建进程,强制重载代码 - 别信“平滑重启万能”:USR1 信号只重载代码,不重置静态变量、全局资源、连接池状态——这些得靠
max_request或显式onWorkerStart清理
真正难的不是写协程代码,而是判断哪段逻辑该进协程、哪段必须出协程、哪段压根不能进——比如日志写入、Redis Pipeline、甚至 json_encode() 大数组,都可能因 CPU 占用过高拖垮整个 Worker。这些细节,才是小米面试官听你讲完“协程是轻量级线程”之后,立刻追问的点。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











