亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 語言 > PHP > 正文

php-msf源碼詳解

2024-05-05 00:01:36
字體:
來源:轉載
供稿:網友

我們來看分享下具體源碼:php-msf: https://github.com/pinguo/php-msf

源碼解讀也做了一段時間了, 總結一下自己的心得:

抓住 生命周期, 讓代碼在你腦海中 跑起來

分析架構, 關鍵字 分層 邊界 隔離

一個好的框架, 弄清楚 生命周期 和 架構, 基本就已經到了 熟悉 的狀態了, 之后是填充細節和編碼熟練了

這里再介紹幾個次重要的心得:

弄明白這個工具擅長干什么, 適合干什么. 這個信息也非常容易獲取到, 工具的文檔通常都會顯眼標注出來, 可以通過這些 功能/特性, 嘗試以點見面

從工程化的角度去看這個項目, 主要和上面的 架構 區分, 在處理核心業務, 也就是上面的 功能/特性 外, 工程化還涉及到 安全/測試/編碼規范/語言特性 等方面, 這些也是平時在寫業務代碼時思考較少并且實踐較少的部分

工具的使用, 推薦我現在使用的組合: phpstorm + 百度腦圖 + Markdown筆記 + blog和 php-msf 的淵源等寫技術生活相關的 blog 再來和大家八, 直接上菜.

生命周期 & 架構

官方文檔制作了一張非常好的圖: 處理請求流程圖. 推薦各位同仁, 有閑暇時制作類似的圖, 對思維很有的幫助.

根據這張圖來思考 生命周期 & 架構, 這里就不贅述了, 這里分析一下 msf 中一些技術點:

協程相關知識

msf 中技術點摘錄

協程

我會用我的方式來講解, 如果需要深入了解的, 可以看我后面推薦的資源.

類 vs 對象 是一組很重要的概念. 類代表我們對事物的抽象, 這個抽象的能力在我們以后會一直用到, 希望大家有意識的培養這方面的意識, 至少可以起到觸類旁通的作用. 對象是 實例化 的類, 是 真正干活的, 我們要討論的 協程, 就是這樣一個 真正干活的 角色.

協程從哪里來, 到哪里去, 它是干什么的?

想一想這幾個簡單的問題, 也許你對協程的理解就更深刻了, 記住這幾個關鍵詞:

產生. 需要有地方來產生協程, 你可能不需要知道細節, 但是需要知道什么時候發生了

調度. 肯定是有很多協程一起工作的, 所以需要調度, 怎么調度的呢?

銷毀. 是否會銷毀? 什么時候銷毀?

現在, 我們再來看看協程的使用方式對比, 這里注意一下, 我沒有用 協程的實現方式對比, 因為很多時候, 需求實際是這樣的:

怎么實現我不管, 我選最好用的.

// msf - 單次協程調度$response = yield $this->getRedisPool('tw')->get('apiCacheForABCoroutine');// msf - 并發協程調用$client1 = $this->getObject(Client::class, ['http://www.baidu.com/']);yield $client1->goDnsLookup();$client2 = $this->getObject(Client::class, ['http://www.qq.com/']);yield $client2->goDnsLookup();$result[] = yield $client1->goGet('/');$result[] = yield $client2->goGet('/');

大致 是這樣的一個等式: 使用協程 = 加上 yield, 所以搞清楚哪些地方需要加上 yield 就好了 -- 有阻塞IO的地方, 比如 文件IO, 網絡IO(redis/mysql/http) 等.

當然, 大致 就是還有需要注意的地方

協程調度順序, 如果不注意, 就可能會退化成同步調用.

調用鏈: 使用 yield 的調用鏈上, 都需要加上 yield. 比如下面這樣:

function a_test() {  return yield $this->getRedisPool('tw')->get('apiCacheForABCoroutine');}$res = yield a_test(); // 如果不加 yield, 就變成了同步執行

對比一下 swoole2.0 的協程方案:

