聊天室(上篇)GatewayWorker 基礎

前言

本文的目的是基於 GatewayWorker 官方手冊,梳理一次 GatewayWorker,並在實踐中與 MVC 框架整合的思路(附最終的項目源碼)。若是你已經理解了整合這一起的知識,那麼就能夠關掉這個網頁了。時間蠻寶貴的~php

這篇是上篇,梳理 GatewayWorker 基礎,下篇是 GatewayWorker 與 Laravel 整合聊天室。若是你具有了 GatewayWorker 基礎,請直接閱讀下篇html

 

好久之前就想作一個聊天室了。查了下 "php 通訊",找到了可用的東西:Socket、WebSocket、 Workerman 以及 GatewayWorker。Socket(接口)提供了一組端到端互相通訊的接口,做爲通訊的核心功能。Websocket(協議)定義了通訊中數據的封裝和顯示的格式,並且最大的特色是它支持服務端向客戶端的主動推送,這一點是 HTTP 作不到的。而 Workerman (框架)將這二者很好地整合在了一塊兒(固然不只僅於此)。GatewayWorker(框架)是在 Workerman 的基礎上開發的 TCP 長鏈接應用框架,提供了單發、羣發和廣播等接口,還能夠客戶端和客戶端通訊。數組

因此最終我選擇了 GatewayWorker 做爲 Socket 監聽的服務端,Laravel 做爲 HTTP 請求的業務處理框架,完成一個響應式的在線聊天室(項目地址在下一篇文章最後)。服務器

 

GatewayWorker工做原理

先理解一下工做原理,能夠對 GatewayWorker 有個總體的把握。這一起其實手冊裏已經詳細不囉嗦地解釋清楚了。我這裏再理一下:session

一、Register、Gateway、BusinessWorker 3 種進程依次啓動(由於支持多進程,因此我說「種」,而不是「個」)app

二、Gateway 進程和 BusinessWorker 進程啓動後向 Register 服務進程發起長鏈接註冊自身。框架

三、Register 服務進程收到 Gateway 的註冊後,把全部 Gateway 進程的通信地址寫入內存。socket

四、Register 服務進程收到 BusinessWorker 的註冊後,把內存中的 Gateway 進程通信地址發給全部 BusinessWorker 進程。分佈式

五、BusinessWorker 進程收到全部 Gateway 進程的通信地址後,嘗試鏈接 Gateway。函數

六、至此,全部 Gateway 和 BusinessWorker 進程就經過 Register 服務進程創建了長鏈接。

若是期間有新的 Gateway 註冊到 Register(通常是分佈式部署加機器),新 Gateway 的通信地址會被廣播給全部 BusinessWorker,BusinessWorker 收到通知後創建新鏈接。

若是期間有 Gateway 下線,Register 會收到通知、刪除這個 Gateway 的內部通信地址,並將新的內部通信地址列表廣播給全部 BusinessWorker,BusinessWorker 再也不鏈接下線的 Gateway。

七、客戶端的鏈接事件和鏈接上的數據會經由 Gateway 轉發給 BusinessWorker,BusinessWorker 默認調用 Events.php 中 Events 類的 onConnect、onMessage、onClose 事件回調處理業務邏輯。

八、BusinessWorker 負責運行全部的業務邏輯,實際的處理邏輯默認在 Events.php 中實現。

 

GatewayWorker進程模型

GatewayWorker 是以進程的形式進駐內存的,瞭解了它的工做原理以後,有必要理解一下它的進程模型。

GatewayWorker 主要有 3 種進程:Register 進程、Gateway 進程和 BusinessWorker 進程。這 3 種進程分別對應了內核源碼中的 Register 類、Gateway 類和 BusinessWorker 類,而且它們都是基於 Workerman 框架的 Worker 類開發的,因此這 3 種進程都有一些公共的屬性,好比 name、count、onWorkerStart、onWorkerStop 等等。能夠說,GatewayWorker 裏全部的進程都是 Worker 進程。

 

一、Register進程

Register 進程主要負責 Gateway 進程 與 BusinessWorker 進程創建鏈接並內部通信。

該進程由 Register 類實例化,並隨進程啓動進駐內存。

它可定製的只有實例化時指定自身所在的服務進程地址。包括 IP 和端口,而且目前只支持 text 協議。text 協議是 Workerman 框架定義的一種文本協議(協議格式爲:數據包 + 換行符)。

 

二、Gateway進程

Gateway 進程主要負責客戶端的鏈接以及鏈接上的數據,並將全部的請求轉發給 BusinessWorker 進程進行處理。BusinessWorker 進程的全部處理結果都經由 Gateway 進程轉發給客戶端。

該進程由 Gateway 類實例化,並隨進程啓動進駐內存。

它可定製的有:

(1)實例化。指定協議、IP 和端口。

協議:目前支持的有 Websocket 協議text 協議Frame 協議自定義通信協議和 裸 TCP 協議(不推薦,見通信協議做用),不支持監聽 HTTP 協議。

