用戶訪問Web站點的過程是基於HTTP協議的,而HTTP協議的工做模式是:請求-響應,客戶端發出訪問請求,服務器端以資源數據響應請求。 也就是說,服務器端始終是被動的,即便服務器端的資源數據發生變化,若是沒有來自客戶端的請求,用戶就不會看到這些變化。 這種模式是不適合某些應用場景的,好比在社交網絡用戶須要近乎實時地知道其餘用戶最新的信息。對於普通站點來講, 請求-響應模式能夠知足絕大多數的功能需求,但總有某些功能咱們但願可以爲用戶提供實時消息的體驗。php
爲解決這個問題,有兩種方案能夠選擇:git
那麼目前最好的方式就是結合以上兩種方案,在不一樣的瀏覽器中,儘量使用瀏覽器支持的最好的方案,即瀏覽器支持第二種方案時,優先使用第二種方案,不然使用第一種方案。socket.io就是這麼作的,而且在服務器端和客戶端對於不一樣的方案提供統一的接口。github
在咱們產品的站內信功能中,但願可以給在線用戶實時推送公共消息或私有消息。考慮到之後可能還有其餘功能須要實現實時消息推送,因此將實時消息推送實現爲一個單獨的服務。這種針對不一樣特性的功能進行解耦也爲以後針對性的優化作了鋪墊。後端
解耦以後的系統結構以下所示:瀏覽器
當站點服務器(A)監測到資源數據更新事件發生時,先將數據推送到消息推送服務器(B),B根據消息的類型以及消息的目標接收人來決定是否推送,如何推送。服務器
因爲咱們的Web後端是基於Yii框架實現,那麼該如何實現A與B的socket.io服務通訊呢?socket.io有本身的一套協議,若是本身實現PHP庫來與socket.io服務交互,還有一些工做量。最終咱們選擇elephant.io這個PHP庫,並將elephant.io封裝爲Yii框架的一個組件,實現以下:cookie
<?php $basePath = Yii::getPathOfAlias('application.vendor.elephantio.lib.ElephantIO'); require_once($basePath . DIRECTORY_SEPARATOR . 'Client.php'); require_once($basePath . DIRECTORY_SEPARATOR . 'Payload.php'); use ElephantIO\Client as Elephant; class extElephantIO extends CApplicationComponent { public $host = null; public $port = null; public $namespace = null; private $elephant = null; private $ioNameSpace = null; public function init() { if ($this->host === null || $this->port === null) { throw new Exception('%s: %s: %s, Please give me parameters host and port', basename( __FILE__ ), __FUNCTION__, __LINE__); } } public function setNameSpace($nameSpace) { if ($this->elephant === null) { $this->elephant = new Elephant('http://' . $this->host . ':' . $this->port, 'socket.io', 1, false, true, true); $this->elephant->init(); } $this->ioNameSpace = $this->elephant->createFrame(null, $nameSpace); } public function sendMsg($event, $msg) { if ($this->ioNameSpace === null) { if ($this->namespace