默认
打赏 发表评论 7
想开发IM:买成品怕坑?租第3方怕贵?找开源自已撸?尽量别走弯路了... 找站长给点建议
socket.io实现消息推送的一点实践及思路
阅读(93050) | 评论(7 收藏3 淘帖2
微信扫一扫关注!

前言


对于想要实现Web端的实时通讯,因为用户访问Web站点的过程是基于HTTP协议的,而HTTP协议的工作模式是:请求-响应,客户端发出访问请求,服务器端以资源数据响应请求。

也就是说,服务器端始终是被动的,即使服务器端的资源数据发生变化,如果没有来自客户端的请求,用户就不会看到这些变化。 这种模式是不适合某些应用场景的,比如在社交网络用户需要近乎实时地知道其他用户最新的信息。

对于普通站点来说, 请求-响应模式可以满足绝大多数的功能需求,但总有某些功能我们希望能够为用户提供实时消息的体验。

更多资料


Web端即时通讯新手入门贴:
新手入门贴:详解Web端即时通讯技术的原理

Web端即时通讯技术盘点请参见:
Web端即时通讯技术盘点:短轮询、Comet、Websocket、SSE

关于Ajax短轮询:
找这方面的资料没什么意义,除非忽悠客户,否则请考虑其它3种方案即可。

有关Comet技术的详细介绍请参见:
Comet技术详解:基于HTTP长连接的Web端实时通信技术
WEB端即时通讯:HTTP长连接、长轮询(long polling)详解
WEB端即时通讯:不用WebSocket也一样能搞定消息的即时性
开源Comet服务器iComet:支持百万并发的Web端即时通讯方案

有关WebSocket的详细介绍请参见:
WebSocket详解(一):初步认识WebSocket技术
WebSocket详解(二):技术原理、代码演示和应用案例
WebSocket详解(三):深入WebSocket通信协议细节
Socket.IO介绍:支持WebSocket、用于WEB端的即时通讯的框架
socket.io和websocket 之间是什么关系?有什么区别?

有关SSE的详细介绍文章请参见:
SSE技术详解:一种全新的HTML5服务器推送事件技术

更多WEB端即时通讯文章请见:
http://www.52im.net/forum.php?mod=collection&action=view&ctid=15

方案选择


有两种方案可以选择:

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

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

我的实现


1基本思路


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

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

socket.io实现消息推送的一点实践及思路_socket.io-push-server.png

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

2如何实现socket.io服务通信呢?


由于我们的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();

3如何判断用户当前是否在线以及验证用户的身份?


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

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

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

小结


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

我认为优势有两点:

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

全站即时通讯技术资料分类


[1] 网络编程基础资料:
TCP/IP详解 - 第11章·UDP:用户数据报协议
TCP/IP详解 - 第17章·TCP:传输控制协议
TCP/IP详解 - 第18章·TCP连接的建立与终止
TCP/IP详解 - 第21章·TCP的超时与重传
理论经典:TCP协议的3次握手与4次挥手过程详解
理论联系实际:Wireshark抓包分析TCP 3次握手、4次挥手过程
计算机网络通讯协议关系图(中文珍藏版)
NAT详解:基本原理、穿越技术(P2P打洞)、端口老化等
UDP中一个包的大小最大能多大?
Java新一代网络编程模型AIO原理及Linux系统AIO介绍
NIO框架入门(三):iOS与MINA2、Netty4的跨平台UDP双向通信实战
NIO框架入门(四):Android与MINA2、Netty4的跨平台UDP双向通信实战
>> 更多同类文章 ……

[2] 有关IM/推送的通信格式、协议的选择:
为什么QQ用的是UDP协议而不是TCP协议?
移动端即时通讯协议选择:UDP还是TCP?
如何选择即时通讯应用的数据传输格式
强列建议将Protobuf作为你的即时通讯应用数据传输格式
移动端IM开发需要面对的技术问题(含通信协议选择)
简述移动端IM开发的那些坑:架构设计、通信协议和客户端
理论联系实际:一套典型的IM通信协议设计详解
58到家实时消息系统的协议设计等技术实践分享
>> 更多同类文章 ……

[3] 有关IM/推送的心跳保活处理:
Android进程保活详解:一篇文章解决你的所有疑问
Android端消息推送总结:实现原理、心跳保活、遇到的问题等
为何基于TCP协议的移动端IM仍然需要心跳保活机制?
微信团队原创分享:Android版微信后台保活实战分享(进程保活篇)
微信团队原创分享:Android版微信后台保活实战分享(网络保活篇)
移动端IM实践:实现Android版微信的智能心跳机制
移动端IM实践:WhatsApp、Line、微信的心跳策略分析
>> 更多同类文章 ……

[4] 有关WEB端即时通讯开发:
新手入门贴:史上最全Web端即时通讯技术原理详解
Web端即时通讯技术盘点:短轮询、Comet、Websocket、SSE
SSE技术详解:一种全新的HTML5服务器推送事件技术
Comet技术详解:基于HTTP长连接的Web端实时通信技术
WebSocket详解(一):初步认识WebSocket技术
socket.io实现消息推送的一点实践及思路
>> 更多同类文章 ……

[5] 有关IM架构设计:
浅谈IM系统的架构设计
简述移动端IM开发的那些坑:架构设计、通信协议和客户端
一套原创分布式即时通讯(IM)系统理论架构方案
从零到卓越:京东客服即时通讯系统的技术架构演进历程
蘑菇街即时通讯/IM服务器开发之架构选择
腾讯QQ1.4亿在线用户的技术挑战和架构演进之路PPT
微信技术总监谈架构:微信之道——大道至简(演讲全文)
如何解读《微信技术总监谈架构:微信之道——大道至简》
快速裂变:见证微信强大后台架构从0到1的演进历程(一)
17年的实践:腾讯海量产品的技术方法论
>> 更多同类文章 ……

[6] 有关IM安全的文章:
即时通讯安全篇(一):正确地理解和使用Android端加密算法
即时通讯安全篇(二):探讨组合加密算法在IM中的应用
即时通讯安全篇(三):常用加解密算法与通讯安全讲解
即时通讯安全篇(四):实例分析Android中密钥硬编码的风险
传输层安全协议SSL/TLS的Java平台实现简介和Demo演示
理论联系实际:一套典型的IM通信协议设计详解(含安全层设计)
微信新一代通信安全解决方案:基于TLS1.3的MMTLS详解
来自阿里OpenIM:打造安全可靠即时通讯服务的技术实践分享
>> 更多同类文章 ……

[7] 有关实时音视频开发:
即时通讯音视频开发(一):视频编解码之理论概述
即时通讯音视频开发(二):视频编解码之数字视频介绍
即时通讯音视频开发(三):视频编解码之编码基础
即时通讯音视频开发(四):视频编解码之预测技术介绍
即时通讯音视频开发(五):认识主流视频编码技术H.264
即时通讯音视频开发(六):如何开始音频编解码技术的学习
即时通讯音视频开发(七):音频基础及编码原理入门
即时通讯音视频开发(八):常见的实时语音通讯编码标准
即时通讯音视频开发(九):实时语音通讯的回音及回音消除概述
即时通讯音视频开发(十):实时语音通讯的回音消除技术详解
即时通讯音视频开发(十一):实时语音通讯丢包补偿技术详解
即时通讯音视频开发(十二):多人实时音视频聊天架构探讨
即时通讯音视频开发(十三):实时视频编码H.264的特点与优势
即时通讯音视频开发(十四):实时音视频数据传输协议介绍
即时通讯音视频开发(十五):聊聊P2P与实时音视频的应用情况
即时通讯音视频开发(十六):移动端实时音视频开发的几个建议
即时通讯音视频开发(十七):视频编码H.264、V8的前世今生
简述开源实时音视频技术WebRTC的优缺点
良心分享:WebRTC 零基础开发者教程(中文)
>> 更多同类文章 ……

[8] IM开发综合文章:
移动端IM开发需要面对的技术问题
开发IM是自己设计协议用字节流好还是字符流好?
请问有人知道语音留言聊天的主流实现方式吗?
IM系统中如何保证消息的可靠投递(即QoS机制)
谈谈移动端 IM 开发中登录请求的优化
完全自已开发的IM该如何设计“失败重试”机制?
微信对网络影响的技术试验及分析(论文全文)
即时通讯系统的原理、技术和应用(技术论文)
开源IM工程“蘑菇街TeamTalk”的现状:一场有始无终的开源秀
>> 更多同类文章 ……

[9] 开源移动端IM技术框架资料:
开源移动端IM技术框架MobileIMSDK:快速入门
开源移动端IM技术框架MobileIMSDK:常见问题解答
开源移动端IM技术框架MobileIMSDK:压力测试报告
开源移动端IM技术框架MobileIMSDK:Android版Demo使用帮助
开源移动端IM技术框架MobileIMSDK:Java版Demo使用帮助
开源移动端IM技术框架MobileIMSDK:iOS版Demo使用帮助
开源移动端IM技术框架MobileIMSDK:Android客户端开发指南
开源移动端IM技术框架MobileIMSDK:Java客户端开发指南
开源移动端IM技术框架MobileIMSDK:iOS客户端开发指南
开源移动端IM技术框架MobileIMSDK:Server端开发指南
>> 更多同类文章 ……

[10] 有关推送技术的文章:
iOS的推送服务APNs详解:设计思路、技术原理及缺陷等
Android端消息推送总结:实现原理、心跳保活、遇到的问题等
扫盲贴:认识MQTT通信协议
一个基于MQTT通信协议的完整Android推送Demo
求教android消息推送:GCM、XMPP、MQTT三种方案的优劣
移动端实时消息推送技术浅析
扫盲贴:浅谈iOS和Android后台实时消息推送的原理和区别
绝对干货:基于Netty实现海量接入的推送服务技术要点
移动端IM实践:谷歌消息推送服务(GCM)研究(来自微信)
为何微信、QQ这样的IM工具不使用GCM服务推送消息?
>> 更多同类文章 ……

[11] 更多即时通讯技术好文分类:
http://www.52im.net/forum.php?mod=collection&op=all

(原文链接:http://blog.xiayf.cn/2014/09/06/socket.io-push-server/

即时通讯网 - 即时通讯开发者社区! 来源: - 即时通讯开发者社区!

上一篇:绝对干货:基于Netty实现海量接入的推送服务技术要点下一篇:扫盲贴:浅谈iOS和Android后台实时消息推送的原理和区别

本帖已收录至以下技术专辑

推荐方案
评论 7
感谢 http://blog.xiayf.cn/ 博主的分享。
大侠, php如何根据cookie验证用户身份, 你说的API根据cookie验证用户身份是如何做到的,请点拨一下
引用:TonyWangx 发表于 2016-03-30 10:57
大侠, php如何根据cookie验证用户身份, 你说的API根据cookie验证用户身份是如何做到的,请点拨一下

原文楼主的回复如下:

cookie的产生是为了解决HTTP协议无状态的问题。可以识别用户、避免每次页面访问都需要用户登录。cookie是存在用户浏览器中的,对于同一个站点页面的请求浏览器都会自动在请求头中带上所有的cookie。与cookie配合使用的是服务器端的session,每个session有一个hash值id,session以key、value的形式存储,key就是session id,value就是应用程序往该session中存入的数据序列化后的值,比如其中包含用户id,另外通常session还会存储一个过期时间。

cookie和session的工作流程一般是这样:用户登录后,服务器端web程序开启一个session,将用户的相关信息存到session的value中,并将session id作为一个cookie项(该cookie具体的名字可以自定义)放到请求响应头set-cookie中发送给用户浏览器。之后对该站点的页面请求都会带上这个cookie,服务器端web程序尝试从cookie中读取session id,如果读到,则以该session id为key,读出session的value,反序列化后得到用户的信息,成功读取用户的信息就算用户身份验证通过了。

session的存储方法,php中默认是文件,文件名就是session id,web程序也可以配置session的存储方式为关系型数据库,NoSQL数据库等。

上述的过程,一些常用的web框架已经自动帮我们完成了。

php中cookie和session的操作方法见:
http://php.net/manual/zh/features.sessions.php
http://php.net/manual/zh/features.cookies.php
想要建立实时web,推荐GoEasy的web实时推送,他们是专注做Web实时推送的, 网上一搜就有其用户自己总结的实例和感想,使用非常简单,都在说几分钟就可以实现推送了,哈哈。 我们项目也在用,推送很稳定!介绍给大家,避免走很多弯路。:-)官网:https://goeasy.io
另外,大家不要被他们的英文版本官网吓到哦,他们有提供中文API文档的哈。
在线demo: https://goeasy.io/www/examples.jsp
引用:点点滴滴 发表于 2016-05-25 17:55
想要建立实时web,推荐GoEasy的web实时推送,他们是专注做Web实时推送的, 网上一搜就有其用户自己总结的实 ...

好吧,这东西什么来路,云IM?开源?不过这网站是真“黑”:
socket.io实现消息推送的一点实践及思路_QQ20160525-2.png
引用:JackJiang 发表于 2016-05-25 18:01
好吧,这东西什么来路,云IM?开源?不过这网站是真“黑”:

哈哈, 哥们儿好幽默,黑是黑,但是速度快啊!
确实“黑”,太不讲究了
打赏楼主 ×
使用微信打赏! 使用支付宝打赏!

返回顶部