用户访问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