下面由workerman教學專欄給大家介紹實作基於workerman的即時推播,拋棄ajax輪詢的方法,希望對需要的朋友有幫助!
先扯些這些內容:
TCP/IP
TCP/IP是個協定群組,可分為三個層次:網路層、傳輸層、應用層。
在網路層有IP協定、ICMP協定、ARP協定、RARP協定和BOOTP協定。
在傳輸層中有TCP協定與UDP協定。
在應用層有:
TCP包含FTP、HTTP、TELNET、SMTP等協定
UDP包含DNS、TFTP等協定
短連線
連線->傳輸資料->關閉連線
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為例):
1、ThinkPHP與Workerman是兩個獨立的系統,獨立部署(可部署在不同伺服器),互不干擾。
2、ThinkPHP以HTTP協定提供網頁頁面在瀏覽器渲染展示。
3、ThinkPHP提供的頁面的js發起websocket連接,連接workerman
4、連接後給Workerman發送一個數據包(包含用戶名密碼或某種token串)用於驗證websocket連線屬於哪個用戶。
5、只有ThinkPHP需要向瀏覽器推送資料時,才會呼叫workerman的socket介面推送資料。
6、其餘請求還是依照原本ThinkPHP的HTTP方式呼叫處理。
總結:
把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(); }
ok,大功告成!
以上是基於workerman的即時推播(摒棄ajax輪詢)的詳細內容。更多資訊請關注PHP中文網其他相關文章!