「走在Swoole學習的道路上」一次解耦歷程

clipboard.png

概述

看標題也不知道做者想要說些什麼,最近在看Swoole方面的內容,在封裝框架時遇到了一個關於解耦的問題,解耦你們並不陌生,此次的解耦是關於監聽事件和心跳檢測的一個demo,直接來看下問題吧。php

解決思路

在Swoole啓動時我想加入一些事件監聽,好比RPC中的註冊中心,我啓動、提供了哪些服務相似的場景等等,可是這只是一種事件監聽,若是要多個呢?好比當我接收到請求以後、收到消息以後、鏈接關閉以後等等,你須要建立不少的時間監聽,這個時候你可能在啓動時候把全部事件都要註冊進去,接下來看下代碼是如何簡單粗暴的實現的。web

<?php

namespace LoyaltyLu\Core;

use LoyaltyLu\Core\Event\Event;
use  Swoole\Http\Server;

class Http
{
    ...
    
    public function start()
    {
        $reload = Reload::get_instance();
        $reload->watch = [CONFIG_PATH, FRAME_PATH, APP_PATH];
        $reload->md5Flag = $reload->getMd5();
        #主動收集已有事件收集Listener目錄
        $this->collecEvent();
        //定時器
        swoole_timer_tick(3000, function () use ($reload) {
            if ($reload->reload()) {
                $this->server->reload(); //重啓
            }
        });
        Event::trigger('start', ['sss']);
    }
    
    /**
     * 收集事件
     */
    public function collecEvent()
    {
        $files = glob(EVENT_PATH . "/*.php");
        if (!empty($files)) {
            foreach ($files as $dir => $fileName) {
                include "{$fileName}";
                $fileName = explode('/', $fileName);
                $className = explode('.', end($fileName))[0];
                $nameSpace = 'App\\Listener\\' . $className;#可放入配置文件

                if (class_exists($nameSpace)) {
                    $obj = new $nameSpace;
                    #獲得本身定義的事件名稱,利用反射讀取類文檔註釋
                    $re = new \ReflectionClass($obj);
                    if (strlen($re->getDocComment()) < 2) {
                        throw new \Exception('沒有按照規範定義事件名稱');
                    } else {
                        preg_match("/@Listener\((.*)\)/i", $re->getDocComment(), $eventName);
                        if (empty($eventName)) {
                            throw new \Exception('沒有按照規範定義事件名稱');
                        }
                        #註冊的事件
                        Event::register($eventName[1], [$obj, 'handle']);
                    }
                }
            }
        }
    }
...
}
  • 當啓動Swoole時調用collecEvent方法
  • collecEvent負責收集,註冊指定目錄、命名空間下的方法
  • EVENT_PATH = APP_PATH.'/listener'
  • 循環遍歷目錄下全部文件
  • 獲取文件名稱
  • 拼接命名空間(命名空間能夠放置到Config等配置文件當中)
  • class_exists檢查類是否存在
  • ReflectionClass 這裏用到了反射、註解的形式判斷是否符合
  • getDocComment()獲取註釋內容
  • 正則/@Listener\((.*)\)/i匹配出須要監聽的所屬事件
  • 執行Event::register($event,$callback)進行事件註冊

接下來看下Event類:json

class Event
{
    public static $events = [];

    //事件註冊

    /**
     * @param $event 事件名
     * @param $callback 事件回調
     */
    public static function register($event, $callback)
    {
        $event = strtolower($event);//不區分大小寫
        if (!isset(self::$events[$event])) {
            self::$events[$event] = [];
        }
        self::$events[$event] = ['callback' => $callback];
    }

    //事件觸發
    public static function trigger($event, $params = [])
    {
        $event = strtolower($event);//不區分大小寫
        if (isset(self::$events[$event])) {
            call_user_func(self::$events[$event]['callback'], $params);
            return true;
        }
        return false;
    }
}
  • Event當中包含2個靜態方法:事件註冊、事件觸發
  • 事件註冊執行的是把說有的事件放入$event屬性當中
  • 事件觸發:驗證事件是否存在、call_user_func()執行回調函數

再看下如何聲明一個事件的文件:swoole

<?php


namespace App\Listener;


/**
 * Class StartListener
 * @package App\Listener
 * @Listener(start)
 */
class StartListener
{
    public function handle($params)
    {
        go(function () {//建立攜程環境
            //升級的websockt
            $cli = new \Swoole\Coroutine\Http\Client('127.0.0.1', 9600);
            $ret = $cli->upgrade('/');
            if ($ret) {
//                Config::get()
                $data = [
                    'method'      => 'register',
                    'serviceName' => "Server",
                    'ip'          => '0.0.0.0',
                    'port'        => 9800,
                ];
                $cli->push(json_encode($data));
                //心跳處理
                swoole_timer_tick(3000, function () use ($cli) {
                    if ($cli->errCode == 0) {
                        $cli->push('11', WEBSOCKET_OPCODE_PING);
                    }
                });
            }
        });
    }
}
  • 應該這裏繼承一個接口類,必須實現handle()方法,你們能夠本身實現下
  • handle() 方法這裏實現了一個相似通知註冊中心註冊服務的功能
  • swoole_timer_tick()利用毫秒級定時器處理心跳
  • 文檔註釋,提供給反射抓取
/**
 * Class StartListener
 * @package App\Listener
 * @Listener(start)
 */

調用

public function start()
    {
        ...
        #主動收集已有事件收集Listener目錄
        $this->collecEvent();
        ...
        Event::trigger('start', ['sss']);
    }

謝謝觀賞

謝謝你們耐心觀看,但願對您有所幫助,也但願你們提供下不一樣的意見,找到更有效的方式來完成,共同窗習,謝謝!框架

相關文章
相關標籤/搜索