web实时通信应用解决方案:WebSocket模拟库-SockJS_蓝戒的博客


web实时通信应用解决方案:WebSocket模拟库-SockJS

SockJS简介:

SockJS是一个浏览器的JavaScript库,它提供了一个类似的WebSocket对象。 SockJS为您提供了一个连贯的,跨浏览器的JavaScript API创建一个低延迟,全双工,浏览器和Web服务器之间的跨域通信通道。

官网:http://sockjs.org

git项目网址:https://github.com/sockjs/sockjs-client

SockJS family:

Work in progress:

SockJS特点:

客户端和服务器端api尽可能简洁,尽量靠近websocket api
支持服务端扩展和负载均衡技术
传输层应该全面支持跨域通信
如果受到代理服务器的限制,传输层能优雅地从一种方式回退到另一种方式
尽可能快地建立连接
客户端只是纯粹的JavaScript,不需要flash
客户端JavaScript必须经过严格的测试
服务器端代码尽可能简单,降低用另一种语言重写server的代价

实际上sockjs的目标也就是sockjs具有的特点。

SockJS: WebSocket emulation done right一文中对sockjs的特点进行了具体阐述。

sockjs几个特点,非常值得一提

跨域通信

sockjs服务器端支持跨域通信,意味着我们可以将sockjs server独立出来,把它放在与web主站点不同的域名之上,实际上这是比较合理的部署策略。关于跨域,也是一个比较大的话题,其中有一个机制叫cors(跨域资源共享)主要解决JavaScript不能跨域请求的问题。sockjs服务器应该支持这种机制。

负载均衡

无论server端优化得再好,一个sockjs server的处理能力都是有限的,我们更需要的是一种可扩展的解决方案。一种非常简单的方法是把每一个sockjs server放到一个不同的域名之下,如sockjs1.example.com和sockjs2.example.com,允许客户端随机选择一个server。

Prefix-based sticky sessions

在sockjs中,一个典型的url如下:

http://localhost:8000/chat/<serverid>/<sessionid>/

url中的第二个参数sessionid,必须是一个随机字符串,唯一标识一个session。第一个参数serverid,主要应用于负载均衡目的。负载均衡器可以利用这个serverid参数作为一个线索,进行负载均衡分流。具体使用方面,参考HAProxy的一个配置参考文件,其中关键的配置在于

balance uri depth 2

JSESSIONID cookie sticky sessions

另外一种负载均衡配置方案,主要利用含有jsessionid的cookie。这个cookie由socketjs server进行设置,当response到达负载均衡器的时候,jessionid会被加上一个额外的前缀或者后缀,具体原理方面可以参考阅读
LOAD BALANCING, AFFINITY, PERSISTENCE, STICKY SESSIONS: WHAT YOU NEED TO KNOW一文。

健壮的传输协议

我们知道HTML5 的websocket协议定义了websocket api使得网页可以利用websocket协议和远端主机进行全双工通信。websocket协议应该是最快,最好的web通信协议,已被大多数的浏览器所支持。那么为什么还需要sockjs进行封装?

在真实的网络世界中,实际上有着非常复杂的网络拓扑结构,在浏览器和server之间,含有很多的中间节点,包括路由器,代理服务器,反向代理服务器,负载均衡器等等。即使Html5 websocket协议已经成为了标准,但是这些中间节点并不一定会遵守这些标准,还有很大可能会阻止websocket handshake的过程,结果无法建立websocket连接。

How HTML5 Web Sockets Interact With Proxy Servers一文中提到了websocket协议和代理服务器的“不友善关系”,源于代理服务器对websocket handshake的阻挠和对长连接,空闲连接的关闭处理,让我们看到了如果只是直接利用websocket协议,实在是困难重重。

sockjs的出现,实际是为了解决这个问题,使得人们可以建立健壮的web实时通信程序。

sockjs服务器传输协议不仅提供了websocket协议的支持,还提供了流传输Streaming和轮询Polling ,其中又包括多种底层传输方案,如:
xhr,xhr_streaming,jsonp,eventsource,htmlfile。每一种传输方案,其实都值得开辟一个章节来大写特写。

如果浏览器客户端js,采用websocket连接不上服务器,它可以回退选择其他传输方案,那么确保总可以利用一种传输协议,连接到服务器。那么开发者就不需要理睬那些可恶的中间节点了。

