成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Swoft 源碼解讀

weij / 2814人閱讀

摘要:官網(wǎng)源碼解讀號(hào)外號(hào)外歡迎大家我們開發(fā)組定了一個(gè)就線下聚一次的小目標(biāo)里面的框架算是非常重的了這里的重先不具體到性能層面主要是框架的設(shè)計(jì)思想和框架集成的服務(wù)讓框架可以既可以快速解決很多問題又可以輕松擴(kuò)展中的框架有在應(yīng)該無出其右了這次解讀的源碼

官網(wǎng): https://www.swoft.org/

源碼解讀: http://naotu.baidu.com/file/8...

號(hào)外號(hào)外, 歡迎大家 star, 我們開發(fā)組定了一個(gè) star 1000+ 就線下聚一次的小目標(biāo)

PHP 里面的 yii/laravel 框架算是非?!钢亍沟牧? 這里的 先不具體到 性能 層面, 主要是框架的設(shè)計(jì)思想和框架集成的服務(wù), 讓框架可以既可以快速解決很多問題, 又可以輕松擴(kuò)展.

PHP 中的框架, 有 yii/laravel 在, 應(yīng)該無出其右了.

這次解讀 swoft 的源碼 -- 基于 swoole2.0 原生協(xié)程的框架. 同時(shí), swoft 使用了大量 swoole 提供的功能, 也非常適合閱讀它的代碼, 來學(xué)習(xí)如何造輪子. 其實(shí)解讀過 yii/laravel 這樣的框架后, 一些 通用 的框架設(shè)計(jì)思想就不贅述了, 主要講解和 服務(wù)器開發(fā) 相關(guān)的部分, 思路也會(huì)按照官網(wǎng)的 feature list 展開.

前半部分聚焦框架常用的功能:

全局容器注入 & MVC 分層設(shè)計(jì)

注解機(jī)制(亮點(diǎn), 強(qiáng)烈推薦了解一下)

高性能路由

別名機(jī)制 $aliases

RestFul風(fēng)格

事件機(jī)制

強(qiáng)大的日志系統(tǒng)

國(guó)際化(i18n)

數(shù)據(jù)庫(kù) ORM

后半部分聚焦服務(wù)器相關(guān)的功能:

基礎(chǔ)概念(亮點(diǎn), 第一個(gè)基于 swoole2.0 原生協(xié)程的框架)

連接池

服務(wù)治理熔斷、降級(jí)、負(fù)載、注冊(cè)與發(fā)現(xiàn)

任務(wù)投遞 & Crontab 定時(shí)任務(wù)

用戶自定義進(jìn)程

Inotify 自動(dòng) Reload

PHP 框架的設(shè)計(jì), 可以參考 [PSR(PHP Standards Recommendations
)](http://www.php-fig.org/psr/).
全局容器注入 & MVC 分層設(shè)計(jì)

之所以把這 2 個(gè)放一起講, 是因?yàn)橐粋€(gè)是 , 一個(gè)是 . 只是新人聽得比較多的是 MVC 的分層設(shè)計(jì)思想, 全局容器注入了解相對(duì)較少.

MVC 分層設(shè)計(jì): 更偏向于業(yè)務(wù)

MVC 是一種簡(jiǎn)單通用并且實(shí)用的 對(duì)業(yè)務(wù)進(jìn)行拆分然后加以實(shí)現(xiàn) 的設(shè)計(jì), 本質(zhì)還是 分層設(shè)計(jì). 更重要的, 還是掌握 分層設(shè)計(jì) 的思想, 這個(gè)在工程實(shí)踐中大量的使用到, 比如 OSI 7 層網(wǎng)絡(luò)模型 和 TCP/IP 4 層網(wǎng)絡(luò)模型. 我分層設(shè)計(jì)可以有效的確定 系統(tǒng)邊界和職責(zé)劃分.

想要培養(yǎng)分層設(shè)計(jì)的思想, 其實(shí)可以從 入手, 在拆輪子然后拼輪子的過程中, 你會(huì)驚奇的發(fā)現(xiàn), 藝術(shù)就在其中.

榫卯 app: https://www.douban.com/note/3...

全局容器注入