$server = new Swoole/Http/Server("127.0.0.1", 9502, SWOOLE_BASE);$server->set([  'worker_num' => 1,]);// 需要在協程 server 的異步回調函數中$server->on('Request', function ($request, $response) {  $tcpclient = new Swoole/Coroutine/Client(SWOOLE_SOCK_TCP); // 需要配合使用協程客戶端  $tcpclient->connect('127.0.0.1', 9501,0.5)  $tcpclient->send("hello world/n");  $redis = new Swoole/Coroutine/Redis();  $redis->connect('127.0.0.1', 6379);  $redis->setDefer(); // 標注延遲收包, 實現并發調用  $redis->get('key');  $mysql = new Swoole/Coroutine/MySQL();  $mysql->connect([    'host' => '127.0.0.1',    'user' => 'user',    'password' => 'pass',    'database' => 'test',  ]);  $mysql->setDefer();  $mysql->query('select sleep(1)');  $httpclient = new Swoole/Coroutine/Http/Client('0.0.0.0', 9599);  $httpclient->setHeaders(['Host' => "api.mp.qq.com"]);  $httpclient->set([ 'timeout' => 1]);  $httpclient->setDefer();  $httpclient->get('/');  $tcp_res = $tcpclient->recv();  $redis_res = $redis->recv();  $mysql_res = $mysql->recv();  $http_res = $httpclient->recv();  $response->end('Test End');});$server->start();

使用 swoole2.0 的協程方案, 好處很明顯:

不用加 yield 了

并發調用不用刻意注意 yield 的順序了, 使用 defer() 延遲收包即可

但是, 沒辦法直接用 使用協程 = 加上 yield 這樣一個簡單的等式了, 上面的例子需要配合使用 swoole 協程 server + swoole 協程 client:

server 在異步回調觸發時 生成協程

client 觸發 協程調度

異步回調執行結束時 銷毀協程

這就導致了 2 個問題:

不在 swoole 協程 server 的異步回調中怎么辦: 使用 Swoole/Coroutine::create() 顯式生成協程

需要使用其他的協程 Client 怎么辦: 這是 Swoole3 的目標, Swoole2.0 可以考慮用協程 task 來偽裝

這樣看起來, 好像 使用協程 = 加上 yield 這樣要簡單一些? 我不這樣認為, 補充一些觀點, 大家自己斟酌:

使用 yield 的方式, 基于 php 生成器 + 自己實現 PHP 協程調度器, 想要用起來不出錯, 比如上面 協程調度順序, 你還是需要去弄清楚這塊的實現

Swoole2.0 的原生方式, 理解起來其實更容易, 只需要知道協程 生成/調度/銷毀 的時機就可以用好

Swoole2.0 這樣異步回調中頻繁創建和銷毀協程, 是否十分損耗性能? -- 不會的, 實際是一些內存操作, 比進程/對象小很多

msf 中技術點摘錄

msf 在設計上有很多出彩的地方, 很多代碼都值得借鑒.

請求上下文 Context

這是從 fpm 到 swoole http server 非常重要的概念. fpm 是多進程模式, 雖然 $_POST 等變量, 被稱之為超全局變量, 但是, 這些變量在不同 fpm 進程間是隔離的. 但是到了 swoole http server 中, 一個 worker 進程, 會異步處理多個請求, 簡單理解就是下面的等式:

fpm worker : http request = 1 : 1swoole worker : http request = 1 : n

所以, 我們就需要一種新的方式, 來進行 request 間的隔離.

在編程語言里, 有一個專業詞匯 scope(作用域). 通常會使用 scope/生命周期, 所以我一直強調的生命周期的概念, 真的很重要.

swoole 本身是實現了隔離的:

$http = new swoole_http_server("127.0.0.1", 9501);$http->on('request', function ($request, $response) {  $response->end("<h1>Hello Swoole. #".rand(1000, 9999)."</h1>");});$http->start();

msf 在 Context 上還做了一層封裝, 讓 Context 看起來 為所欲為:

// 你幾乎可以用這種方式, 完成任何需要的邏輯$this->getContext()->xxxModule->xxxModuleFunction();

細節可以查看 src/Helpers/Context.php 文件

對象池

對象池這個概念, 大家可能比較陌生, 目的是減少對象的頻繁創建與銷毀, 以此來提升性能, msf 做了很好的封裝, 使用很簡單:

// getObject() 就可以了/** @var DemoModel $demoModel */$demoModel = $this->getObject(DemoModel::class, [1, 2]);

