workerman實現PHP實時消息推送,getwayworker知足你的項目需求









爲了帶領你們深刻學習workerman以及getwayworker,讓你們迅速在本身項目中使用workerman,使用Websocket協議,實現長鏈接,擺脫長輪詢,詳細完整的講解如何利用workerman開發即時通信,而且部署到本身的項目裏以及運行在Linux系統裏面。 無論你的項目是基於ThinkPHP仍是laravel或者yii,甚至微擎,無論你是pc端應用仍是移動端應用,均可以在本身的項目裏面完整的實現通信模塊。
首先了解一下通信協議:

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


在應用層有:
TCP包括FTP、HTTP、TELNET、SMTP等協議
UDP包括DNS、TFTP等協議
html

短鏈接
鏈接->傳輸數據->關閉鏈接
HTTP是無狀態的,瀏覽器和服務器每進行一次HTTP操做,就創建一次鏈接,但任務結束就中斷鏈接。
也能夠這樣說:短鏈接是指SOCKET鏈接後發送後接收完數據後立刻斷開鏈接。

 
長鏈接
鏈接->傳輸數據->保持鏈接 -> 傳輸數據-> 。。。->關閉鏈接。
長鏈接指創建SOCKET鏈接後不論是否使用都保持鏈接,但安全性較差。


http的長鏈接
HTTP也能夠創建長鏈接的,使用Connection:keep-alive,HTTP 1.1默認進行持久鏈接。HTTP1.1和HTTP1.0相比較而言,最大的區別就是增長了持久鏈接支持(貌似最新的 http1.0 能夠顯示的指定 keep-alive),但仍是無狀態的,或者說是不能夠信任的。
 
何時用長鏈接,短鏈接?
長鏈接多用於操做頻繁,點對點的通信,並且鏈接數不能太多狀況,。每一個TCP鏈接都須要三步握手,這須要時間,若是每一個操做都是先鏈接,再操做的話那麼處理速度會下降不少,因此每一個操做完後都不斷開,次處理時直接發送數據包就OK了,不用創建TCP鏈接。例如:數據庫的鏈接用長鏈接, 若是用短鏈接頻繁的通訊會形成socket錯誤,並且頻繁的socket 建立也是對資源的浪費。
 
而像WEB網站的http服務通常都用短連接,由於長鏈接對於服務端來講會耗費必定的資源,而像WEB網站這麼頻繁的成千上萬甚至上億客戶端的鏈接用短鏈接會更省一些資源,若是用長鏈接,並且同時有成千上萬的用戶,若是每一個用戶都佔用一個鏈接的話,那可想而知吧。因此併發量大,但每一個用戶無需頻繁操做狀況下需用短連好。


workerman是啥?
Workerman是一款純PHP開發的開源高性能的PHP socket 服務器框架。被普遍的用於手機app、移動通信,微信小程序,手遊服務端、網絡遊戲、PHP聊天室、硬件通信、智能家居、車聯網、物聯網等領域的開發。支持TCP長鏈接,支持Websocket、HTTP等協議,支持自定義協議。擁有異步Mysql、異步Redis、異步Http、異步消息隊列等衆多高性能組件。

開始步入正題:爲了達到實時通信,不少時候咱們採用了ajax輪詢機制,後面能夠採用workerman方式來實現,項目也是tp寫的,官方手冊這麼說到
與其它mvc框架結合建議以上圖的方式(ThinkPHP爲例):
一、ThinkPHP與Workerman是兩個獨立的系統,獨立部署(可部署在不一樣服務器),互不干擾。
二、ThinkPHP以HTTP協議提供網頁頁面在瀏覽器渲染展現。
三、ThinkPHP提供的頁面的js發起websocket鏈接,鏈接workerman
四、鏈接後給Workerman發送一個數據包(包含用戶名密碼或者某種token串)用於驗證websocket鏈接屬於哪一個用戶。
五、僅在ThinkPHP須要向瀏覽器推送數據時,才調用workerman的socket接口推送數據。
六、其他請求仍是按照本來ThinkPHP的HTTP方式調用處理。


總結:
把Workerman做爲一個能夠向瀏覽器推送的通道,僅僅在須要向瀏覽器推送數據時才調用Workerman接口完成推送。業務邏輯所有在ThinkPHP中完成。
ok,到這裏,把workerman容器跑起來,注意這裏是CLI模式運行


而後再咱們項目接收信息中這麼寫,附上代碼laravel

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

接着,咱們在用戶向用戶發送信息的時候添加web

// 指明給誰推送,爲空表示向全部在線用戶推送
$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裏面的推送核心代碼實現ajax

// 全局數組保存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();
}


本文分享自微信公衆號 - Laravel技術社區(Laravel360)。
若有侵權,請聯繫 support@oschina.cn 刪除。
本文參與「OSC源創計劃」,歡迎正在閱讀的你也加入,一塊兒分享。sql

相關文章
相關標籤/搜索