在進(jìn)入這個(gè)概念之前, 先要認(rèn)清另一個(gè)概念: 面向?qū)ο缶幊?/strong>. 更常用的可能是 面向過程編程 vs 面向?qū)ο缶幊?/strong>. 這里不會(huì)長(zhǎng)篇大論, 只就思維方式來進(jìn)行比較:

面向過程編程: 一條接一條指令的執(zhí)行, 這是計(jì)算機(jī)喜歡的方式

面向?qū)ο缶幊? 通過對(duì)象來 抽象 里面不同的事物, 通過事物之間的聯(lián)系, 來解決與之相關(guān)的業(yè)務(wù).

從這個(gè)角度來看, 面向?qū)ο?/strong> 可能是更符合人類的思維方式, 或者說更智能的思維方式:

上者勞人. 抽象好管理對(duì)象, 從而更好的完成任務(wù).

但是使用面向?qū)ο缶幊痰倪^程中, 就會(huì)出現(xiàn)一個(gè)問題: new, 需要管理好對(duì)象之間依賴關(guān)系, 全局容器注入就是做這樣一件事. 使用 new, 表明一個(gè)對(duì)象需要依賴另一個(gè)對(duì)象, 但是使用容器, 則是一個(gè)對(duì)象告訴容器它需要什么對(duì)象.

怎么實(shí)現(xiàn)我不管 -- 這就是使用 new 和容器注入的區(qū)別, 學(xué)名叫 控制反轉(zhuǎn).

所以, 容器是 , 在處理具體業(yè)務(wù)時(shí), 由容器按需提供相應(yīng)的 MVC 對(duì)象來處理.

注解進(jìn)制

在容器的實(shí)現(xiàn)上, 或者說框架的底層上, 其實(shí)各個(gè)框架都 大同小異. 這里說一下 swoft 不同的地方 -- 引入注解進(jìn)制.

簡(jiǎn)單解釋一下注解進(jìn)制: 通過添加注釋 & 解析注釋, 將注釋轉(zhuǎn)化為一些特定的有意義的代碼.

更簡(jiǎn)單一點(diǎn): 注釋 == 代碼

實(shí)現(xiàn)起來其實(shí)也很簡(jiǎn)單, 只是可能接觸的比較少 -- 反射:

// BeanParserInjectParser
class InjectParser extends AbstractParser
{

    /**
     * Inject注解解析
     *
     * @param string $className
     * @param object $objectAnnotation
     * @param string $propertyName
     * @param string $methodName
     *
     * @return array
     */
    public function parser(string $className, $objectAnnotation = null, string $propertyName = "", string $methodName = "", $propertyValue = null)
    {
        $injectValue = $objectAnnotation->getName();
        if (!empty($injectValue)) {
            return [$injectValue, true];
        }

        // phpdoc解析
        $phpReader = new PhpDocReader(); // 將注釋轉(zhuǎn)化為類
        $property = new ReflectionProperty($className, $propertyName); // 使用反射
        $propertyClass = $phpReader->getPropertyClass($property);

        $isRef = true;
        $injectProperty = $propertyClass;
        return [$injectProperty, $isRef];
    }
}

如果熟悉 java, 會(huì)發(fā)現(xiàn)里面有很多地方在方法前用到了 @override, 在 symfony 中也使用到了這樣的方式. 好處是一定程度的內(nèi)聚, 使用起來更加簡(jiǎn)潔, 而且可以減少配置.

高性能路由

首先回答一個(gè)問題, 路由是什么? 從對(duì)象的角度出發(fā), 其實(shí)路由就對(duì)應(yīng) URL. 那 URL 是什么呢?

URL, Uniform Resource Locator, 統(tǒng)一資源定位符.

所以, 路由這一層抽象, 就是為了解決 -- 找到 URL 對(duì)應(yīng)需要執(zhí)行的邏輯.

現(xiàn)在再來解釋一下 swoft 提到的高性能:

// app/routes.php: 路由配置文件
$router = SwoftApp::getBean("httpRouter"); // 通過容器拿 httpRouter

// config/beans/base.php: beans 配置文件
"httpRouter"      => [
    "class"          => SwoftRouterHttpHandlerMapping::class, // httpRouter 其實(shí)對(duì)應(yīng)這個(gè)
    "ignoreLastSep"  => false,
    "tmpCacheNumber" => 1000,
    "matchAll"       => "",
],

