qps卡在1200是因未替换阻塞io:数据库须用swoole协程mysql(禁用pdo)、http需swoole协程客户端(设timeout+setdefer)、redis改用swoole协程驱动、文件io和sleep须协程化。

在ThinkPHP或原生PHP项目中启用Swoole协程后QPS仍卡在1200左右,说明传统阻塞IO调用尚未被真正替换,数据库、HTTP、Redis等操作仍在拖垮Worker进程——必须逐个击破这些隐性阻塞点,否则协程调度器无法释放CPU去处理其他请求。
数据库:弃用PDO/MySQLi,改用协程MySQL客户端
方法一:直接实例化协程MySQL并手动连接
创建新协程→实例化Swoole\Coroutine\MySQL→调用connect()传入配置数组→执行query()。这一步不能复用ThinkPHP原有的Db类静态实例,否则PDO连接早已在协程启动前建立,Hook失效。
方法二:封装为协程安全的查询函数
定义co_query($sql)函数,在内部go(function() use ($sql) { ... })中完成连接与查询,确保每次调用都在干净协程上下文中执行。注意:不要在函数外提前new Swoole\Coroutine\MySQL()并赋值给全局变量——【协程对象不可跨协程复用,否则引发连接错乱或段错误】。
方法三:接入连接池(生产必备)
使用Swoole\Coroutine\Pool管理MySQL连接,预设最小5、最大20个连接;每次从池中get()获取连接,用完put()归还。不配连接池时,高频创建销毁连接会触发TCP TIME_WAIT堆积,导致端口耗尽。
HTTP请求:禁用file_get_contents和Guzzle同步模式
第一步:确认协程Hook已覆盖stream函数
执行php --ri swoole | grep hook,输出需含stream => enabled。若缺失,需在server.php入口处顶部调用Swoole\Runtime::enableCoroutine(SWOOLE_HOOK_ALL),且必须在任何HTTP客户端初始化之前执行。
第二步:改用Swoole\Coroutine\Http\Client
实例化时指定域名和端口(HTTPS需设true)→调用set(['timeout' => 3])防止无限挂起→setDefer()开启异步模式→get('/path')发起请求→最后统一recv()收包。不设timeout会导致某个慢接口拖垮整个协程组。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
这一步操作起来很简单,直接把旧的file_get_contents('https://api.xxx.com')替换成协程客户端四行代码就行,但必须确保不在协程外提前初始化$client对象。
Redis:原生扩展无效,必须用Swoole协程驱动
方法一:零配置切换
删除new Redis()或Predis\Client调用→改用$redis = new Swoole\Coroutine\Redis()→$redis->connect('127.0.0.1', 6379)→后续所有get、set操作保持原语义。注意:【Swoole的Redis协程客户端是独立C实现,不依赖PHP redis.so扩展,即使装了原生扩展也必须禁用】。
方法二:配合连接池提升吞吐
用Swoole\Coroutine\Pool包装Swoole\Coroutine\Redis实例,设置max=50,避免单连接并发瓶颈。每次请求从池取连接,操作完立即归还,严禁unset($redis)——协程结束时连接自动回收。
方法三:批量操作用pipeline
对同一Key的多次set或get,改用$redis->pipeline()->set(...)->get(...)->exec(),减少网络往返次数。普通串行调用10次get会发10个包,pipeline合并为1个。
文件IO与Sleep:同步写法照常可用,但必须协程化
将sleep(1)改为co::sleep(1),底层由协程调度器接管,不会阻塞Worker进程。
读写文件不再用fopen/file_get_contents,改用co::readFile('a.txt')和co::writeFile('b.txt', $data)。这些函数在协程内自动yield,等待磁盘IO完成后再恢复执行。
如果仍沿用file_put_contents,哪怕启用了SWOOLE_HOOK_FILE,也会因底层系统调用未完全覆盖而偶发阻塞——尤其在高负载下,部分Linux发行版的glibc对open()的hook存在兼容性问题。










