workerman做實時訊息推送,用過沒?

Laravel00發表於2021-12-24

文章來自微信公眾號:PHP自學中心


學習實時訊息推送之前,先了解以下的這些概念,這些也是你必須要懂的。

TCP/IP
TCP/IP是個協議組,可分為三個層次:網路層、傳輸層和應用層。
在網路層有IP協議、ICMP協議、ARP協議、RARP協議和BOOTP協議。
在傳輸層中有TCP協議與UDP協議。

在應用層有:
TCP包括FTPHTTPTELNETSMTP等協議
UDP包括DNSTFTP等協議

短連線
連線->傳輸資料->關閉連線
HTTP是無狀態的,瀏覽器和伺服器每進行一次HTTP操作,就建立一次連線,但任務結束就中斷連線。
也可以這樣說:短連線是指SOCKET連線後傳送後接收完資料後馬上斷開連線。

長連線
連線->傳輸資料->保持連線 -> 傳輸資料-> 。。。->關閉連線。
長連線指建立SOCKET連線後不管是否使用都保持連線,但安全性較差。

http的長連線
HTTP也可以建立長連線的,使用Connection:keep-alive,HTTP 1.1預設進行持久連線。HTTP1.1HTTP1.0相比較而言,最大的區別就是增加了持久連線支援(貌似最新的http1.0 可以顯示的指定keep-alive),但還是無狀態的,或者說是不可以信任的。

什麼時候用長連線,短連線?
長連線多用於操作頻繁,點對點的通訊,而且連線數不能太多情況,。每個TCP連線都需要三步握手,這需要時間,如果每個操作都是先連線,再操作的話那麼處理速度會降低很多,所以每個操作完後都不斷開,次處理時直接傳送資料包就OK了,不用建立TCP連線。例如:資料庫的連線用長連線, 如果用短連線頻繁的通訊會造成socket錯誤,而且頻繁的socket 建立也是對資源的浪費。

而像WEB網站的http服務一般都用短連結,因為長連線對於服務端來說會耗費一定的資源,而像WEB網站這麼頻繁的成千上萬甚至上億客戶端的連線用短連線會更省一些資源,如果用長連線,而且同時有成千上萬的使用者,如果每個使用者都佔用一個連線的話,那可想而知吧。所以併發量大,但每個使用者無需頻繁操作情況下需用短連好。

workerman是啥?
Workerman是一款純PHP開發的開源高效能的PHP socket 伺服器框架。被廣泛的用於手機app、移動通訊,微信小程式,手遊服務端、網路遊戲、PHP聊天室、硬體通訊、智慧家居、車聯網、物聯網等領域的開發。支援TCP長連線,支援WebsocketHTTP等協議,支援自定義協議。擁有非同步Mysql、非同步Redis、非同步Http、非同步訊息佇列等眾多高效能元件。


開始步入正題:為了達到實時通訊,很多時候我們採用了ajax輪詢機制,後面可以採用workerman方式來實現.本文與ThinkPHPWorkerman為例):
1、ThinkPHPWorkerman是兩個獨立的系統,獨立部署(可部署在不同伺服器),互不干擾。
2、ThinkPHPHTTP協議提供網頁頁面在瀏覽器渲染展示。
3、ThinkPHP提供的頁面的js發起websocket連線,連線workerman
4、連線後給Workerman傳送一個資料包(包含使用者名稱密碼或者某種token串)用於驗證websocket連線屬於哪個使用者。
5、僅在ThinkPHP需要向瀏覽器推送資料時,才呼叫workermansocket介面推送資料。
6、其餘請求還是按照原本ThinkPHPHTTP方式呼叫處理。

總結:
Workerman作為一個可以向瀏覽器推送的通道,僅僅在需要向瀏覽器推送資料時才呼叫Workerman介面完成推送。業務邏輯全部在ThinkPHP中完成。
ok,到這裡,把workerman容器跑起來,注意這裡是CLI模式執行

圖片
然後再我們專案接收資訊中這麼寫,附上程式碼

<script>

    // 連線服務端
    var socket = io('http://127.0.0.1:2120');

    // uid可以是自己網站的使用者id,以便針對uid推送
    uid = 123;

    // socket連線後以uid登入
    socket.on('connect', function(){
        socket.emit('login', uid);
    });

    // 後端推送來訊息時
    socket.on('new_msg', function(msg){
        console.log("收到訊息:"+msg);  //自己業務邏輯處理
    });

</script>

接著,我們在使用者向使用者傳送資訊的時候新增

// 指明給誰推送,為空表示向所有線上使用者推送
$to_uid = "123";

// 推送的url地址
$push_api_url = "http://127.0.0.1:2121/";

$post_data = array(
   "type" => "publish",
   "content" => "資料",
   "to" => $to_uid, 
);

$ch = curl_init ();
curl_setopt ( $ch, CURLOPT_URL, $push_api_url );
curl_setopt ( $ch, CURLOPT_POST, 1 );
curl_setopt ( $ch, CURLOPT_HEADER, 0 );
curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, 1 );
curl_setopt ( $ch, CURLOPT_POSTFIELDS, $post_data );
curl_setopt ($ch, CURLOPT_HTTPHEADER, array("Expect:"));
$return = curl_exec ( $ch );
curl_close ( $ch );
var_export($return);

其中,workerman裡面的推送核心程式碼實現

// 全域性陣列儲存uid線上資料
$uidConnectionMap = array();

// 記錄最後一次廣播的線上使用者數
$last_online_count = 0;

// PHPSocketIO服務
$sender_io = new SocketIO(2120);

// 客戶端發起連線事件時,設定連線socket的各種事件回撥
// 當$sender_io啟動後監聽一個http埠,透過這個埠可以給任意uid或者所有uid推送資料
$sender_io->on('workerStart', function(){

    // 監聽一個http埠
    $inner_http_worker = new Worker('http://0.0.0.0:2121');

    // 當http客戶端發來資料時觸發
    $inner_http_worker->onMessage = function($http_connection, $data){
        global $uidConnectionMap;
        $_POST = $_POST ? $_POST : $_GET;

        // 推送資料的url格式 type=publish&to=uid&content=xxxx
        switch(@$_POST['type']){

            case 'publish':

                global $sender_io;
                $to = @$_POST['to'];
                $_POST['content'] = htmlspecialchars(@$_POST['content']);

                // 有指定uid則向uid所在socket組傳送資料
                if($to){
                    $sender_io->to($to)->emit('new_msg', $_POST['content']);

                // 否則向所有uid推送資料
                }else{
                    $sender_io->emit('new_msg', @$_POST['content']);

                }

                // http介面返回,如果使用者離線socket返回fail
                if($to && !isset($uidConnectionMap[$to])){
                    return $http_connection->send('offline');

                }else{
                    return $http_connection->send('ok');

                }
        }
        return $http_connection->send('fail');

    };

});


if(!defined('GLOBAL_START'))
{
    Worker::runAll();
}

以上核心程式碼已經提供,大家可以試著去實踐一下

本作品採用《CC 協議》,轉載必須註明作者和本文連結
溫馨提示:微信搜尋並關注   學Laravel  裡面有兩套Laravel課程:【laravel7.x 從入門到核心架構講解】 與 【Laravel高階實戰教程42集】,直接獲取就可以了,學習與交流可加入:Laravel技術交流微信群

相關文章