// SwoftRouterHttpHandlerMapping
private $cacheCounter = 0;
private $staticRoutes = []; // 靜態(tài)路由
private $regularRoutes = []; // 動(dòng)態(tài)路由
protected function cacheMatchedParamRoute($path, array $conf){} // 會(huì)緩存匹配到的路由
// 路由匹配的方法也很簡(jiǎn)單: 校驗(yàn) -> 處理靜態(tài)路由 -> 處理動(dòng)態(tài)路由
public function map($methods, $route, $handler, array $opts = [])
{
    ...
    $methods = static::validateArguments($methods, $handler);
    ...
    if (self::isNoDynamicParam($route)) {
        ...
    }
    ...
    list($first, $conf) = static::parseParamRoute($route, $params, $conf);
}

高性能 = 路由匹配邏輯簡(jiǎn)單 + 路由緩存

別名機(jī)制 $aliases

用過 yii 的對(duì)這個(gè)就比較熟悉了, 其實(shí)是這樣一個(gè) 進(jìn)化過程:

使用 __DIR__ / DIRECTORY_SEPARATOR 等拼接出絕對(duì)路徑

使用 define() / defined() 定義全局變量來使用路徑

使用 $aliases 變量替代全局變量

這里只展示一下配置的地方, 實(shí)現(xiàn)只是在類中開一個(gè)變 $aliases 屬性存儲(chǔ)一下就行了:

// config/define.php
// 基礎(chǔ)根目錄
!defined("BASE_PATH") && define("BASE_PATH", dirname(__DIR__, 1));
// 注冊(cè)別名
$aliases = [
    "@root"       => BASE_PATH,
    "@app"        => "@root/app",
    "@res"        => "@root/resources",
    "@runtime"    => "@root/runtime",
    "@configs"    => "@root/config",
    "@resources"  => "@root/resources",
    "@beans"      => "@configs/beans",
    "@properties" => "@configs/properties",
    "@commands"   => "@app/Commands"
];
App::setAliases($aliases);
RestFul風(fēng)格

restful 的思想其實(shí)很簡(jiǎn)單: 以資源為核心, 業(yè)務(wù)其實(shí)是圍繞資源的增刪改查. 具體到 http 中:

url 只作為資源標(biāo)識(shí), 有 2 種形式, itemitem/id, 后者表示操作具體某個(gè)資源

http method(get/post/put等)用來對(duì)應(yīng)資源的 CRUD

使用 json 格式進(jìn)行數(shù)據(jù)的 輸入輸出

實(shí)現(xiàn)起來也很簡(jiǎn)單: 路由 + 返回

事件機(jī)制

先用 3W1H(who what why how) 分析法的思路來解釋一下 事件機(jī)制, 更重要的是, 這個(gè)有什么用.

正常的程序執(zhí)行, 或者說人的思維趨勢(shì), 都是按照 時(shí)間線性串行 的, 保持 連續(xù)性. 不過現(xiàn)實(shí)中會(huì)存在各種 打斷, 程序也不是永遠(yuǎn)都是 就緒狀態(tài), 那么, 就需要有一種機(jī)制, 來處理可能出現(xiàn)的各種打斷, 或者在程序不同狀態(tài)之間切換.

事件機(jī)制發(fā)展到現(xiàn)在, 有時(shí)候也算是一種預(yù)留手段, 根據(jù)你的經(jīng)驗(yàn)在需要的地方 埋點(diǎn), 方便之后 打補(bǔ)丁.

swoft 的事件機(jī)制基于 PSR-14 實(shí)現(xiàn), 高度內(nèi)聚簡(jiǎn)潔.

由三部分組成:

EventManager: 事件管理器

Event: 事件

EventHandler / Listener: 事件處理器/監(jiān)聽器

執(zhí)行流程:

先生成 EventManager

將 Event 和 EventHandler 注冊(cè)到 EventManager

觸發(fā) Event, EventManager 就會(huì)調(diào)用相應(yīng)的 EventHandler

使用起來就更加簡(jiǎn)單了:

use SwoftEventEventManager;

$em = new EventManager;

// 注冊(cè)事件監(jiān)聽
$em->attach("someEvent", "callback_handler"); // 這里也可以使用注解機(jī)制, 實(shí)現(xiàn)事件監(jiān)聽注冊(cè)

// 觸發(fā)事件
$em->trigger("someEvent", "target", ["more params"]);

