基于socket.io的实时消息推送

用户访问Web站点的过程是基于HTTP协议的,而HTTP协议的工作模式是:请求-响应,客户端发出访问请求,服务器端以资源数据响应请求。 也就是说,服务器端始终是被动的,即使服务器端的资源数据发生变化,如果没有来自客户端的请求,用户就不会看到这些变化。 这种模式是不适合某些应用场景的,比如在社交网络用户需要近乎实时地知道其他用户最新的信息。对于普通站点来说, 请求-响应模式可以满足绝大多数的功能需求,但总有某些功能我们希望能够为用户提供实时消息的体验。

为解决这个问题,有两种方案可以选择:

  1. 仍旧使用请求-响应模式,只是增大请求的频率或者使用长连接,来达到尽可能接近实时的效果,如使用polling/long-polling,但可能会极大地增加服务器的负载压力或降低服务器的吞吐量
  2. 使用新的协议,在服务器端有资源数据更新时,主动推送给客户端,如WebSocket,虽然这种思路也是使用了长连接,但效率更高,且是客户端服务器端之间的全双工通信。 问题在于目前各大浏览器并不都支持WebSocket。

那么目前最好的方式就是结合以上两种方案,在不同的浏览器中,尽可能使用浏览器支持的最好的方案,即浏览器支持第二种方案时,优先使用第二种方案,否则使用第一种方案。socket.io就是这么做的,并且在服务器端和客户端对于不同的方案提供统一的接口。



在我们产品的站内信功能中,希望能够给在线用户实时推送公共消息或私有消息。考虑到以后可能还有其他功能需要实现实时消息推送,所以将实时消息推送实现为一个单独的服务。这种针对不同特性的功能进行解耦也为之后针对性的优化做了铺垫。

解耦之后的系统结构如下所示:

当站点服务器(A)监测到资源数据更新事件发生时,先将数据推送到消息推送服务器(B),B根据消息的类型以及消息的目标接收人来决定是否推送,如何推送。

由于我们的Web后端是基于Yii框架实现,那么该如何实现A与B的socket.io服务通信呢?socket.io有自己的一套协议,如果自己实现PHP库来与socket.io服务交互,还有一些工作量。最终我们选择elephant.io这个PHP库,并将elephant.io封装为Yii框架的一个组件,实现如下:

<?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 !== null) {
                $this->ioNameSpace = $this->elephant->createFrame(null, $this->namespace);
            } else {
                throw new Exception(‘%s: %s: %s, Please setNameSpace before sendMsg‘, basename(
                    __FILE__
                ), __FUNCTION__, __LINE__);
            }
        }
        $this->ioNameSpace->emit($event, $msg);
    }

    public function close()
    {
        $this->elephant->close();
        $this->elephant = null;
    }
}

将该代码文件放在应用目录extensions下,然后为Yii添加如下配置项:

‘components‘ => array(
    ‘ElephantIO‘ => array(
        ‘class‘ => ‘application.extensions.extElephantIO‘,
        ‘host‘ => ‘xxx‘,
        ‘port‘ => xxx,
    ),
    ...
),

当有资源数据变更事件产生时,如下调用向消息推送服务器发送消息:

$elephant = Yii::app()->ElephantIO;
$elephant->setNameSpace(‘/message_namespace‘);
$elephant->sendMsg(
    ‘message_event_type‘,
    $messageContent
);
$elephant->close();


对于私有消息推送,如何判断用户当前是否在线?如何验证用户的身份?

可以基于cookie来实现,socket.io提供的浏览器端JS库,在每次连接时,和普通HTTP请求一样,会携带站点域名下的cookie(我们的消息推送服务的域名为站点服务器域名的子域,所以能拿到站点域名下的所有cookie),消息推送服务器在接收到连接(connection事件)请求时,从连接中取出所有cookie,然后向站点Web后端的某个API转发这些cookie,这个API根据cookie验证用户身份,并将用户信息返回给消息推送服务器,消息推送服务器根据用户信息存储当前连接对象,之后当站点服务器向消息推送服务器发送该用户的消息时,就通过该连接对象给用户推送消息。

对于公有消息,即广播消息,实现则比较简单,直接向当前所有连接推送消息即可。



也许有人会问,既然在某些浏览器中socket.io会退化为使用polling/long-polling来传输消息,那么相比直接向站点服务器进行polling/long-polling,有什么优势吗?

我认为优势有两点:

  1. NodeJS的异步事件回调的方式,适合大并发长连接的应用场景。如果Web后端是使用PHP等实现,则更适合短连接的服务。
  2. 将站点的业务逻辑与消息推送逻辑解耦,那么浏览器通过polling/long-polling来获取消息时,只是涉及消息推送逻辑,不需要执行业务逻辑的代码,而业务逻辑的代码可能很复杂,每次polling,都需要执行一遍的话,会浪费服务器很多资源。
时间: 2024-12-23 17:54:20

基于socket.io的实时消息推送的相关文章

开源实时消息推送系统 MPush

