swoole協程

swoole協程

建立協程

echo "test-1\n";
    go(function(){
        Co::sleep(2);  // 協程讓出控制,進入協程控制隊列。繼續往下執行
        echo "coroutine 111 \n";
    });

    echo "test-2";
    go(function(){
        echo "coroutine 222 \n";
    });

    echo "test-3\n";

Image text

  • go() 是 \Co::create() 的縮寫, 用來建立一個協程
  • 執行過程:php

    一、運行php Demo.php 系統啓動一個新進程
    二、執行到go()函數,在當前進程中生成一個協程
    三、協程中遇到IO阻塞(Co::sleep(2)),協程讓出控制, 進入協程調度隊列
    四、進程繼續向下執行,輸出:test-2
    五、執行下一個協程, 輸出:coroutine 222
    六、以前的協程準備就緒, 繼續執行, 輸出:coroutine 111mysql

  • 結果:經過以上代碼例子,能夠了解到協程和進程的關係,協程的調度sql

協程爲何快,快在哪

  • 常見說法: 能夠建立不少個協程來執行任務, 因此快
  • 計算機任務分爲2種數據庫

    一、CPU密集型: 好比加減乘除等科學計算
    二、IO 密集型: 好比網絡請求, 文件讀寫等數組

  • 併發swoole

    因爲CPU切換任務很是快, 快到人類能夠感知的極限, 就會有不少任務同時執行的錯覺網絡

  • 並行併發

    同一個時刻, 同一個CPU只能執行同一個任務, 要同時執行多個任務, 就須要有多個CPU才行函數

  • 協程適合的是 IO 密集型應用, 由於協程在 IO阻塞時會自動調度, 減小IO阻塞致使的時間損失高併發

普通程序運行以下:

$n = 3;
for($i=0; $i < $n; $i++){
    sleep(1);
    echo microtime(true) . " : hello {$i}".PHP_EOL;
}
echo "hello main".PHP_EOL;

Image text

多協程運行以下:

$n = 3;
for ($i = 0; $i < $n; $i++) {
    go(function () use ($i) {
        Co::sleep(1);
        echo microtime(true) . ": hello {$i}".PHP_EOL;
    });
};
echo "hello main".PHP_EOL;

Image text

  • 結果

    一、普通的程序運行時遇到 IO阻塞 會致使的性能損失
    二、多協程,遇到IO阻塞時發生調度, IO就緒時恢復運行

  • 說明

    一、sleep() 能夠看作是 CPU密集型任務, 不會引發協程的調度
    二、Co::sleep() 模擬的是 IO密集型任務, 會引起協程的調度

    管道(channel)

  • Channel 管道:支持多生產者協程和多消費者協程。底層自動實現了協程的切換和調度。
  • Channel 管道是用於同一進程內協程之間交換數據的工具,能夠理解爲,Channel 就是一個實現了協程切換和調度的隊列,亦或是數組。
  • 生產協程:在channel已滿時,會被掛起;
  • 消費協程:在channel爲空是,也會被掛起。

$chan = new Chan();
go(function()use($chan){
        for($i=0;$i<5;$i++){
                $chan->push($i);
                echo "順序插入{$i}".PHP_EOL;
        }
});
echo "順序執行".PHP_EOL;
go(function()use($chan){
        while(!$chan->isEmpty()){
                $res = $chan->pop();
                echo "順序消費{$res}".PHP_EOL;
        }
});

Image text

結果能夠看出生產者協程和消費者協程是交替運行的,而協程切換的時機則是在運行到 push 和 pop 的時候,首先會進入生產者協程,而後生產了一條數據,而後代碼繼續執行輸出「順序執行」的字符串並建立了消費者協程;因爲前面已經 push 了一條數據因此此時的 $channel->isEmpty() 是非空狀態,再執行 pop。

鏈接池

  • 因爲管道(channel)的特性(寫入消費),能夠經過管道實現鏈接池
  • 鏈接池是一個用於分配和管理鏈接的容器,能夠避免在高併發的系統下反覆地去建立和銷燬鏈接,便於鏈接的複用。
<?php 

class Mysql
{
    public $pool;
    public $config = [
        'maxnum' => 20, // 最大鏈接數
        'mysql' => [  // 數據庫配置
            'host'=>'192.168.1.13',
            'port'=>3306,
            'user'=>'root',
            'password'=>'123456',
            'database'=>'test'
        ]
    ];
    /** 初始化 */
    public function __construct()
    {
        $maxnum = $this->config['maxnum'];
        $this->pool = new \Swoole\Coroutine\Channel($maxnum);
        for($i=1; $i<$maxnum; $i++){
            $mysqlConnect = $this->createConnect();
            $this->push($mysqlConnect);
        }
    }
    /** 建立數據庫鏈接 */
    public function createConnect()
    {
        $mysql = new \Swoole\Coroutine\MySQL();  // 使用鏈接mysql組件
        $mysql->connect($this->config['mysql']);  // 鏈接操做
        return $mysql;
    }
    /** 生產者插入 */
    public function push($source){
        return $this->pool->push($source);
    }
    /** 銷燬 */
    public function pop(){
        return $this->pool->pop();
    }
    /** 當前鏈接數 */
    public function length(){
        return $this->pool->length();
    }
}
// 建立一個協程
go(function(){
    $model = new Mysql();
    $conn = $model->pop(); //  獲取鏈接
    $res = $conn->query('show tables');  // 查詢當前庫的有哪些表
    // 逐條打印表名
    foreach($res as $key => $val){
        echo $val['Tables_in_test'].PHP_EOL;
    }
});

Image text

相關文章
相關標籤/搜索