// 也可以
$event = new Event("someEvent", ["more params"]);
$em->trigger($event);

來看一下 swoft 在事件機(jī)制這里用來提升性能的亮點(diǎn):

namespace SwoftEvent;

class ListenerQueue implements IteratorAggregate, Countable
{
    protected $store;

    /**
     * 優(yōu)先級(jí)隊(duì)列
     * @var SplPriorityQueue
     */
    protected $queue;

    /**
     * 計(jì)數(shù)器
     * 設(shè)定最大值為 PHP_INT_MAX == 300
     * @var int
     */
    private $counter = PHP_INT_MAX;

    public function __construct()
    {
        $this->store = new SplObjectStorage(); // Event 對(duì)象先添加都這里
        $this->queue = new SplPriorityQueue(); // 然后加入優(yōu)先級(jí)隊(duì)列, 之后進(jìn)行調(diào)度
    }
    ...
}

稍微玩過 ACM 的人對(duì) 優(yōu)先級(jí)隊(duì)列 就不會(huì)陌生了, 基本所有 OJ 都有相關(guān)的題庫(kù). 不過 PHPer 不用太操心底層實(shí)現(xiàn), 直接借助 SPL 庫(kù)即可.

SPL, Standard PHP Library, 類似 C++ 的 STL, PHPer 一定要了解一下.
強(qiáng)大的日志系統(tǒng)

使用 monolog/monolog 來實(shí)現(xiàn)日志系統(tǒng)基本已成為標(biāo)配了, 當(dāng)然底層還是實(shí)現(xiàn) PSR-3 標(biāo)準(zhǔn). 不過這個(gè)標(biāo)準(zhǔn)出現(xiàn)比較早, 發(fā)展到現(xiàn)在, 隱藏得比較深了.

這也是建立技術(shù)標(biāo)準(zhǔn)/協(xié)議的理由, 劃定好 最佳實(shí)踐, 之后的努力都是朝著越來越易用發(fā)展.

swoft 的日志系統(tǒng), 由 2 部分組成:

SwoftLogLogger: 日志主體功能

SwoftLogFileHandler: 輸出日志

至于另一個(gè)文件, SwoftLogLog, 只是對(duì) Logger 的一層封裝, 調(diào)用起來更方便而已.

當(dāng)然, swoft 的日志系統(tǒng)和 yii2 框架有明顯相似的地方:

// 都在 App 中快讀暴露日志功能
public static function info($message, array $context = array())
{
    self::getLogger()->info($message, $context); // 其實(shí)還是使用 Logger 來處理
}

// 都添加了 profile 功能
public static function profileStart(string $name)
{
    self::getLogger()->profileStart($name);
}
public static function profileEnd($name)
{
    self::getLogger()->profileEnd($name);
}

值得一提的是, yii2 框架的日志系統(tǒng)由三部分組成:

Logger: 日志主體功能

Dispatch: 日志分發(fā), 可以將同一個(gè)日志分發(fā)給不同的 Target 處理

Target: 日志消費(fèi)者

這樣的設(shè)計(jì), 其實(shí)是將 FileHandler 的功能進(jìn)行拆解, 更靈活, 更方便擴(kuò)展.

來看看 swoft 日志系統(tǒng)強(qiáng)大的一面:

private function aysncWrite(string $logFile, string $messageText)
{
    while (true) {
        // 使用 swoole 異步文件 IO
        $result = SwooleAsync::writeFile($logFile, $messageText, null, FILE_APPEND);
        if ($result == true) {
            break;
        }
    }
}

當(dāng)然, 也可以選擇同步的方式:

private function syncWrite(string $logFile, string $messageText)
{
    $fp = fopen($logFile, "a");
    if ($fp === false) {
        throw new InvalidArgumentException("Unable to append to log file: {$this->logFile}");
    }
    flock($fp, LOCK_EX); // 注意要加鎖
    fwrite($fp, $messageText);
    flock($fp, LOCK_UN);
    fclose($fp);
}

PS: 日志統(tǒng)計(jì)分析功能開發(fā)團(tuán)隊(duì)正在開發(fā)中, 歡迎大家推薦方案~

國(guó)際化(i18n)