IP:"0.0.0.0" 表示監聽本機全部網卡;"127.0.0.1"表示僅容許本機經過 127.0.0.1 訪問該進程;內網 IP 如 "192.168.11.2" 表示只容許該 IP 訪問;外網 IP 如 "110.110.110.110" 表示只容許該 IP 訪問。

端口:大於 1024 小於等於 65535。小於 1024 時須要 root 權限運行該進程。

(2)name:Gateway 進程名。以便在 Bash 等終端裏查看區分。

(3)count:Gateway 進程數。充分利用多 CPU 資源。默認爲 1。如何設置進程數,請參考這裏

(4)lanIp:Gateway 進程所在服務器的內網 IP,默認填寫 "127.0.0.1" 便可。多服務器分佈式部署 時要填寫真實 IP。不管如何都不能填寫 "0.0.0.0"。

(5)startPort:Gateway 進程啓動後監聽的起始端口(本機端口),用來給 BusinessWorker 進程提供鏈接服務,而後二者經過這個端口創建通信。假設進程數 count 爲 4,起始端口 startPort 爲 2003,則 會啓動 4 個 Gateway進程,各進程分別監聽 200三、200四、200五、2006 端口。

(6)registerAddress:向 Register 進程的註冊地址,格式爲"IP + 端口",如 "127.0.0.1:1236"。和 BusinessWorker 進程指定的註冊地址要保持一致

(7)心跳設置:爲了防止長時間不通信被路由節點強行斷開或斷電斷網等極端事件,必須加心跳。相關屬性有 pingInterval、pingNotResponseLimit、pingInterval。詳細心跳設置請參考服務端到客戶端的心跳檢測

pingInterval:心跳間隔,單位秒,0 表示不發送心跳檢測。

pingNotResponseLimit:客戶端連續 $pingNotResponseLimit * $pingInterval 秒內不迴應心跳則斷開鏈接。

pingData:心跳數據,可任意,客戶端能識別就行。

(8)onWorkerStart:Gateway 進程啓動後的回調函數。

(9)onWorkerStop:Gateway 進程關閉的回調函數。

(10)onConnect:當有客戶端鏈接上來時觸發。與 Events::onConnect 的區別是 Events::onConnect 方法運行在 BusinessWorker 進程上。而 Gateway::onConnect 方法是運行在Gateway 進程上,沒法使用 \GatewayWorker\Lib\Gateway 類提供的接口。

(11)onClose:當有客戶端鏈接關閉時觸發。一樣與Events::onClose的區別是 Gateway::onClose 方法是運行在 Gateway 進程上,沒法使用 \GatewayWorker\Lib\Gateway 類提供的接口。

 

三、BusinessWorker進程

BusinessWorker 進程負責運行業務邏輯。BusinessWorker 進程收到 Gateway 進程轉發來的事件和請求時,會默認調用 Events.php 中的 onConnect、onMessage、onClose 方法處理事件和數據。

該進程由 BusinessWorker 類實例化,並隨進程啓動進駐內存。

它可定製的有:

(1)name:BusinessWorker 進程名。以便在 Bash 等終端裏查看區分。

(2)count:BusinessWorker 進程數。充分利用多 CPU 資源。默認爲 1。如何設置進程數,請參考這裏

(3)registerAddress:向 Register 進程的註冊地址,格式爲"IP + 端口",如 "127.0.0.1:1236"。和 Gateway 進程指定的註冊地址要保持一致

(4)onWorkerStart:BusinessWorker 進程啓動後的回調函數

(5)onWorkerStop:BusinessWorker 進程關閉的回調函數。

(6)eventHandler:指定 BusinessWorker 進程裏實際處理業務邏輯的類,默認是 Events。也就是默認使用 Events.php 中的 Events 類來處理業務。業務類至少要實現onMessage 靜態方法,onConnect 和 onClose 靜態方法能夠不用實現。(若是使用了命名空間,建議填寫徹底限定名稱的命名空間。)

 。

Events.php 

上面提到了 Events.php,它是實際處理業務邏輯的類 Events 所在的文件。咱們在實際的開發中,只須要關注這一個文件。

Events 裏有 5 個事件回調的處理方法,按照發生順序,依次是

  • onWorkerStart (Worker $businessWorker):當 BusinessWorker 進程啓動時觸發。每一個進程生命週期內只觸發一次。
  • onConnect (string $client_id):當客戶端鏈接上 Gateway 進程時觸發(TCP 三層握手)。

  • onMesssge (string $client_id, mixed $recv_data):當客戶端發來數據,也就是 Gateway 進程收到數據後觸發。
  • onClose (string client_id):當客戶端鏈接斷開時觸發。不管是客戶端仍是服務端主動斷開,都會觸發。

  • onWorkerStop (Worker $businessWorker):當 BusinessWorker 進程退出時觸發。每一個進程生命週期內只觸發一次。

這裏面咱們經常使用到的是 onMessage 和 onClose 回調,其餘比較少用。

