SyncInvoker 组件

使用场景

Swoole4.x 后,提供了非常强大的协程能力,让我们可以更好地压榨服务器性能,提高并发。然而,目前 PHPSwoole 协程生态上,并不是很完善,比如:没有协程版本的 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

仓库地址

easyswoole/sync-invoker

基本使用

首先定义一个驱动工作实例(可以定义多个),示例代码如下:

  1. <?php
  2. namespace App\Utility;
  3. use EasySwoole\SyncInvoker\AbstractDriver;
  4. class MyInvokerDriver extends AbstractDriver
  5. {
  6. private $stdclass;
  7. function __construct()
  8. {
  9. $this->stdclass = new \stdClass();
  10. parent::__construct();
  11. }
  12. public function test($a, $b)
  13. {
  14. $this->response($a + $b);
  15. }
  16. public function a()
  17. {
  18. $this->response('this is a');
  19. }
  20. public function getStdClass()
  21. {
  22. return $this->stdclass;
  23. }
  24. }

然后注册一个对应的调用器,示例代码如下:

  1. <?php
  2. namespace App\Utility;
  3. use EasySwoole\Component\Singleton;
  4. use EasySwoole\SyncInvoker\SyncInvoker;
  5. // 注册一个对应的调用器
  6. class MyInvoker extends SyncInvoker
  7. {
  8. use Singleton;
  9. }

最后在 EasySwoole全局事件 中 的 mainServerCreate 事件中进行注册

  1. <?php
  2. namespace EasySwoole\EasySwoole;
  3. use EasySwoole\EasySwoole\AbstractInterface\Event;
  4. use EasySwoole\EasySwoole\Swoole\EventRegister;
  5. class EasySwooleEvent implements Event
  6. {
  7. public static function initialize()
  8. {
  9. // TODO: Implement initialize() method.
  10. date_default_timezone_set('Asia/Shanghai');
  11. }
  12. public static function mainServerCreate(EventRegister $register)
  13. {
  14. $invokerConfig = \App\Utility\MyInvoker::getInstance()->getConfig();
  15. // 以下这些配置都是可选的,可以使用组件默认的配置
  16. /*
  17. $invokerConfig->setServerName('EasySwoole'); // 设置服务名称,默认为 'EasySwoole'
  18. $invokerConfig->setWorkerNum(3); // 设置 Worker 进程数,默认为 3
  19. $invokerConfig->setTempDir(EASYSWOOLE_ROOT . '/Temp'); // 设置 unixSocket 存放目录,默认为 系统临时文件存放目录('/tmp')
  20. $invokerConfig->setMaxPackageSize(2 * 1024 * 1024); // 设置最大允许发送数据大小,默认为 2M
  21. $invokerConfig->setTimeout(3.0); // 设置服务调用超时时间,默认为 3.0 秒
  22. $invokerConfig->setAsyncAccept(true); // 设置异步接收数据,默认为 异步接收(不建议修改)
  23. $invokerConfig->setOnWorkerStart(function (\EasySwoole\SyncInvoker\Worker $worker) {
  24. var_dump('worker start at Id ' . $worker->getArg()['workerIndex']);
  25. }); // 设置服务启动时执行的事件回调
  26. */
  27. $invokerConfig->setDriver(new \App\Utility\MyInvokerDriver()); // 设置驱动工作实例【必须配置】
  28. // 注册 Invoker
  29. \App\Utility\MyInvoker::getInstance()->attachServer(ServerManager::getInstance()->getSwooleServer());
  30. }
  31. }

在框架服务启动后,即可在框架的任意位置调用 Invoker 服务了,使用示例如下:

例如在控制器中进行调用:

  1. <?php
  2. namespace App\HttpController;
  3. use EasySwoole\Http\AbstractInterface\Controller;
  4. class Index extends Controller
  5. {
  6. public function index()
  7. {
  8. $ret = \App\Utility\MyInvoker::getInstance()->invoke()->test(1, 2);
  9. var_dump($ret);
  10. var_dump(\App\Utility\MyInvoker::getInstance()->invoke()->a());
  11. $ret = \App\Utility\MyInvoker::getInstance()->invoke()->callback(function (\App\Utility\MyInvokerDriver $driver) {
  12. $std = $driver->getStdClass();
  13. if (isset($std->time)) {
  14. return $driver->response($std->time);
  15. } else {
  16. $std->time = time();
  17. return $driver->response('new set time');
  18. }
  19. });
  20. var_dump($ret);
  21. }
  22. }
  23. /**
  24. * 输出结果:
  25. * int(3)
  26. * string(9) "this is a"
  27. * string(12) "new set time"
  28. * int(3)
  29. * string(9) "this is a"
  30. * int(1611071672)
  31. */

注意事项

  • 尽量使用函数名调用方式,闭包方式调用会存在部分闭包函数序列化失败问题
  • 传递参数,返回结果尽量用数组或者字符串传递,资源对象无法序列化