Coroutine\Channel

通道,类似于go语言的chan,支持多生产者协程和多消费者协程。底层自动实现了协程的切换和调度。

实现原理

  • 通道与PHPArray类似,仅占用内存,没有其他额外的资源申请,所有操作均为内存操作,无IO消耗
  • 底层使用PHP引用计数实现,无内存拷贝。即使是传递巨大字符串或数组也不会产生额外性能消耗

方法

  • Channel->push :当队列中有其他协程正在等待pop数据时,自动按顺序唤醒一个消费者协程。当队列已满时自动yield让出控制器,等待其他协程消费数据
  • Channel->pop:当队列为空时自动yield,等待其他协程生产数据。消费数据后,队列可写入新的数据,自动按顺序唤醒一个生产者协程。

Coroutine\Channel使用本地内存,不同的进程之间内存是隔离的。只能在同一进程的不同协程内进行pushpop操作
Coroutine\Channel2.0.13或更高版本可用

属性

  • $capacity 通道缓冲区容量
  • $errCode channel错误码

示例

use Swoole\Coroutine as co;
$chan = new co\Channel(1);
co::create(function () use ($chan) {
    for($i = 0; $i < 100000; $i++) {
        co::sleep(1.0);
        $chan->push(['rand' => rand(1000, 9999), 'index' => $i]);
        echo "$i\n";
    }
});
co::create(function () use ($chan) {
    while(1) {
        $data = $chan->pop();
        var_dump($data);
    }
});
swoole_event::wait();

连接池

使用 Chan 可以方便的实现连接池功能。管理各类 Socket 连接资源。

class RedisPool
{
    /**
     * @var \Swoole\Coroutine\Channel
     */
    protected $pool;

    /**
     * RedisPool constructor.
     * @param int $size 连接池的尺寸
     */
    function __construct($size = 100)
    {
        $this->pool = new Swoole\Coroutine\Channel($size);
        for ($i = 0; $i < $size; $i++)
        {
            $redis = new Swoole\Coroutine\Redis();
            $res = $redis->connect('127.0.0.1', 6379);
            if ($res == false)
            {
                throw new RuntimeException("failed to connect redis server.");
            }
            else
            {
                $this->put($redis);
            }
        }
    }

    function put($redis)
    {
        $this->pool->push($redis);
    }

    function get()
    {
        return $this->pool->pop();
    }
}


  • 新用户(手机注册)

    这个链接池,只能在一个go内 new一次 每次都要新建一个连接池么? 创建一个连接池怎么在很多地方使用呢?求大侠指点 go(function(){ sleep(3); $mpool = new MysqlPool(10); $mysqlpools = $mpool->get(); $result = $mysqlpools->query('select * from user'); $all = $mpool->getAllPools(); var_dump($all); });

  • woann

    用单例模式啊

  • 奔赴

    不同协程 客户端不能共用 响应数据会有异常

  • 奔赴

    在不同协程 下客户端不可共用(脏数据) , 通过协程创建连接池的方式 提前准备好大量连接, 但 具体连接数量(channel 长度)还是需要根据线上业务决定 过去swoole 1 版本都是通过单例去节省服务器连接的 , swoole 4 协程的使用 对worker 进程中 连接的数量要根据业务去调试

    worker start 时 制定连接池长度并创建连接池 push 入队列 每个任务开始 pop 一个连接 任务结束 在 push 入队列 形成一个可循环的调用池.

    channel size 设置 100 就创建 100个 连接 如果同时有200个任务 就会有 100个任务会挂起

    存在问题 这个池子里的连接, 在大量任务面前是否够用, 就需要精确的去定位了. 还有这个连接池的方式, 服务端的连接数会大量占用, 可能导致连接数被打满, 具体使用还是需要根据业务.

  • 十里_路途~

    这样写连接池肯定有问题的,有没有考虑到带宽的占用?