使用方法

SockJS模拟WebSockets API,而且是WebSocket SockJS Javascript对象。首先,您需要加载SockJS JavaScript库。例如,您可以把它放在你的HTML头:

<script src="https://cdn.jsdelivr.net/sockjs/1/sockjs.min.js"></script>

脚本加载后可以与SockJS服务器建立一个连接。这是一个简单的例子:

var sock = new SockJS('https://mydomain.com/my_prefix');
sock.onopen = function() {
console.log('open');
};
sock.onmessage = function(e) {
console.log('message', e.data);
};
sock.onclose = function() {
console.log('close');
};

sock.send('test');
sock.close();

SockJS-client API

SockJS类

相似的WebSocket API,SockJS的构造函数接受一个,或多个参数:

var sockjs = new SockJS(url, _reserved, options);

url可能包含一个查询字符串,如果你需要的话。

实践代码片段:

//注:这里的消息提醒需要引入iNotifyJS库,协议处理需要引入stompJS库
var iN = new iNotify().init({
  effect: 'scroll',
  message:"有消息拉!",
  audio:{
    file: 'static/dist/others/4331.mp3'//可以使用数组传多种格式的声音文件
  },
  notification:{
    title:"通知!",
    icon:"",
    body:'您来了一条新消息'
  }
});
iN.isPermission();
var client;
var url = "/imapi/stomp";
var ws = new SockJS(url);
client = Stomp.over(ws);
var headers = {
  login: '123',
  passcode: ''
};
client.connect(headers, function(success) {

      //iN.notify().player().setFavicon(10);
      var subscription = client.subscribe("/topic/site/"+main.userObject.siteId, function(message) {
        main.totalFunc();

        // 文本格式uri解码
        message = decodeURI(message);

        // 解码之后还会有一些残留的编码, 替换掉
        message = message.replace(/%3A/g,':');
        message = message.replace(/%2C/g,',');
        message = message.replace(/\+/g,' ');

        // 截取报文, 以及去掉最后一个结束符 '='
        message = message.substring(message.indexOf("{"), message.lastIndexOf('='));  

        // 转换为json对象
        message = JSON.parse(message);

        if(message && message.site) {
            iN.notify({
                 title: message.site.title,
                 body: message.site.body
             }).player();

             //console.info(message);
        }

        // 待处理订单数量
        if(message && message.repair) {
           // msgType : 报修类型
           // count_wait_for_process : 待受理的订单数量
           // count_wait_for_check : 已申请验收的订单数量
            if(message.repair.msgType == 'HOME_REPAIR'){
                main.messageSend.count_wait_for_process_homeOrder = message.repair.count_wait_for_process;
                main.messageSend.count_wait_for_check_homeOrder = message.repair.count_wait_for_check;
            }
            if(message.repair.msgType == 'PUBLIC_REPAIR'){
                main.messageSend.count_wait_for_process_publicOrder = message.repair.count_wait_for_process;
                main.messageSend.count_wait_for_check_publicOrder = message.repair.count_wait_for_check;
            }
            if(message.repair.msgType == 'COMMUNITY_COMPLAINT'){
                main.messageSend.count_wait_for_process_complaintOrder = message.repair.count_wait_for_process;
                main.messageSend.count_wait_for_check_complaintOrder = message.repair.count_wait_for_check;
            }

           // 打印结构
           //console.info(message.repair);
        }

      });
    }
    ,function(error){
      console.log('connect error:'+error);//连接出错或掉线时会回调这个方法。
      util.showTip("实时推送消息连接未成功或连接掉线,请刷新网页重连");
    }
);

参考资料:

1.http://blog.csdn.net/wyx819/article/details/46592153

本文固定链接: http://www.webzsky.com/?p=1053 | 蓝戒的博客

cywcd
该日志由 cywcd 于2017年01月13日发表在 javascript 分类下, 你可以发表评论,并在保留原文地址及作者的情况下引用到你的网站或博客。
原创文章转载请注明: web实时通信应用解决方案:WebSocket模拟库-SockJS | 蓝戒的博客
关键字: ,

web实时通信应用解决方案:WebSocket模拟库-SockJS:等您坐沙发呢!

发表评论


快捷键:Ctrl+Enter
来自的朋友,欢迎您 点击这里 订阅我的博客 o(∩_∩)o~~~
×