這個(gè)功能的實(shí)現(xiàn)比較簡(jiǎn)單, 不過 i18n 這個(gè)詞倒是可以多講一句, 原詞是 internationalization, 不過實(shí)在太長(zhǎng)了, 所以簡(jiǎn)寫為 i18n, 類似的還有 kubernetes -> k8s.

數(shù)據(jù)庫(kù) ORM

ORM 這個(gè)發(fā)展很也成熟了, 看清楚下面的進(jìn)化史就好了:

Statement: 直接執(zhí)行 sql 語句

QueryBuild: 使用鏈?zhǔn)秸{(diào)用, 來實(shí)現(xiàn)拼接 sql 語句

ActiveRecord: Model, 用來映射數(shù)據(jù)庫(kù)中的表, 實(shí)際還是封裝的 QueryBuild

當(dāng)然這一層層的封裝好處也很明顯, 減少 sql 的存在感.

// insert
$post = new Post();
$post->title = "daydaygo";
$post->save();

// query
$post = Post::find(1);

// update
$post->content = "coder at work";
$post->save();

// delete
$post->del();

要實(shí)現(xiàn)這樣的效果, 還是有一定的代碼量的, 也會(huì)遇到一些問題, 比如 代碼提示, 還有一些更高級(jí)的功能, 比如 關(guān)聯(lián)查詢

基本概念

并發(fā) vs 并行

抓住 并行 這個(gè)范圍更小的概念就容易理解了, 并行是要 同時(shí)執(zhí)行, 那么只能多 cpu 核心同時(shí)運(yùn)算才行; 并發(fā)則是因?yàn)?cpu運(yùn)行和切換速度快, 時(shí)間段內(nèi)執(zhí)行多個(gè)程序, 宏觀上 看起來 像在同時(shí)執(zhí)行

協(xié)程 vs 進(jìn)程

一種簡(jiǎn)單的說法 協(xié)程是用戶態(tài)的線程. 線程由操作系統(tǒng)進(jìn)行調(diào)度, 可以自動(dòng)調(diào)度到多 cpu 上執(zhí)行; 同一個(gè)時(shí)刻同一個(gè) cpu 核心上只有一個(gè)協(xié)程運(yùn)行, 當(dāng)遇到用戶代碼中的阻塞 IO 時(shí), 底層調(diào)度器會(huì)進(jìn)入事件循環(huán), 達(dá)到 協(xié)程由用戶調(diào)度 的效果

swoole2.0 原生

具體的實(shí)現(xiàn)原理大家到官網(wǎng)查看, 會(huì)有更詳細(xì)的 wiki 說明, 我這里從 工具 使用的角度來說明一下

限制條件一: 需要 swoole2.0 的協(xié)程 server + 協(xié)程 client 配合

限制條件二: 在協(xié)程 server 的 onRequet, onReceive, onConnect 事件回調(diào)中才能使用

$server = new SwooleHttpServer("127.0.0.1", 9501, SWOOLE_BASE);

// 1: 創(chuàng)建一個(gè)協(xié)程
$server->on("Request", function($request, $response) {
    $mysql = new SwooleCoroutineMySQL();
    // 協(xié)程 client 有阻塞 IO 操作, 觸發(fā)協(xié)程調(diào)度
    $res = $mysql->connect([
        "host" => "127.0.0.1",
        "user" => "root",
        "password" => "root",
        "database" => "test",
    ]);
    // 阻塞 IO 事件就緒, 協(xié)程恢復(fù)執(zhí)行
    if ($res == false) {
        $response->end("MySQL connect fail!");
        return;
    }
    // 出現(xiàn)阻塞 IO, 繼續(xù)協(xié)程調(diào)度
    $ret = $mysql->query("show tables", 2);
    $response->end("swoole response is ok, result=".var_export($ret, true));
});

$server->start();

注意: 觸發(fā)一次回調(diào)函數(shù), 就會(huì)在開始的時(shí)候生成一個(gè)協(xié)程, 結(jié)束的時(shí)候銷毀這個(gè)協(xié)程, 協(xié)程的生命周期, 伴隨此處回調(diào)函數(shù)執(zhí)行的生命周期

連接池

swoft 的連接池功能實(shí)現(xiàn), 主要在 src/Pool 下, 主要由三部分組成:

Connect: 連接, 值得一提的是, 為了后續(xù)使用方便, 這里同時(shí)配置了 同步連接 + 異步連接

