前面,咱們利用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] // 從名字上來看是存的進程號.後面再來處理.