## 用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