Balancer: 負(fù)載均衡器, 目前提供 2 種策略, 隨機(jī)數(shù) + 輪詢

Pool: 連接池, 核心部分, 負(fù)責(zé)連接的管理和調(diào)度

PS: 自由切換同步/異步客戶端非常簡(jiǎn)單, 切換一下連接就好

直接上代碼:

// 使用 SqlQueue 來管理連接
public function getConnect()
{
    if ($this->queue == null) {
        $this->queue = new SplQueue(); // 又見 Spl
    }

    $connect = null;
    if ($this->currentCounter > $this->maxActive) {
        return null;
    }
    if (!$this->queue->isEmpty()) {
        $connect = $this->queue->shift(); // 有可用連接, 直接取
        return $connect;
    }

    $connect = $this->createConnect();
    if ($connect !== null) {
        $this->currentCounter++;
    }
    return $connect;
}

// 如果接入了服務(wù)治理, 將使用調(diào)度器
public function getConnectAddress()
{
    $serviceList = $this->getServiceList(); // 從 serviceProvider 那里獲取到服務(wù)列表
    return $this->balancer->select($serviceList);
}
服務(wù)治理熔斷、降級(jí)、負(fù)載、注冊(cè)與發(fā)現(xiàn)

swoft 的服務(wù)治理相關(guān)的功能, 主要在 src/Service 下:

Packer: 封包器, 和協(xié)議進(jìn)行對(duì)應(yīng), 看過 swoole 文檔的同學(xué), 就能知道協(xié)議的作用了

ServiceProvider: 服務(wù)提供者, 用來對(duì)接第三方服務(wù)管理方案, 目前已實(shí)現(xiàn) Consul

Service: RPC服務(wù)調(diào)用, 包含同步調(diào)用和協(xié)程調(diào)用(deferCall()), 目前添加 callback 實(shí)現(xiàn)簡(jiǎn)單的 降級(jí)

ServiceConnect: 連接池中 Connect 的 RPC Service 實(shí)現(xiàn), 不過個(gè)人認(rèn)為放到連接池中實(shí)現(xiàn)更好

Circuit: 熔斷, 在 src/Circuit 中實(shí)現(xiàn), 有三種狀態(tài), 關(guān)閉/開啟/半開

DispatcherService: 服務(wù)調(diào)度器, 在 Service 之前封裝一層, 添加 Middleware/Event 等功能

這里看看熔斷這部分的代碼, 半開狀態(tài)的邏輯復(fù)雜一些, 值得參考:

// SwoftCircuitCircuitBreaker
public function init()
{
    // 狀態(tài)初始化
    $this->circuitState = new CloseState($this);
    $this->halfOpenLock = new swoole_lock(SWOOLE_MUTEX); // 使用 swoole lock
}

// SwoftCircuitHalfOpenState
public function doCall($callback, $params = [], $fallback = null)
{
    // 加鎖
    $lock = $this->circuitBreaker->getHalfOpenLock();
    $lock->lock();
    ...
    // 釋放鎖
    $lock->unlock();
}
任務(wù)投遞 & Crontab 定時(shí)任務(wù)

swoft 任務(wù)投遞的實(shí)現(xiàn)機(jī)制當(dāng)然離不開 SwooleTimer::tick()(SwooleServer->task() 底層執(zhí)行機(jī)制是一樣的) , swoft 在實(shí)現(xiàn)的時(shí)候, 添加了 喜聞樂見 的 crontab 方式, 實(shí)現(xiàn)在 src/Crontab 下:

ParseCrontab: 解析 crontab

TableCrontab: 使用 SwooleTable 實(shí)現(xiàn), 用來存儲(chǔ) crontab 任務(wù)

Crontab: 連接 Task 和 TableCrontab

這里主要看一下 TableCrontab:

// 存儲(chǔ)原始的任務(wù)
private $originStruct = [
    "rule"       => [SwooleTable::TYPE_STRING, 100],
    "taskClass"  => [SwooleTable::TYPE_STRING, 255],
    "taskMethod" => [SwooleTable::TYPE_STRING, 255],
    "add_time"   => [SwooleTable::TYPE_STRING, 11]
];
// 存儲(chǔ)解析后的任務(wù)
private $runTimeStruct = [
    "taskClass"  => [SwooleTable::TYPE_STRING, 255],
    "taskMethod" => [SwooleTable::TYPE_STRING, 255],
    "minte"      => [SwooleTable::TYPE_STRING, 20],
    "sec"        => [SwooleTable::TYPE_STRING, 20],
    "runStatus"  => [SwooleTABLE::TYPE_INT, 4]
];
用戶自定義進(jìn)程

