1.5版本升級指南

升級前請務必備份,執行以下命令進行升級
composer require workerman/webman-framework ^1.5 -W && composer require webman/console ^1.2.12 && php webman install

功能特性及變更

支援workerman v5協程

提示
workerman v5要求 PHP>=8.1
workerman升級命令 composer require workerman/workerman ^5.0.0 -W
Fiber協程需要安裝 composer require revolt/event-loop ^1.0.0

示例

延遲響應

<?php

namespace app\controller;

use support\Request;
use Workerman\Timer;

class TestController
{
    public function index(Request $request)
    {
        // 睡眠1.5秒
        Timer::sleep(1.5);
        return $request->getRemoteIp();
    }
}

Timer::sleep() 類似 PHP自帶的sleep()函數,區別是Timer::sleep()不會阻塞進程

發起HTTP請求

注意
需要安裝 composer require workerman/http-client ^2.0.0

<?php

namespace app\controller;

use support\Request;
use Workerman\Http\Client;

class TestController
{
    public function index(Request $request)
    {
        static $client;
        $client = $client ?: new Client();
        $response = $client->get('http://example.com'); // 同步方法發起異步請求
        return $response->getBody()->getContents();
    }
}

同樣的$client->get()請求是非阻塞的,這可用於在webman中非阻塞處理http請求,提高性能。

更多參考workerman/http-client

增加 support\Context 類

support\Context 類用於儲存請求相關的數據,當請求完成時,相關的context數據會自動刪除。也就是說context數據生命週期是跟隨請求生命週期的。

全局變量污染

協程環境禁止將請求相關的狀態信息存儲在全局變量或者靜態變量中,因為這可能會導致全局變量污染,例如

<?php

namespace app\controller;

use support\Request;
use Workerman\Timer;

class TestController
{
    protected static $name = '';

    public function index(Request $request)
    {
        static::$name = $request->get('name');
        Timer::sleep(5);
        return static::$name;
    }
}

將進程數設定為1,當我們連續發起兩個請求時
http://127.0.0.1:8787/test?name=lilei
http://127.0.0.1:8787/test?name=hanmeimei
我們期望兩個請求返回的結果分別是 lileihanmeimei,但實際上返回的都是hanmeimei
這是因為第二個請求將靜態變量$name覆蓋了,第一個請求睡眠結束時返回時靜態變量$name已經成為hanmeimei

正確但方法應該是使用context存儲請求狀態數據

<?php

namespace app\controller;

use support\Request;
use support\Context;
use Workerman\Timer;

class TestController
{
    public function index(Request $request)
    {
        Context::set('name', $request->get('name'));
        Timer::sleep(5);
        return Context::get('name');
    }
}

局部變量不會造成數據污染

<?php

namespace app\controller;

use support\Request;
use support\Context;
use Workerman\Timer;

class TestController
{
    public function index(Request $request)
    {
        $name = $request->get('name');
        Timer::sleep(5);
        return $name;
    }
}

因為$name是局部變量,協程之間無法互相訪問局部變量,所以使用局部變量是協程安全的。

關於協程

協程不是萬能解,引入協程意味著需要注意全局變量/靜態變量污染問題,需要設定context上下文。另外協程環境調試bug比阻塞式編程更複雜一些。

webman阻塞式編程實際上已經足夠快,通過techempower.com 最近三年的三輪的壓測數據看,webman阻塞式編程帶資料庫業務比go的web框架gin、echo等性能高近1倍,比傳統框架laravel性能高出近40倍。

當資料庫、redis都在內網時,多進程阻塞式編程性能可能往往高於協程,這是由於資料庫、redis等足夠快時,協程創建、調度、銷毀的開銷可能要大於進程切換的開銷,所以這時引入協程並不能顯著提升性能。

什麼時候使用協程

當業務中有慢訪問時,例如業務需要訪問第三方接口時,可以採用workerman/http-client以協程的方式發起異步HTTP調用,提高應用並行能力。