對象池的具體代碼在 src/Base/Pool.php 下:

底層使用反射來實現對象的動態創建

public function get($class, ...$args){  $poolName = trim($class, '//');  if (!$poolName) {    return null;  }  $pool   = $this->map[$poolName] ?? null;  if ($pool == null) {    $pool = $this->applyNewPool($poolName);  }  if ($pool->count()) {    $obj = $pool->shift();    $obj->__isConstruct = false;    return $obj;  } else {    // 使用反射    $reflector     = new /ReflectionClass($poolName);    $obj        = $reflector->newInstanceWithoutConstructor();    $obj->__useCount  = 0;    $obj->__genTime  = time();    $obj->__isConstruct = false;    $obj->__DSLevel  = Macro::DS_PUBLIC;    unset($reflector);    return $obj;  }}

使用 SplStack 來管理對象

private function applyNewPool($poolName){  if (array_key_exists($poolName, $this->map)) {    throw new Exception('the name is exists in pool map');  }  $this->map[$poolName] = new /SplStack();  return $this->map[$poolName];}// 管理對象$pool->push($classInstance);$obj = $pool->shift();

連接池 & 代理

連接池 Pools

連接池的概念就不贅述了, 我們來直接看 msf 中的實現, 代碼在 src/Pools/AsynPool.php 下:

public function __construct($config){  $this->callBacks = [];  $this->commands = new /SplQueue();  $this->pool   = new /SplQueue();  $this->config  = $config;}

這里使用的 SplQueue 來管理連接和需要執行的命令. 可以和上面對比一下, 想一想為什么一個使用 SplStack, 一個使用 SplQueue.

代理 Proxy

代理是在連接池的基礎上進一步的封裝, msf 提供了 2 種封裝方式:

主從 master slave

集群 cluster

查看示例 App/Controllers/Redis 中的代碼:

class Redis extends Controller{  // Redis連接池讀寫示例  public function actionPoolSetGet()  {    yield $this->getRedisPool('p1')->set('key1', 'val1');    $val = yield $this->getRedisPool('p1')->get('key1');    $this->outputJson($val);  }  // Redis代理使用示例(分布式)  public function actionProxySetGet()  {    for ($i = 0; $i <= 100; $i++) {      yield $this->getRedisProxy('cluster')->set('proxy' . $i, $i);    }    $val = yield $this->getRedisProxy('cluster')->get('proxy22');    $this->outputJson($val);  }  // Redis代理使用示例(主從)  public function actionMaserSlaveSetGet()  {    for ($i = 0; $i <= 100; $i++) {      yield $this->getRedisProxy('master_slave')->set('M' . $i, $i);    }    $val = yield $this->getRedisProxy('master_slave')->get('M66');    $this->outputJson($val);  }}

代理就是在連接池的基礎上進一步 搞事情. 以 主從 模式為例:

主從策略: 讀主庫, 寫從庫
代理做的事情:

判斷是讀操作還是寫操作, 選擇相應的庫去執行
公共庫

msf 推行 公共庫 的做法, 希望不同功能組件可以做到 可插拔, 這一點可以看 laravel 框架和 symfony 框架, 都由框架核心加一個個的 package 組成. 這種思想我是非常推薦的, 但是仔細看 百度腦圖 - php-msf 源碼解讀 這張圖的話, 就會發現類與類之間的依賴關系, 分層/邊界 做得并不好. 如果看過我之前的 blog - laravel源碼解讀 / blog - yii源碼解讀, 進行對比就會感受很明顯.

但是, 這并不意味著 代碼不好, 至少功能正常的代碼, 幾乎都能算是好代碼. 從功能之外建立的 優越感, 更多的是對 美好生活的向往 -- 還可以更好一點.

AOP

php AOP 擴展: http://pecl.php.net/package/aop

PHP-AOP擴展介紹 | rango: http://rango.swoole.com/archives/83

AOP, 面向切面編程, 韓老大 的 blog - PHP-AOP擴展介紹 | rango 可以看看.

需不需要了解一個新事物, 先看看這個事物有什么作用:

AOP, 將業務代碼和業務無關的代碼進行分離, 場景有 日志記錄 / 性能統計 / 安全控制 / 事務處理 / 異常處理 / 緩存 等等.
這里引用一段 程序員DD - 翟永超的公眾號 文章里的代碼, 讓大家感受下:

同樣是 CRUD, 不使用 AOP

@PostMapping("/delete")public Map<String, Object> delete(long id, String lang) { Map<String, Object> data = new HashMap<String, Object>(); boolean result = false; try {  // 語言(中英文提示不同)  Locale local = "zh".equalsIgnoreCase(lang) ? Locale.CHINESE : Locale.ENGLISH;  result = configService.delete(id, local);  data.put("code", 0); } catch (CheckException e) {  // 參數等校驗出錯,這類異常屬于已知異常,不需要打印堆棧,返回碼為-1  data.put("code", -1);  data.put("msg", e.getMessage()); } catch (Exception e) {  // 其他未知異常,需要打印堆棧分析用,返回碼為99  log.error(e);  data.put("code", 99);  data.put("msg", e.toString()); } data.put("result", result); return data;}

使用 AOP

@PostMapping("/delete")public ResultBean<Boolean> delete(long id) { return new ResultBean<Boolean>(configService.delete(id));}

代碼只用一行, 需要的特性一個沒少, 你是不是也想寫這樣的 CRUD 代碼?

配置文件管理

先明確一下配置管理的痛點:

是否支撐熱更新, 常駐內存需要考慮

考慮不同環境: dev test production

方便使用

熱更其實可以算是常駐內存服務器的整體需求, 目前 php 常用的解決方案是 inotify, 可以參考我之前的 blog - swoft 源碼解讀 .

msf 使用第三方庫來解析處理配置文件, 這里著重提一個 array_merge() 的細節:

$a = ['a' => [  'a1' => 'a1',]];$b = ['a' => [  'b1' => 'b1',]];$arr = array_merge($a, $b); // 注意, array_merge() 并不會循環合并var_dump($arr);// 結果array(1) { ["a"]=> array(1) {  ["b1"]=>  string(2) "b1" }}

msf 中使用配置:

$ids = $this->getConfig()->get('params.mock_ids', []);// 對比一下 laravel$ids = cofnig('params.mock_ids', []);

看起來 laravel 中要簡單一些, 其實是通過 composer autoload 來加載函數, 這個函數對實際的操作包裝了一層. 至于要不要這樣做, 就看自己需求了.

寫在最后

msf 最復雜的部分在 服務啟動階段, 繼承也很長:

Child -> Server -> HttpServer -> MSFServer -> AppServer, 有興趣可以挑戰一下.

另外一個比較難的點, 是 MongoDbTask 實現原理.

msf 還封裝了很多有用的功能, RPC / 消息隊列 / restful, 大家根據文檔自己探索即可.

 

注:相關教程知識閱讀請移步到PHP教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表

圖片精選

亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美成人激情视频免费观看| 亚洲国产免费av| 国产精品视频一| 最近日韩中文字幕中文| 国产精品网站入口| 成人激情视频网| 久久这里有精品视频| 一区二区三区在线播放欧美| 78色国产精品| 91美女片黄在线观看游戏| 国产成人精品久久二区二区| 亚洲中国色老太| www.色综合| 国产精品久久久久久久一区探花| 九九热精品视频在线播放| xxxx欧美18另类的高清| 国产欧美日韩精品专区| 久久偷看各类女兵18女厕嘘嘘| 国产精品一区二区久久久久| 国产免费一区二区三区香蕉精| 91av在线免费观看| 欧美插天视频在线播放| 国产一区二区在线免费| 国产精品一区二区三区久久| 91久久精品美女| 91国偷自产一区二区三区的观看方式| 91美女片黄在线观看游戏| 亚洲国产成人久久综合| 高清欧美性猛交xxxx| 91免费精品国偷自产在线| 亚洲无亚洲人成网站77777| 亚洲自拍偷拍色片视频| 欧美理论片在线观看| 亚洲国产中文字幕久久网| 精品女同一区二区三区在线播放| 国产精品18久久久久久麻辣| 欧美午夜片在线免费观看| 国产精品99久久久久久www| 97香蕉久久夜色精品国产| 亚洲欧洲日本专区| 亚洲天堂网站在线观看视频| 狠狠操狠狠色综合网| 一区二区欧美亚洲| 国产精品美女网站| 欧美成aaa人片免费看| 日韩动漫免费观看电视剧高清| 美女黄色丝袜一区| 日韩中文字幕免费| 国产免费一区二区三区在线观看| 久久男人av资源网站| 欧美日韩午夜剧场| 中文字幕欧美在线| 久久久亚洲精品视频| 国产精品一区二区三| 亚洲人成在线观看网站高清| 69久久夜色精品国产69乱青草| 成人精品久久一区二区三区| 亚洲国产精品电影| 国产精品久久久久国产a级| 综合136福利视频在线| 欧美电影在线观看| 成人av资源在线播放| 成人久久久久爱| 国产精品69久久| 最近2019免费中文字幕视频三| 精品久久香蕉国产线看观看gif| 亚洲人在线视频| 国产成人久久久| 中文字幕在线看视频国产欧美| 国产精品一区二区三区毛片淫片| 亚洲全黄一级网站| 欧美性做爰毛片| 国产亚洲人成a一在线v站| 夜夜狂射影院欧美极品| 欧美韩国理论所午夜片917电影| 久久精品国产亚洲精品| 日韩精品免费在线视频观看| 一本大道久久加勒比香蕉| 黑人巨大精品欧美一区二区免费| 这里精品视频免费| 国产一区二区三区欧美| 成人免费视频网址| 欧美午夜精品久久久久久浪潮| 日韩av免费看网站| 欧美电影院免费观看| 欧美中在线观看| 中文字幕成人在线| 精品国产区一区二区三区在线观看| 欧洲精品在线视频| 久久久久久久久综合| 欧美人成在线视频| 中文字幕亚洲图片| 国产精品成av人在线视午夜片| 欧美人交a欧美精品| 91精品国产综合久久久久久久久| 国产精品视频免费在线观看| 日本视频久久久| 日韩欧美高清视频| 久久精品国产亚洲7777| 午夜精品美女自拍福到在线| 全球成人中文在线| 日韩福利伦理影院免费| 色综合久综合久久综合久鬼88| 91在线视频导航| 国产精品精品国产| 国产91露脸中文字幕在线| 国产福利视频一区二区| 亚洲一区二区久久久久久久| 欧美成人午夜激情在线| 91精品国产综合久久久久久久久| 7m第一福利500精品视频| 国产综合视频在线观看| 久久久国产精品亚洲一区| 午夜精品久久久久久久白皮肤| 中文字幕欧美日韩精品| 国产精品男女猛烈高潮激情| 国产亚洲精品久久久久久777| 精品国产一区二区三区久久狼5月| 亚洲国产精品系列| 91久久嫩草影院一区二区| 亚洲成人激情在线观看| 日韩精品在线免费观看视频| 国产成人福利视频| 欧美日韩在线观看视频| 夜夜躁日日躁狠狠久久88av| 久久这里有精品| 成人有码视频在线播放| 国产亚洲欧美aaaa| 欧美大胆在线视频| 欧美日韩中国免费专区在线看| 一区二区三区在线播放欧美| 日韩有码在线电影| 疯狂欧美牲乱大交777| 成人妇女淫片aaaa视频| 国产精品v片在线观看不卡| 欧美丰满片xxx777| 久久91精品国产91久久久| 日本不卡高字幕在线2019| 亚洲第一综合天堂另类专| 久久99青青精品免费观看| 亚洲午夜国产成人av电影男同| 一区二区三区 在线观看视| 国产69久久精品成人看| 国产97在线视频| 国产一区二区在线播放| 久久久亚洲福利精品午夜| 亚洲另类激情图| 国产精品户外野外| 亚洲成人a**站| 色偷偷av一区二区三区乱| 亚洲精品v欧美精品v日韩精品| 亚洲aaaaaa| 国产精品久久国产精品99gif| 岛国精品视频在线播放| 亚洲精品国产品国语在线| 亚洲欧美精品伊人久久| 亚洲精品电影在线| 国产精品免费久久久久久| 欧美激情一区二区三区成人| 久久精品男人天堂| 亚洲国产精品成人av| 亚洲淫片在线视频| 亚洲精品国产精品久久清纯直播| 国产精品91在线|