自定義進(jìn)程對(duì) SwooleProcess 的封裝, swoft 封裝之后, 想要使用用戶自定義進(jìn)程更簡(jiǎn)單了:

繼承 AbstractProcess 類, 并實(shí)現(xiàn) run() 來執(zhí)行業(yè)務(wù)邏輯.

swoft 中功能實(shí)現(xiàn)在 src/Process 下, 框架自帶三個(gè)自定義進(jìn)程:

Reload: 配合 ext-inotify 擴(kuò)展實(shí)現(xiàn)自動(dòng) reload, 下面會(huì)具體講解

CronTimer: crontab 里的 task 在這里觸發(fā) SwooleServer->tick()

CronExec: 實(shí)現(xiàn)協(xié)程 task, 實(shí)現(xiàn)中.

代碼就不貼了, 這里再擴(kuò)展一個(gè)比較適合使用自定義進(jìn)程的場(chǎng)景: 訂閱服務(wù)

Inotify 自動(dòng) Reload

服務(wù)器程序大都是常駐進(jìn)程, 有效減少對(duì)象的生成和銷毀, 提供性能, 但是這樣也給服務(wù)器程序的開發(fā)帶來了問題, 需要 reload 來查看生效后的程序. 使用 ext-inotify 擴(kuò)展可以解決這個(gè)問題.

直接上代碼, 看看 swoft 中的實(shí)現(xiàn):

// SwoftProcessReloadProcess
public function run(Process $process)
{
    $pname = $this->server->getPname();
    $processName = "$pname reload process";
    $process->name($processName);

    /* @var Inotify $inotify */
    $inotify = App::getBean("inotify"); // 自定義進(jìn)程來啟動(dòng) inotify
    $inotify->setServer($this->server);
    $inotify->run();
}

// SwoftBaseInotify
public function run()
{

    $inotify = inotify_init(); // 使用 inotify 擴(kuò)展

    // 設(shè)置為非阻塞
    stream_set_blocking($inotify, 0);

    $tempFiles = [];
    $iterator = new RecursiveDirectoryIterator($this->watchDir);
    $files = new RecursiveIteratorIterator($iterator);
    foreach ($files as $file) {
        $path = dirname($file);

        // 只監(jiān)聽目錄
        if (!isset($tempFiles[$path])) {
            $wd = inotify_add_watch($inotify, $path, IN_MODIFY | IN_CREATE | IN_IGNORED | IN_DELETE);
            $tempFiles[$path] = $wd;
            $this->watchFiles[$wd] = $path;
        }
    }

    // swoole Event add
    $this->addSwooleEvent($inotify);
}
private function addSwooleEvent($inotify)
{
    // swoole Event add
    swoole_event_add($inotify, function ($inotify) { // 使用 SwooleEvent
        // 讀取有事件變化的文件
        $events = inotify_read($inotify);
        if ($events) {
            $this->reloadFiles($inotify, $events); // 監(jiān)聽到文件變動(dòng)進(jìn)行更新
        }
    }, null, SWOOLE_EVENT_READ);
}
寫在最后

再補(bǔ)充一點(diǎn), 在實(shí)現(xiàn)服務(wù)管理(reload stop)時(shí), 使用的 posix_kill(pid, sig);, 并不是用 SwooleServer 中自帶的 reload() 方法, 因?yàn)槲覀儺?dāng)前環(huán)境的上下文并不一定在SwooleServer 中.

想要做好一個(gè)框架, 尤其是一個(gè)開源框架, 實(shí)際上要比我們平時(shí)寫 業(yè)務(wù)代碼 要難很多, 一方面是業(yè)務(wù)初期的 多快好省, 往往要上一些 能跑 的代碼. 這里引入一些關(guān)于代碼的觀點(diǎn):

代碼質(zhì)量: bug 率 + 性能

代碼規(guī)范: 形成規(guī)范可以提高代碼開發(fā)/使用的體驗(yàn)

