1. 程式人生 > >聊天室(上篇)GatewayWorker 基礎

聊天室(上篇)GatewayWorker 基礎

前言

本文的目的是基於 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 和埠。

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 基礎

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

Lumion 4.5 & 5.0 基礎入門提升教程

作者:活力網 課程講師:Jason 課程時長:6小時3分鐘 課程類別:Lumion基礎教程 學員要求:中高配置電腦一臺,SketchUp入門水平以上 教學重點:Lumion入門,Lumion中的各種命令、特效 教學難點:將所學知識綜合使用 教學目標:通過本課程的學習,學員能夠掌握L

Asp.net過濾器理論基礎

Filter 微軟為Asp.net MVC開發的4種過濾器: 許可權校驗過濾器 Action過濾器(IActionFilter),action方法執行前和執行後會執行的過濾器,需要實現介面 IActionFilter Result(IResultFilt

【MyBatis源碼分析】insert方法、update方法、delete方法處理流程

times database connect 環境 enable clas 它的 java對象 ace 打開一個會話Session 前文分析了MyBatis將配置文件轉換為Java對象的流程,本文開始分析一下insert方法、update方法、delete方法處理的流程,至

編程經常使用設計模式具體解釋--工廠、單例、建造者、原型

-a 裝飾器模式 nds support art 類的繼承 兩個 開放 lose 參考來自:http://zz563143188.iteye.com/blog/1847029 一、設計模式的分類 整體來說設計模式分為三大類: 創建型模式。共五種:工廠方法模式、抽

2017最新PHP經典面試題目匯總

4.0 .net true 服務 一次 模板 混合 符號 組織 原文鏈接:http://www.cnblogs.com/zhyunfe/p/6209097.html 1、雙引號和單引號的區別 雙引號解釋變量,單引號不解釋變量 雙引號裏插入單引號,其中單引號裏如果有變量

Vue.js——組件快速入門

綁定 ram 字符串過濾 技術 dem ava 對象 src get Vue.js——60分鐘組件快速入門(上篇) 組件簡介 組件系統是Vue.js其中一個重要的概念,它提供了一種抽象,讓我們可以使用獨立可復用的小組件來構建大型應用,任意類型的應用界面都可以抽象為一個組件

iOS多線程開發之離不開的GCD

sop 先進先出 調度 事件 實現 說明 優先級 子線程 函數 一、GCD基本概念 GCD 全稱Grand Central Dispatch(大中樞隊列調度),是一套低層API,提供了?種新的方法來進?並發程序編寫。從基本功能上講,GCD有點像NSOperatio

支付網關 | 京東618、雙11用戶支付的核心承載系統

java 支付 雙11 支付網關 618 二零一七年六月二十一日,就是年中大促剛結束的那一天,我午飯時間獨在辦公室裏徘徊,遇見X君,前來問我道,“可曾為這次大促寫了一點什麽沒有?”我說“沒有”。他就正告我,“還是寫一點罷;小夥伴們很想了解支撐起這麽大的用戶支付流量所采用的技術。”「摘要

.net ef core 領域設計代碼轉換

解決 con mage keys $1 服務 結構 刪除 sql 一、前言 .net core 2.0正式版已經發布幾個月了,經過研究,決定把項目轉移過來,新手的話可以先看一些官方介紹 傳送門:https://docs.microsoft.com/zh-cn/do

CLR via C# 讀書筆記-27.計算限制的異步操作

top oid 輔助線 var 思考 read 運行 簡單例子 class 前言 學習這件事情是一個習慣,不能停。。。另外這篇已經看過兩個月過去,但覺得有些事情不總結跟沒做沒啥區別,遂記下此文 1.CLR線程池基礎 2.ThreadPool的簡單使用練習 3.執行上下文 4

【SqlServer系列】淺談SQL Server事務與鎖

架構 tab 要求 允許 ble 1.2 定義 由於 數據庫引擎 一 概述 在數據庫方面,對於非DBA的程序員來說,事務與鎖是一大難點,針對該難點,本篇文章試圖采用圖文的方式來與大家一起探討。 “淺談SQL Server 事務與鎖”這個專題共分

【ASP.NET Core】處理異常

關心 指向 然而 sub 相關 pri roo epon netcore 依照老周的良好作風,開始之前先說點題外話。 前面的博文中,老周介紹過自定義 MVC 視圖的搜索路徑,即向 ViewLocationFormats 列表添加相應的內容,其實,對 Razor Page

[ Python ] 基本數據類型及屬性

獲取 string ast 轉換 分割字符串 upper not found 不可 inf 1. 基本數據類型 (1) 數字 - int (2) 字符串 - str (3) 布爾值 - bool 2. int 類型中重要的方法

這一次,真正搞懂信用評分模型

工程師 集中 重要 sklearn app 目的 概率 單變量 是我 python風控評分卡建模和風控常識 https://study.163.com/course/introduction.htm?courseId=1005214003&utm_campaign

從誌願軍“斷刀”再論敏捷之道

慢慢 失敗 多個 之一 朝鮮 無法 一次 mark 學習 從誌願軍“斷刀”再論敏捷之道(上篇) 作者:歐德張(原創) ??在現在的IT項目中,以往常用的是瀑布模型套路,這些年敏捷模式大受歡迎,關於敏捷,現在諸人開口PMI-ACP,閉口則SCRUM,又有諸多實踐、案例遵行其

Win10深度學習環境配置:python3 + curl + pip + Jupyter notebook

好記性不如爛筆頭,純粹為自己的學習生活記錄點什麼! 本次記錄win10下安裝python3+curl+pip+jupyter,以及修改右鍵快捷開啟cmd 對於大多數的學習者,還是習慣選擇在ubuntu系統上學習深度學習,主要還是因為絕大多數演算法實現都是ubunt

資料結構------------線性表

線性表:由n(n>=0)個數據特性相同的元素構成的有限序列 線性表中的袁旭個數n(n>=0)定義為線性表的長度,n=0時為空表 非空的線性表或線性結構特點: 1)存在唯一的一個數被稱為“第一個”的資料元素; 2)存在唯一的一個數被稱為“最後一個”的資料元素; 3)除第

Java中的Object類

   要麼讀書,要麼旅行,身體和心靈總有一個要在路上。——羅馬假日   咱今天學習的是Java的Object類,首先先看程式碼,類裡面有哪些方法。 咱今天學習兩個方法,分別是hashCode,equals。  Obje

Android原生下載基本邏輯+斷點續傳

零、前言 1.今天帶來的是Android原生下載的上篇,主要核心是斷點續傳,多執行緒下載將會在下篇介紹 2.本例使用了Activity,Service,BroadcastReceiver三個元件 3.本例使用了兩個執行緒:LinkURLThread做一些初始工作,DownLoadThread進行核心下