聊天室(上篇)GatewayWorker 基礎

来源:https://www.cnblogs.com/mingc/archive/2018/01/24/8328582.html
-Advertisement-
Play Games

前言 本文的目的是基於 GatewayWorker 官方手冊,梳理一次 GatewayWorker,併在實踐中與 MVC 框架整合的思路(附最終的項目源碼)。如果你已經理解了整合這一塊兒的知識,那麼就可以關掉這個網頁了。時間蠻寶貴的~ 很久以前就想做一個聊天室了。百度了下 "php 通信",看到了可 ...


前言

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

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

 

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

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

 

GatewayWorker工作原理

先理解一下工作原理,可以對 GatewayWorker 有個整體的把握。這一塊兒其實手冊里已經詳細不啰嗦地解釋清楚了。我這裡再理一下:

1、Register、Gateway、BusinessWorker 3 種進程依次啟動(因為支持多進程,所以我說“種”,而不是“個”)

2、Gateway 進程和 BusinessWorker 進程啟動後向 Register 服務進程發起長連接註冊自身。

3、Register 服務進程收到 Gateway 的註冊後,把所有 Gateway 進程的通訊地址寫入記憶體。

4、Register 服務進程收到 BusinessWorker 的註冊後,把記憶體中的 Gateway 進程通訊地址發給所有 BusinessWorker 進程。

5、BusinessWorker 進程收到所有 Gateway 進程的通訊地址後,嘗試連接 Gateway。

6、至此,所有 Gateway 和 BusinessWorker 進程就通過 Register 服務進程建立了長連接。

如果期間有新的 Gateway 註冊到 Register(一般是分散式部署加機器),新 Gateway 的通訊地址會被廣播給所有 BusinessWorker,BusinessWorker 收到通知後建立新連接。

如果期間有 Gateway 下線,Register 會收到通知、刪除這個 Gateway 的內部通訊地址,並將新的內部通訊地址列表廣播給所有 BusinessWorker,BusinessWorker 不再連接下線的 Gateway。

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

8、BusinessWorker 負責運行所有的業務邏輯,實際的處理邏輯預設在 Events.php 中實現。

 

GatewayWorker進程模型

GatewayWorker 是以進程的形式進駐記憶體的,瞭解了它的工作原理之後,有必要理解一下它的進程模型。

GatewayWorker 主要有 3 種進程:Register 進程、Gateway 進程和 BusinessWorker 進程。這 3 種進程分別對應了內核源碼中的 Register 類、Gateway 類和 BusinessWorker 類,並且它們都是基於 Workerman 框架的 Worker 類開發的,所以這 3 種進程都有一些公共的屬性,比如 name、count、onWorkerStart、onWorkerStop 等等。可以說,GatewayWorker 里所有的進程都是 Worker 進程。

 

1、Register進程

Register 進程主要負責 Gateway 進程 與 BusinessWorker 進程建立連接並內部通訊。

該進程由 Register 類實例化,並隨進程啟動進駐記憶體。

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

 

2、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進程,各進程分別監聽 2003、2004、2005、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 類提供的介面。

 

3、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/

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • HTTP緩存是個大公司面試幾乎必考的問題,寫篇隨筆說一下HTTP緩存。 1. HTTP報文首部中有關緩存的欄位 在HTTP報文中,與緩存相關的信息都存在首部里,簡單說一下首部。 首部 HTTP首部欄位向請求報文和相應報文中添加了一些附加信息。本質上來說,它們只是一些鍵值對的列表。比如,下麵的首部行會 ...
  • 基本使用 html: JavaScript: 通過註入路由,我們可以用 this.$router 來訪問它,就像在任何組件里用 this.$router 訪問當前路有一樣。 路由的命名 要鏈接到一個命名路由,可以給 router-link 的 to 屬性傳一個對象: 這跟代碼調用 router.pu ...
  • 對於前端分頁,不懂的人也許會覺得是個笑話,分頁不都是後端的事情嗎?的確不得不承認,處理邏輯性的話,還是後端更加友好,但前端分頁,也有他友好的一面,舉個慄子:伺服器壓力請求減少,換頁時用戶體驗好。直戳主題——前端實現分頁的原理及其步驟 ...
  • 虛擬化和容器化是項目雲化不可避免的兩個問題。虛擬化由於是純平臺操作,一個運行於linux操作系統的項目幾乎不需要做任何改造就可以支持虛擬化。而項目如果要支持容器化則需要做許多細緻的改造工作。容器化相對於虛擬化的優勢也相當明顯,運行於裸機性能高,秒級啟停容器,更不用說開發、測試、佈署一致的環境(Dev... ...
  • 接著上一篇的內容,我們繼續來梳理分散式系統之中的副本機制與副本一致。上文我們聊到了在可用性與一致性之間的一個折中的一致性等級: 最終一致性 。我們順著上篇的內容,由用戶來分析一致性等級。 1. 客戶端的困擾 上篇文章我們提到了數據系統常用的模型,當提交新數據時,必須將它發送給Leader節點,但是當 ...
  • 本篇將介紹python中sys, getopt模塊處理命令行參數 本篇將介紹python中sys, getopt模塊處理命令行參數 如果想對python腳本傳參數,python中對應的argc, argv(c語言的命令行參數)是什麼呢?需要模塊:sys參數個數:len(sys.argv)腳本名: s ...
  • 如果你在使用 Pandas(Python Data Analysis Library) 的話,下麵介紹的對你一定會有幫助的。 如果你在使用 Pandas(Python Data Analysis Library) 的話,下麵介紹的對你一定會有幫助的。 如果你在使用 Pandas(Python Dat ...
  • 思路 上一篇大概梳理了一下 GatewayWorker 的基礎知識。這篇就來準備整合 GatewayWorker 到 Laravel。 GatewayWorker 是基於 Socket 監聽的伺服器框架,而 Laravel 是基於 HTTP 請求/響應模型的 Web 框架。所以一定要明白,兩者的部署 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...