代碼復(fù)用: 這是軟件工程的難題, 需要慢慢積累, 有些地方可以通過遵循規(guī)范走走捷徑

總結(jié)起來就一句話:

想要顯著提高編碼水平或者快速積累相關(guān)技術(shù)知識(shí), 參與開源可以算是一條捷徑.

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/28210.html

相關(guān)文章

  • swoft| 源碼解讀系列二: 啟動(dòng)階段, swoft 都干了些啥?

    摘要:源碼解讀系列二啟動(dòng)階段都干了些啥閱讀框架源碼了解啟動(dòng)階段的那些事兒小伙伴剛接觸的時(shí)候會(huì)感覺壓力有點(diǎn)大更直觀的說法是難開發(fā)組是不贊成難這個(gè)說法的的代碼都是實(shí)現(xiàn)的而又是世界上最好的語言的代碼閱讀起來是很輕松的之后開發(fā)組會(huì)用系列源碼解讀文章深 date: 2018-8-01 14:22:17title: swoft| 源碼解讀系列二: 啟動(dòng)階段, swoft 都干了些啥?descriptio...

    hqman 評(píng)論0 收藏0
  • swoft| 源碼解讀系列一: 好難! swoft demo 都跑不起來怎么破? docker 了解

    摘要:源碼解讀系列一好難都跑不起來怎么破了解一下唄閱讀框架源碼第一步搞定環(huán)境小伙伴剛接觸的時(shí)候會(huì)感覺壓力有點(diǎn)大更直觀的說法是難開發(fā)組是不贊成難這個(gè)說法的的代碼都是實(shí)現(xiàn)的而又是世界上最好的語言的代碼閱讀起來是很輕松的開發(fā)組會(huì)用源碼解讀系列博客深 date: 2018-8-01 14:22:17title: swoft| 源碼解讀系列一: 好難! swoft demo 都跑不起來怎么破? doc...

    shenhualong 評(píng)論0 收藏0
  • swoft| 源碼解讀系列一: 好難! swoft demo 都跑不起來怎么破? docker 了解

    摘要:源碼解讀系列一好難都跑不起來怎么破了解一下唄閱讀框架源碼第一步搞定環(huán)境小伙伴剛接觸的時(shí)候會(huì)感覺壓力有點(diǎn)大更直觀的說法是難開發(fā)組是不贊成難這個(gè)說法的的代碼都是實(shí)現(xiàn)的而又是世界上最好的語言的代碼閱讀起來是很輕松的開發(fā)組會(huì)用源碼解讀系列博客深 date: 2018-8-01 14:22:17title: swoft| 源碼解讀系列一: 好難! swoft demo 都跑不起來怎么破? doc...

    rollback 評(píng)論0 收藏0
  • Swoole 在 Swoft 中的應(yīng)用

    摘要:在中的應(yīng)用官網(wǎng)源碼解讀號(hào)外號(hào)外歡迎大家我們開發(fā)組定了一個(gè)就線下聚一次的小目標(biāo)上一篇源碼解讀反響還不錯(cuò)不少同學(xué)推薦再加一篇講解一下中使用到的功能幫助大家開啟的實(shí)戰(zhàn)之旅服務(wù)器開發(fā)涉及到的相關(guān)技術(shù)領(lǐng)域的知識(shí)非常多不日積月累打好基礎(chǔ)是很難真正 date: 2017-12-14 21:34:51title: swoole 在 swoft 中的應(yīng)用 swoft 官網(wǎng): https://www.sw...

    EscapedDog 評(píng)論0 收藏0
  • Swoft 源碼剖析 - 目錄

    摘要:作者鏈接來源簡(jiǎn)書著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對(duì)原文進(jìn)行了重新的排版。同時(shí)順手整理個(gè)人對(duì)源碼的相關(guān)理解,希望能夠稍微填補(bǔ)學(xué)習(xí)領(lǐng)域的空白。系列文章只會(huì)節(jié)選關(guān)鍵代碼輔以思路講解,請(qǐng)自行配合源碼閱讀。 作者:bromine鏈接:https://www.jianshu.com/p/2f6...來源:簡(jiǎn)書著作權(quán)歸作者所有,本文已獲得作者授權(quán)轉(zhuǎn)載,并對(duì)原文進(jìn)行了重新的排版。Swoft...

    qpwoeiru96 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

weij

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<