SyncInvoker 組件
使用場景
Swoole4.x
后,提供了非常強大的協程能力,讓我們可以更好地壓榨服務器性能,提高并發。然而,目前 PHP
在 Swoole
協程生態上,并不是很完善,比如:沒有協程版本的 MonogoDB
客戶端,而為了避免在 Worker
進程中調用了同步阻塞的 Api
,例如在 Http
回調中使用了同步的 MonogoDB
客戶端,導致 Worker
進程退化為同步阻塞,導致無法完全地發揮協程的優勢。所以 EasySwoole
提供了一個同步程序協程調用轉化驅動。
設計原理
啟動自定義進程監聽 UnixSocket
,然后在 Worker
進程中調用協程客戶端發送命令到自定義進程并處理,然后把處理結果返回給 Worker
進程中的協程客戶端。
組件要求
- php: >= 7.1.0
- ext-swoole: >= 4.4.23
- easyswoole/component: ^2.0
- opis/closure: ^3.5
安裝方法
composer require easyswoole/sync-invoker
倉庫地址
基本使用
首先定義一個驅動工作實例(可以定義多個),示例代碼如下:
<?php
namespace App\Utility;
use EasySwoole\SyncInvoker\AbstractDriver;
class MyInvokerDriver extends AbstractDriver
{
private $stdclass;
function __construct()
{
$this->stdclass = new \stdClass();
parent::__construct();
}
public function test($a, $b)
{
$this->response($a + $b);
}
public function a()
{
$this->response('this is a');
}
public function getStdClass()
{
return $this->stdclass;
}
}
然后注冊一個對應的調用器,示例代碼如下:
<?php
namespace App\Utility;
use EasySwoole\Component\Singleton;
use EasySwoole\SyncInvoker\SyncInvoker;
// 注冊一個對應的調用器
class MyInvoker extends SyncInvoker
{
use Singleton;
}
最后在 EasySwoole全局事件
中 的 mainServerCreate
事件中進行注冊
<?php
namespace EasySwoole\EasySwoole;
use EasySwoole\EasySwoole\AbstractInterface\Event;
use EasySwoole\EasySwoole\Swoole\EventRegister;
class EasySwooleEvent implements Event
{
public static function initialize()
{
// TODO: Implement initialize() method.
date_default_timezone_set('Asia/Shanghai');
}
public static function mainServerCreate(EventRegister $register)
{
$invokerConfig = \App\Utility\MyInvoker::getInstance()->getConfig();
// 以下這些配置都是可選的,可以使用組件默認的配置
/*
$invokerConfig->setServerName('EasySwoole'); // 設置服務名稱,默認為 'EasySwoole'
$invokerConfig->setWorkerNum(3); // 設置 Worker 進程數,默認為 3
$invokerConfig->setTempDir(EASYSWOOLE_ROOT . '/Temp'); // 設置 unixSocket 存放目錄,默認為 系統臨時文件存放目錄('/tmp')
$invokerConfig->setMaxPackageSize(2 * 1024 * 1024); // 設置最大允許發送數據大小,默認為 2M
$invokerConfig->setTimeout(3.0); // 設置服務調用超時時間,默認為 3.0 秒
$invokerConfig->setAsyncAccept(true); // 設置異步接收數據,默認為 異步接收(不建議修改)
$invokerConfig->setOnWorkerStart(function (\EasySwoole\SyncInvoker\Worker $worker) {
var_dump('worker start at Id ' . $worker->getArg()['workerIndex']);
}); // 設置服務啟動時執行的事件回調
*/
$invokerConfig->setDriver(new \App\Utility\MyInvokerDriver()); // 設置驅動工作實例【必須配置】
// 注冊 Invoker
\App\Utility\MyInvoker::getInstance()->attachServer(ServerManager::getInstance()->getSwooleServer());
}
}
在框架服務啟動后,即可在框架的任意位置調用 Invoker 服務了,使用示例如下:
例如在控制器中進行調用:
<?php
namespace App\HttpController;
use EasySwoole\Http\AbstractInterface\Controller;
class Index extends Controller
{
public function index()
{
$ret = \App\Utility\MyInvoker::getInstance()->invoke()->test(1, 2);
var_dump($ret);
var_dump(\App\Utility\MyInvoker::getInstance()->invoke()->a());
$ret = \App\Utility\MyInvoker::getInstance()->invoke()->callback(function (\App\Utility\MyInvokerDriver $driver) {
$std = $driver->getStdClass();
if (isset($std->time)) {
return $driver->response($std->time);
} else {
$std->time = time();
return $driver->response('new set time');
}
});
var_dump($ret);
}
}
/**
* 輸出結果:
* int(3)
* string(9) "this is a"
* string(12) "new set time"
* int(3)
* string(9) "this is a"
* int(1611071672)
*/
注意事項
- 盡量使用函數名調用方式,閉包方式調用會存在部分閉包函數序列化失敗問題
- 傳遞參數,返回結果盡量用數組或者字符串傳遞,資源對象無法序列化