avatar

mdo

Hello

  • 首页
  • 知识库
  • 归档
  • 标签
  • 关于
主页 利用 Swoole 协程特性
文章

利用 Swoole 协程特性

发表于 2026-06-26 更新于 2026-06- 26
作者 mdo
7~9 分钟 阅读

默认配置 task_worker_num => 4 在面对批量推送消息时严重不足。当需要给成百上千个用户推送消息时,循环调用 Swoole 的 push 或投递大量小任务,会瞬间挤爆这 4 个进程,导致后续任务全部排队甚至超时。

请按照以下分级方案进行优化,方案一和方案二可以立即见效:

方案一:立即调整配置(治标)

在批量推送场景下,进程主要在等待网络 I/O 响应,属于 I/O 密集型 任务。你可以大胆地将进程数放大。

修改 config/swoole.php 中的 options:

php

'options' => [
    // 1. 大幅提升进程数:建议调整为 16 或 32(甚至 64,取决于你的用户量和服务器内存)
    'task_worker_num' => 32, 

    // 2. 必须调大任务队列缓冲区(防止任务因为堆积而直接丢弃或报错)
    'task_max_request' => 2000,
    'socket_buffer_size' => 128 * 1024 * 1024, // 128M
],

请谨慎使用此类代码。

方案二:改变投递策略(治本)

绝对不要在循环中为每个用户单独投递一个 Task(例如:循环 1000 次投递 1000 个 Task)。这会产生高额的进程间通信(IPC)开销。

正确做法:打包成一个或数个大任务投递。

  • 错误写法:

    php

    // 导致 task 瞬间不够用的元凶
    foreach ($userIds as $id) {
        $server->task(['type' => 'push', 'uid' => $id, 'msg' => $data]);
    }
    

    请谨慎使用此类代码。

  • 正确写法(分批打包):

    php

    // 将 10000 个用户切分成每 500 个一组,只投递 20 个 Task
    $chunks = array_chunk($userIds, 500); 
    foreach ($chunks as $chunkUids) {
        $server->task(['type' => 'batch_push', 'uids' => $chunkUids, 'msg' => $data]);
    }
    

    请谨慎使用此类代码。

  • 在 Task 消费端处理:

    php

    // 在 onTask 事件中拿到一组 uids 循环 push
    foreach ($data['uids'] as $uid) {
        if ($server->isEstablished($fd)) { // 确保连接有效
            $server->push($fd, $data['msg']);
        }
    }
    

    请谨慎使用此类代码。

方案三:利用 Swoole 协程特性(高性能选型)

推送消息本身是非常适合协程的。如果开启了协程,你甚至可以不用 Task 进程,直接在 Worker 中利用协程并发去推送,速度会比 Task 快数倍。

  1. 确保 config/swoole.php 开启了 'coroutine' => true。

  2. 使用 Swoole\Coroutine\Barrier 或通道(Channel)并发推送:

php

use Swoole\Coroutine;
use Swoole\Coroutine\Barrier;

// 在 WebSocket 路由或事件中直接执行
Coroutine::create(function() use ($userIds, $data, $server) {
    $barrier = Barrier::create();
    $chunks = array_chunk($userIds, 100); // 100个一组并发

    foreach ($chunks as $chunk) {
        // 自动并发,利用底层协程调度,不阻塞当前进程
        Coroutine::create(function() use ($barrier, $chunk, $data, $server) {
            foreach ($chunk as $uid) {
                $fd = getFdByUid($uid); // 自定义获取 fd 的方法
                if ($server->isEstablished($fd)) {
                    $server->push($fd, $data);
                }
            }
        });
    }
    // 等待这一批全部送达
    Barrier::wait($barrier);
});

技术
许可协议:  CC BY 4.0
分享

相关文章

7月 26, 2026

完美地解决 TP3 老系统数据的平滑读取

为了彻底、完美地解决 TP3 老系统数据的平滑读取,必须解决两个核心痛点: TP3 的盲猜反序列化机制(标量存原样,数组/对象存原生 serialize,同时兼容 json)。 TP3 与 TP6 的缓存前缀(Prefix)不一致。 以下是为你量身定制的终极落地解决方案,通

7月 26, 2026

thinkphp3 redis序列化和反序列化

在老系统重构迁移至 ThinkPHP 6 (TP6) 的过程中,ThinkPHP 3 (TP3) 的 Redis 序列化历史遗留问题是最核心的连环坑之一。 TP3 的 S('key', $value) 缓存方法和底层 Redis 驱动,在存储非字符串(如数组、对象)时,有一套独特的序列化行为。如果不

7月 24, 2026

Table 空间极易发生哈希冲突并溢出

既然你没有在业务中主动调用 Room,只使用了全局广播,那么这个问题就非常明确了:即使你不用房间功能,think-swoole 只要开启了 WebSocket,底层就会强行初始化并注入一个名为 Room 的驱动组件。 [1] 框架底层默认使用 table 类型来作为 Room 的存储介质。当进行全局

下一篇

Linux | Reqable · API抓包调试 + API测试一站式工具

上一篇

think-swoole 全局广播

最近更新

  • 完美地解决 TP3 老系统数据的平滑读取
  • thinkphp3 redis序列化和反序列化
  • Table 空间极易发生哈希冲突并溢出
  • 将监控程序直接跑在云端
  • AI 驱动型 Facebook 群组关键词监控 Chrome 浏览器插件

热门标签

API CodeGeex Gitkraken Management Manticore Premiere Sublime Swoole ThinkPHP ThinkPHP5

目录

©2026 mdo. 保留部分权利。