前面,咱们利用composer安装了workerman. 接下来咱们就开始用一个简单的demo来初步了解它的流程.php
// 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] // 从名字上来看是存的进程号.后面再来处理.