🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
## 用swoole存在的问题 详细见: [严重错误](https://wiki.swoole.com/#/coroutine/notice?id=%e4%b8%a5%e9%87%8d%e9%94%99%e8%af%af) ### 在多个协程间共用一个连接 此框架的 `mysql` 只会创建一次, `单例`。 会导致不同协程之间发生数据错乱。 解决方式: 1. 每个请求都重新创建 `mysql链接` 2. 创建 mysql对象池,参考[# 对象池模式(Pool)](https://learnku.com/docs/study-php-design-patterns/1.0/object-pool-mode-pool/7998) (这个问题此教程不会解决,但是可以参考解决 `request`) ### 使用类静态变量 / 全局变量保存上下文 容器是单例的,所有协程都是共用容器。 像 `request`, 是每个请求独有的,所以需要协程上下文来存储,隔离不同协程之间变量。 ## 创建core/SwooleContext.php 复制这些代码: [正确示例:使用 Context 管理上下文](https://wiki.swoole.com/#/coroutine/notice?id=%e6%ad%a3%e7%a1%ae%e7%a4%ba%e4%be%8b%ef%bc%9a%e4%bd%bf%e7%94%a8context%e7%ae%a1%e7%90%86%e4%b8%8a%e4%b8%8b%e6%96%87) ``` <?php namespace core; use Swoole\Coroutine; class SwooleContext { // 所有协程的变量存储 protected static $pool = []; static function get($key) { $cid = Coroutine::getuid(); // 获取当前协程id if ($cid < 0) { return null; } if(isset(self::$pool[$cid][$key])){ return self::$pool[$cid][$key]; } return null; } static function put($key, $item) { $cid = Coroutine::getuid(); if ($cid > 0) { self::$pool[$cid][$key] = $item; } } // 删除当前协程的变量 static function delete($key = null) { $cid = Coroutine::getuid(); if ($cid > 0) { if($key){ unset(self::$pool[$cid][$key]); }else{ unset(self::$pool[$cid]); } } } } ``` ### 修改swoole.php (改几处代码) ``` <?php use core\request\PhpRequest; error_reporting(E_ALL); ini_set("display_errors","On"); require_once __DIR__ . '/vendor/autoload.php'; // 引入自动加载 require_once __DIR__ . '/app.php'; // 框架的文件 $start = function() { Swoole\Runtime::enableCoroutine($flags = SWOOLE_HOOK_ALL); // 如果你用了CURL PDO之类的客户端 请开启这个客户端协程化 // 详细见文档: https://wiki.swoole.com/#/runtime // 绑定主机 端口 $http = new Swoole\Http\Server('0.0.0.0', 9501); $http->set([ // 进程pid文件 'pid_file' => FRAME_BASE_PATH.'/storage/swoole.pid', 'enable_coroutine' => true, // 开启异步协程化 默认开启 'worker_num' => 4 // Worker进程数 跟协程处理有关系 https://wiki.swoole.com/#/server/setting?id=worker_num ]); // 绑定request app()->bind(\core\request\RequestInterface::class,function () { return \core\SwooleContext::get('request'); },false); // 肯定要设置成false 让他每次都调用 $http->on('request', function ($request, $response) { echo Swoole\Coroutine::getCid().PHP_EOL; // 打印当前协程id // 文档:https://wiki.swoole.com/#/coroutine/coroutine?id=getcid // 绑定request 现在这个是有问题 因为容器的单例的 如果request会一直变 // 应该使用协程上下文来保存request 下一篇文章会讲 $server = $request->server; /* app()->bind(\core\request\RequestInterface::class,function () use ($server){ return PhpRequest::create( $server['path_info'], $server['request_method'], $server ); },false); */ // 用协程上下文保存当前的request \core\SwooleContext::put('request',PhpRequest::create( $server['path_info'], $server['request_method'], $server )); $response->end( app('response')->setContent( // 响应 app('router')->dispatch( // 路由 app(\core\request\RequestInterface::class) // 请求 ) )->getContent() ); \core\SwooleContext::delete(); // 请求结束删除当前协程的变量 否则会内存泄漏 }); echo 'start ok'.PHP_EOL; $http->start(); }; $stop = function () { if(! file_exists(FRAME_BASE_PATH.'/storage/swoole.pid')) return; $pid = file_get_contents(FRAME_BASE_PATH.'/storage/swoole.pid'); Swoole\Process::kill($pid); // 见文档 }; $handle = $argv[1]; // 启动 if( $handle == 'start') $start(); // 停止 elseif( $handle == 'stop'); $stop(); ``` ## 结尾 禁止改变 `config` 的信息,因为后续请求也会受到影响。 (解决方式也是协程上下文)