系统介绍 mpush,是一款开源的实时消息推送系统,采用java语言开发,服务端采用模块化设计,具有协议简洁,传输安全,接口流畅,实时高效,扩展性强,可配置化,部署方便,监控完善等特点.同时也是少有的可商用的开源push推送系统. 特性和优势 源码全部开放,包括server.android.ios .websocket等 代码质量高,全部模块化设计,真正的商用级产品,考虑到推送中遇到的大部分场景 安全性高,基于RSA精简的加密握手协议,简单,高效,安全 支持断线重连,及弱网下的快速重连,无网络下

基于C++ 的苹果apns消息推送实现(2)

1.本模块使用C++ 和 Openssl 代码 实现了一个简单的apns客户端 2.本文的姐妹篇:基于boost 的苹果apns消息推送实现(1) 3.最初使用的sslv23/sslv2/sslv3只能和apple 建立连接,但一直是handshake失败, 最后换tls连接,握手成功! original_ssl_client.h #ifndef original_ssl_client_h #define original_ssl_client_h #pragma once #include <

【纵横科技】基于dwr框架的reverseajax消息推送 有图有图!

原文:[纵横科技]基于dwr框架的reverseajax消息推送 有图有图! 源代码下载地址:http://www.zuidaima.com/share/1584228052847616.htm\ 网络的最初设计是不允许服务器向浏览器发起连接,所以从浏览器及时的获取数据是个棘手的问题. 反向Ajax是具备有从web服务器向浏览器异步的发送数据的能力. 实现原理是保持客服端与服务器的长连接. 这里就附上一个简单的例子咯,给大家玩玩. 本人使用的是NetBeans编辑器,有不会转到MyEclipse

[转载]使用node.js+socket.io搭建实时消息系统

在开发web应用时,经常会有消息接收需求.例如后台处理完某个任务,需要告知用户等.一个简单的做法,是使用ajax轮询.这样带来的问题一是低效,二是消息触达不够实时.另一个方法是使用websocket来接收消息,但可惜IE不支持这种方式.下面推荐一种既能实时接收消息,又能兼容各种浏览器的方案,那就是node.js+socket.io. node.js的异步非阻塞模型,做消息推送非常合适.socket.io则负责屏蔽浏览器的差异,其会选择性的使用下列方式建立连接:websocket, flash s

读《架构师于小波:魅族实时消息推送架构》总结

原文网址:http://www.csdn.net/article/2015-12-22/2826542 1.系统架构设计.系统架构逻辑上划分,划分为四层,最下面的一个是提供魅族手机的接入.第二层是消息分发服务,主要的作用就是提供上行消息的路由和用户下行消息的路,这边有一个用户路由表.第三层是订阅信息,第四层是存储,包括离岸消息存储,包括订阅消息的存储. 2.手机功耗问题主要涉及两个点,第一个是流量,第二个是电量.先看流量的问题,怎么样解决流量的问题,通过协议选择,现在传统的互联网上,有比较典型的

基于Qt移动应用的消息推送服务原理与应用

说到移动应用,大家都觉得移动嘛,当然是Java和Object-c来做啦,什么推送啊,各种系统调用啊,其实不然?如果你了解Qt, 你就知道我说的不然,也有所道理. 说道几点 一.目前Android的移动的消息.通知推送 1)轮询(Pull)方式:应用程序应当阶段性的与服务器进行连接并查询是否有新的消息到达,你必须自己实现与服务器之间的通信,例如消息排队等.而且你还要考虑轮询的频率,如果太慢可能导致某些消息的延迟,如果太快,则会大量消耗网络带宽和电池. 2)SMS(Push)方式:在Android平

基于boost 的苹果apns消息推送实现(1)

1. 为了当时测试,做了2份C++代码实现,一份是基于boost的实现 较完整,一份是C++加Openssl实现(可以用,不少细节需要调整) 2. 本模块只涉及apns客户端部分 3. 涉及boost的主要模块有boost bind,boost asio,boost ssl,boost deadline_timer, boost 正则表达式 4. 有一点需要注意,最初使用的sslv23/sslv2/sslv3只能和apple 建立连接,但一直是handshake失败,郁闷了相当长一段时间 最后换

Socket,长连接,消息推送,消息提醒,未读消息提醒,消息通知,未读消息通知

今天公司要搞个类似QQ空间的未读消息通知,于是想到用WebSocket长连接,搜索一些资料后,调试好久,还以为不行,结果发现是IIS版本的原因,我本地用的Win7的系统,是IIS6的,本地测试一直不行,后来查资料发现用这个WebSocket要IIS8,我直接放服务器上,测试就OK了,废话不多说,直接上代码.我用的MVC+EF的框架. 1.新建一个Controller,继承APIController [Description("消息")] public class MessageCont

MPush开源消息推送系统:简洁、安全、支持集群

引言 由于之前自己团队需要一个消息推送系统来替换JPUSH,一直找了很久基本没有真正可用的开源系统 所有就直接造了个轮子,造轮子的时候就奔着开源做打算的,只是后来创业项目失败一直没时间整理 这一套代码,最近比较闲就拿出来给开源做点贡献. 作为Java版的开源推送系统,MPUSH还是有很多不错的设计的,特别是对想自己搭建一套推送系统的团队 是有很大的借鉴意义的.当然开源出来也是不想曾经做过的工作白白浪费掉,特别希望对这方面有兴趣的同学 来一起把这套东西做的更好,服务更多的用户! 项目主页 http