上面的回調事件裏有一個比較重要的參數:$client_id。client_id 是 20 個字符的定長字符串,用來全局標識一個 Socket 鏈接。每一個客戶端鏈接都會被分配一個全局惟一的 client_id。客戶端關閉鏈接時,對應的 client_id 會失效。當客戶端再次打開一個 Socket 鏈接時,會被分配一個新的 client_id。

 

Lib\Gateway類提供的接口

既然(默認)在 Events.php 中處理實際的業務邏輯,回調的事件咱們已經知道了。那麼怎麼向客戶端發送消息呢?

命名空間 \GatewayWorker\Lib\Gateway 指向的這個 Gateway 類,提供了一組單發、羣發和廣播的接口,在 Events.php 中向客戶端發信的時候就可使用這個類。它提供的接口很是豐富:

Gateway::sendToAll($data);      // 向全部客戶端發送數據
Gateway::sendToClient($client_id, $data);  // 向某個客戶端發送數據
Gateway::closeClient($client_id);      // 關閉某個客戶端
Gateway::isOnline($client_id);  // 判斷某客戶端鏈接是否在線 
Gateway::bindUid($client_id, $uid);    // 綁定 uid 與 client_id 
Gateway::unbindUid($client_id, $uid);  // 取消 uid 與 某個 client_id 的綁定
Gateway::isUidOnline($uid);      // 某個 uid 是否在線
Gateway::GetClientIdByUid();     // 獲取與 uid 綁定的 client_id 列表(一對多)
Gateway::sendToUid($uid, $data); // 向全部 uid 發送
Gateway::joinGroup($client_id, $group);  // 把該 client_id 加入羣組
Gateway::leaveGroup($client_id, $group); // 將 client_id 離開羣組
Gateway::sendToGroup($group, $data);     // 向某羣組 group 發送
Gateway::getClientCountByGroup($group);  // 獲取某個組的在線鏈接數
Gateway::getClientSessionsByGroup($group); // 獲取某個組的鏈接信息
Gateway::getClientInfoByGroup($group);   // getClientSessionsByGroup 的別名
Gateway::getAllClientCount();     // 獲取全部的在線鏈接數
Gateway::getAllClientSessions();  // 獲取全部在線用戶的 session
Gateway::getAllClientInfo();      // getAllClientSessions 的別名
Gateway::setSession($client_id, $session);      // 設置 session,原 session 值會被覆蓋
Gateway::updateSession($client_id, $session);   // 更新 session,其實是與舊的session合併
Gateway::getSession($client_id);  // 獲取某個 client_id的 session

 這裏面比較重要的是 GatewayWorker 的超全局數組 $_SESSION。每一個客戶端鏈接對應一個 Session 會話,並由 Gateway 進程存儲在內存裏。示例以下,在收到客戶端消息時,打印全部在線鏈接的 Session:

use \GatewayWorker\Lib\Gateway;

class Events
{
    ...
    public onMessage($client_id, $message)
    {
        $_SESSION['name'] = $message['name']; // 操做當前用戶的 Session 時,直接使用 $_SESSION 便可
        var_export(Gateway::getAllClientSessions());
    }
    ...
}
打印出的數據相似以下:

array(
    '7f00000108fc00000008' => array('name' => 'Tom'),
    '7f00000108fc00000009' => array('name' => 'Joan')
)

注意上面的註釋,操做當前鏈接上的 Session 時,直接使用 $_SESSION['xx'] = 'xxx'; 的方式賦值便可,操做其餘用戶的 Session 時用  Gateway::setSession 接口。

此外,若是你在 GatewayWorker 的進程模型裏須要獲取客戶端、服務端的 IP,請使用 $_SERVER 數組。它由 Workerman 框架定義,內置了 5 個數組成員,數組 key 分別以下,詳細請參考文檔

REMOTE_ADDR   // 客戶端IP(若是客戶端處於局域網,則是客戶端所在局域網的出口IP)
REMOTE_PORT   // 客戶端端口(若是客戶端處於局域網,則是客戶端所在局域網的出口端口)
GATEWAY_ADDR  // Gateway 進程所在服務器的 IP
GATEWAY_PORT  // Geteway 監聽的端口,這對於多端口應用中在 Events.php 裏區分客戶端連的是哪一個端口很是有用。
GATEWAY_CLIENT_ID  // 全局惟一的客戶端 IP

好的。有關 GatewayWorker 框架的基礎暫時就梳理這麼多。更多 GatewayWorker 開發和部署的細節或問題,好比心跳檢測、設置定時器、合理選擇多進程、分佈式部署、定製通信協議、啓用 wss 協議等等,都在文檔裏有詳細的介紹。車在下面。

我感受這一篇有點長了,因此將在下一篇開始梳理 GatewayWorker 與 Laravel 框架的整合。

 

相關連接

GatewayWorker 在線文檔:http://doc2.workerman.net/326102

Workerman 在線文檔:http://doc.workerman.net/

Workerman 官網:https://www.workerman.net/

相關文章
相關標籤/搜索