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]    // 从名字上来看是存的进程号.后面再来处理.
相关文章
相关标签/搜索