workerman源碼-workerman初始化流程

前面,咱們利用composer安裝了workerman. 接下來咱們就開始用一個簡單的demo來初步瞭解它的流程.php

簡單demo

// http.php
require __DIR__ . '/vendor/autoload.php';

use Workerman\Worker;

// 建立一個Worker監聽2345端口,使用http協議通信
$http_worker = new Worker("http://0.0.0.0:2345");

// 啓動4個進程對外提供服務
$http_worker->count = 4;

// 接收到瀏覽器發送的數據時回覆hello world給瀏覽器
$http_worker->onMessage = function($connection, $data) {
    // 向瀏覽器發送hello world
    $connection->send('hello world');
};

// 運行worker
Worker::runAll();

從官網抄的.先直接拿來用.出處.可使用以下命令來運行linux

php http.php start
// or win
php http.php

構造函數

上面的示例代碼中,建立了一個worker類,並設置監聽2345端口. 而後設置了回調信息.就執行運行了.若是不算引入,全部的一切都是從new開始.咱們先來看看new的時候幹了什麼.先貼上源碼.一步步來分析.windows

public function __construct($socket_name = '', $context_option = array())
{
    // Save all worker instances.
    $this->workerId                    = \spl_object_hash($this);
    static::$_workers[$this->workerId] = $this;
    static::$_pidMap[$this->workerId]  = array();

    // Get autoload root path.
    $backtrace                = \debug_backtrace();
    $this->_autoloadRootPath = \dirname($backtrace[0]['file']);

    if (static::$_OS === OS_TYPE_LINUX && version_compare(PHP_VERSION,'7.0.0', 'ge')) {
        $php_uname = strtolower(php_uname('s'));
        // If not Mac OS then turn reusePort on.
        if ($php_uname !== 'darwin') {
            $this->reusePort = true;
        }
    }

    // Context for socket.
    if ($socket_name) {
        $this->_socketName = $socket_name;
        $this->parseSocketAddress();
        if (!isset($context_option['socket']['backlog'])) {
            $context_option['socket']['backlog'] = static::DEFAULT_BACKLOG;
        }
        $this->_context = \stream_context_create($context_option);
    }
}

首先.從進入構造函數第一步就是給本身聲明的了ID,由對象而來的ID.spl_object_hash能夠直接爲對象生成一個惟一的ID.你能夠理解爲uuid的概念.而後下一步,準備開始懵圈吧.瀏覽器

static::$_workers[$this->workerId] = $this;
static::$_pidMap[$this->workerId]  = array();

將本身放入到Worker::$_workers的靜態變量中,而後將本身的pidMap也初始話. 至於爲什麼要這麼放.後面來講.而後調用了debug_backtrace來獲取命令運行的根目錄,緊接着接下來就判斷本身是不是linux來開啓端口複用.服務器

if (static::$_OS === OS_TYPE_LINUX && version_compare(PHP_VERSION,'7.0.0', 'ge')) {
    $php_uname = strtolower(php_uname('s'));
    if ($php_uname !== 'darwin') {
        $this->reusePort = true;    // 開始端口複用
    }
}

這個端口複用在linux和windows上有本地的區別.先過.而後就開始解析咱們的協議.協議解析事後.就設置了一個stream_context_create的上下文對象. 至此初始化完畢. 下面咱們來看協議解析app

協議解析

// parseSocketAddress
protected function parseSocketAddress() {
    if (!$this->_socketName) {
        return;
    }
    // Get the application layer communication protocol and listening address.
    list($scheme, $address) = \explode(':', $this->_socketName, 2);
    // Check application layer protocol class.
    if (!isset(static::$_builtinTransports[$scheme])) {
        $scheme         = \ucfirst($scheme);
        $this->protocol = \substr($scheme,0,1)==='\\' ? $scheme : '\\Protocols\\' . $scheme;
        if (!\class_exists($this->protocol)) {
            $this->protocol = "\\Workerman\\Protocols\\$scheme";
            if (!\class_exists($this->protocol)) {
                throw new Exception("class \\Protocols\\$scheme not exist");
            }
        }

        if (!isset(static::$_builtinTransports[$this->transport])) {
            throw new \Exception('Bad worker->transport ' . \var_export($this->transport, true));
        }
    } else {
        $this->transport = $scheme;
    }

    $local_socket = static::$_builtinTransports[$this->transport] . ":" . $address;
    return $local_socket;
}

在這以前,咱們要了解咱們的協議.這裏若是單開,就又要開一篇文章了.因此咱們只瞭解格式便可.一個套接字.有三部分構成. 第一就是協議類型,http,tcp...等等.第二個就是監聽地址.0.0.0.0就是監聽本地地址.第三個就是咱們的端口.這裏咱們的端口就是2345.composer

// http協議
// 0.0.0.0 監聽地址
// 2345 監聽端口.
http://0.0.0.0:2345

這裏咱們就監聽本地的2345端口用來作一個http協議的服務器. 下面咱們來看解析協議這個函數.第一步根據咱們提供的socketName來獲得對應的協議類型(http)和地址.ssh

list($scheme, $address) = \explode(':', $this->_socketName, 2);

而後就去Worker::$_builtinTransports中去查找協議類型.這裏麪包含了咱們最基礎的協議.tcp,udp,unix,和ssl.若是是最基礎的就直接保存到$this->transport中.若是不是就稍微麻煩點.socket

$scheme         = \ucfirst($scheme);    // 將http 轉爲Http
$this->protocol = \substr($scheme,0,1)==='\\' ? $scheme : '\\Protocols\\' . $scheme;    // 若是第一個是\則會直接存給protocol.若是不是,就是\Protocols\$scheme,顯然咱們是Protocols\Http.
if (!\class_exists($this->protocol)) {  // 若是不存在類, 就去查看自Workerman本身自己己定義的協議.
    $this->protocol = "\\Workerman\\Protocols\\$scheme";
    if (!\class_exists($this->protocol)) {  // 若是都找不到就報錯.
        throw new Exception("class \\Protocols\\$scheme not exist");
    }
}

// 若是不存在,就報錯.$this->transport默認爲tcp
if (!isset(static::$_builtinTransports[$this->transport])) {
    throw new \Exception('Bad worker->transport ' . \var_export($this->transport, true));
}

從上面代碼能夠看出,先查找php自己支持或者本身寫的協議,若是都沒有就找workerman自己的協議.若是是本身定義的協議.都是基於tcp協議來建立本身的協議.這裏有幾個點,第一.Protocols這個命名空間是什麼?這個是php本身定義的協議,包含以下tcp

file:// — 訪問本地文件系統
http:// — 訪問 HTTP(s) 網址
ftp:// — 訪問 FTP(s) URLs
php:// — 訪問各個輸入/輸出流(I/O streams)
zlib:// — 壓縮流
data:// — 數據(RFC 2397)
glob:// — 查找匹配的文件路徑模式
phar:// — PHP 歸檔
ssh2:// — Secure Shell 2
rar:// — RAR
ogg:// — 音頻流
expect:// — 處理交互式的流

固然,以上協議php都未實現對應的Protocols.因此都會去查找WorkermanProtocols下的解析.至此初始化就完全完成了.

未處理的點

以上,初始化的疑問. 在後續的過程當中慢慢來了解.

$this->reusePort = true;    // 端口複用,只要系統不是mac和windows.這個值都爲true.
Worker::$_workers           // 這裏保存了全部的worker
Worker::$_pidMap[$this->workerId]    // 從名字上來看是存的進程號.後面再來處理.
相關文章
相關標籤/搜索