## 用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` 的信息,因为后续请求也会受到影响。
(解决方式也是协程上下文)
- 前言
- 基础篇
- 1. 第一步 创建框架目录结构
- 2. 引入composer自动加载
- 3. php自动加载 (解释篇)
- 4. 创建容器 注册树模式
- 5. 关于psr规范解释
- 6. 关于"容器" "契约" "依赖注入" (解释篇)
- 7. 添加函数文件helpers.php
- 8. 初始化请求(Request)
- 9. 响应 (Response)
- 10. 路由一 (路由组实现)
- 11. 路由二 (加入中间件)
- 12. 配置信息 (类似laravel)
- 13. 数据库连接 (多例模式)
- 14. 查询构造器 (query builder)
- MVC实现
- M 模型实现 (数据映射 + 原型 模式)
- C 控制器实现 + 控制器中间件
- V 视图实现 (Laravel Blade 引擎)
- V 视图切换成 ThinkPhp 模板 引擎)
- 其他轮子
- 日志
- 自定义异常 (异常托管)
- 单元测试 (phpunit)
- 替换成swoole的http服务器
- 协程上下文解决request问题
- qps测试
- 发布到packagist.org