前端通讯:SSE设计方案(二)--- 服务器推送技术的实践以及一些应用场景的demo(包括在线及时聊天系统以及线上缓存更新,代码热修复案例)

  距离上一篇博客,这篇文章的发布大概过了整整三个月。下面废话很少说了,直接进入主题。html

上一篇博客介绍了基础的纯概念,这篇文章纯粹偏技术实践,须要理解一些玩意的。技术介绍前端

  • 客户端基础类库代码 -- SSE.js和ajax.1.7.js    客户端建立链接和定义监听的代码 以及结合ajax完成双工通道,完成双向均可推送
  • node -- SSE_server.js      node服务器代码,处理请求和消息推送
  • nginx     做为测试服务器进行浏览器和局域网测试

官方文档和设计方案node

SSE.js 代码改动(比较上一个版本)nginx

 1   // 抛出对象
 2   var output = {
 3     create:function (options) {
 4       var param = tool.initParam(options),sendData = '';
 5 
 6       if (param.data){
 7         tool.each(param.data, function (item, index) {
 8           sendData += (index + "=" + item + "&")
 9         });
10         sendData = sendData.slice(0, -1);
11       }
12 
13       var es = new EventSource(param.url+'?'+sendData);
14 
15       es.addEventListener('open',function (e) {
16         param.openEvent(e)
17       });
18 
19       es.addEventListener('message',function (e) {
20         param.messageEvent(e)
21       });
22 
23       es.addEventListener('error',function (e) {
24         param.errorEvent(e)
25         es.close()  // 关闭链接
26       });
27 
28       // 建立用户自定义事件
29       if (param.customEvent.length > 0){
30         tool.each(param.customEvent,function (item) {
31           es.addEventListener(item.name,item.callback);
32         })
33       }
34     }

 

改动:git

针对上一个版本,增长了数据验证以及一些参数的传递。虽然开启withCredentials能够传递信息的凭据,好比cookie,可是毕竟一个url,也是能够用url带参传递过去的,因此额外增长了这个玩意。so,这样咱们就能够在非ie系列的浏览器上痛快的翱翔了。github

PS:上面代码为建立的核心代码,其余代码能够到github上看,或者直接npm安装好了看。ajax

 

客户端的准备工做搞好了,下一步就是服务器的工做了,由于一个技术确定是多个技术配合才能完成的,不懂服务器的前端,不是好前端,这是我一项承认的事情。chrome

 

下面为简易的node测试代码,没有那么追求精简,为了测试搞的。npm

  1 var http = require("http");
  2 var url=require('url');
  3 var qs=require('querystring');//解析参数的库
  4 var sendObject = {}, count = 0;
  5 var gerry = {}
  6 
  7 // 向除本身的全部人推送信息
  8 function sendAll(data) {
  9   for (let index in sendObject){
 10     if (data.name !== index){
 11       sendObject[index].write("retry: " + data.retry + "\n");
 12       if (data.event){
 13         sendObject[index].write("event: " + data.event + "\n");
 14       }
 15 
 16       let sengData = {
 17         author:data.name,
 18         data:data.data
 19       }
 20       sendObject[index].write("data: " + JSON.stringify(sengData) + "\n\n");
 21     }
 22   }
 23 }
 24 
 25 // 聊天系统的demo推送测试
 26 http.createServer(function (req, res) {
 27 
 28     var arg1=url.parse(req.url,true).query;
 29     console.log(arg1.name);
 30     res.setHeader('Access-Control-Allow-Origin', '*');
 31     res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
 32     res.setHeader('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
 33     res.writeHead(200, {
 34         "Content-Type": "text/event-stream",
 35         "Cache-Control": "no-cache",
 36         "Connection": "keep-alive"
 37     });
 38 
 39     res.setTimeout(5000,()=>{
 40         res.write(":this is test")
 41     })
 42 
 43 
 44 
 45     sendObject[arg1.name] = res;
 46 }).listen(8074);
 47 
 48 // ajax双工执行发送机制
 49 http.createServer(function (req, res) {
 50     res.setHeader('Access-Control-Allow-Origin', '*');
 51     res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
 52     res.setHeader('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
 53     res.writeHead(200,{
 54         "content-type":"application/json; charset=utf-8"
 55     });
 56   var arr = []
 57     new Promise(function(x,xx){
 58       req.on("data",function(data){
 59         arr.push(data);
 60       });
 61 
 62       req.on("end",function(){
 63         var data= Buffer.concat(arr).toString(),ret;
 64         try{
 65           ret = JSON.parse(data);
 66           x(ret)
 67         }catch(err){}
 68       })
 69   }).then(function (req) {
 70       var data = {
 71         retry: 10000,
 72         name: req.name,
 73         data: req.message,
 74         event:req.event
 75       }
 76       sendAll(data)
 77       // res.write(Buffer.concat(arr).toString())
 78 
 79     })
 80   res.end()
 81 }).listen(8075)
 82 
 83 
 84 var cacheUpdate = {}
 85 
 86 // 缓存更新和线上正在使用的代码热修复和强制用户从新请求去拉取最新代码
 87 http.createServer(function (req, res) {
 88 
 89   var arg1=url.parse(req.url,true).query;
 90   console.log(arg1.name);
 91   res.setHeader('Access-Control-Allow-Origin', '*');
 92   res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
 93   res.setHeader('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
 94   res.writeHead(200, {
 95     "Content-Type": "text/event-stream",
 96     "Cache-Control": "no-cache",
 97     "Connection": "keep-alive"
 98   });
 99 
100   res.setTimeout(5000,()=>{
101     res.write(":this is test")
102   })
103   cacheUpdate[arg1.name] = res;
104 }).listen(8076);
105 
106 // 触发推送的动做
107 http.createServer(function (req, res) {
108   res.setHeader('Access-Control-Allow-Origin', '*');
109   res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Content-Length, Authorization, Accept, X-Requested-With , yourHeaderFeild');
110   res.setHeader('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');
111   res.writeHead(200,{
112     "content-type":"application/json; charset=utf-8"
113   });
114   var arr = []
115   new Promise(function(x,xx){
116     req.on("data",function(data){
117       arr.push(data);
118     });
119 
120     req.on("end",function(){
121       var data= Buffer.concat(arr).toString(),ret;
122       try{
123         ret = JSON.parse(data);
124         x(ret)
125       }catch(err){}
126     })
127   }).then(function (req) {
128     var data = {
129       retry: 10000,
130       name: req.name,
131       data: req.message,
132       event:req.event
133     }
134 
135     cacheUpdate[data.name].write("retry: " + data.retry + "\n");
136     cacheUpdate[data.name].write("event: " + data.event + "\n");
137     cacheUpdate[data.name].write("data: " + data.data + "\n\n");
138 
139   })
140   res.end()
141 }).listen(8077)
node服务器测试代码

 

最重要的环节开始了,下面就是个人show time。json

1.  聊天系统的demo

  介绍:既然服务器有了主动推送给客户端的能力,那么最重要的基础场景就是交流。因此这个聊天系统demo应运而生

  使用的类库:ajax类库+sse类库(这里的ajax随便你们用什么通讯类库,我这里使用的是我本身第一阶段研究的类库)

  demo查看:https://github.com/GerryIsWarrior/SSE/blob/master/js-demo/index_chat.html

 

测试(模拟了2我的,只要服务器吃的消,多少人均可以):

 

总结: 这个聊天系统的demo,建立了2个用户,用左边用户进行发送,监控右边用户的控制台的network,能够查看到通讯的请求,在左边用户每次发完消息以后,右边用户每次都接受到数据,而后处理到聊天系统界面。其实sse后端底层也相似于长链接,只是多了一个服务器能够反向推的动做。

 

2.   线上客户端缓存的更新demo

  介绍:由于前端浏览器的机制,将全部用的数据请求过来,没有请求的页面也能跑起来。因此对于作前端优化的同窗就明白一件事,但愿用户能更多的缓存命中咱们存储在用户浏览器中的一些数据,这样对于第一次加载以后,若是一些缓存数据没动的data,直接取出来,渲染到用户浏览器中。这样给用户的感受就是秒开的感受(你们能够参考京东网站的首页优化,首屏啊,二次打开等等)

  使用类库:SSE.js+原生js+postMan去触发推送

  demo查看:https://github.com/GerryIsWarrior/SSE/blob/master/js-demo/index_cache.html

 

测试(模拟localStory缓存用户数据更新)

 

总结:测试页面,首先我在代码中固定写了一个dom的我是基础缓存的数据,存到localStory中。而后对于这个打开咱们的网页在线上的用户,我使用了postman往个人借口里发送数据,触发推送的动做,好比个人demo中就是发送了一个‘我试更新过的混存数据’,而后推送到客户端,而后客户端接收到这个event,而后更新缓存。这样就完成了线上缓存更新的方式。这样避免了让用户再刷一次页面(从新请求页面数据,比对缓存版本啥的),毕竟用户,才是咱们的上帝。

 

3.   线上代码热修复以及生产版本更新提示用户从新拉取页面

  介绍:对于如今更多的spa来讲,浏览器下载结束了代码,你使用的代码就是浏览器下载的,若是这块前端代码是有问题的,那么生产跑得代码都是有问题代码,除非用户手动刷新页面,从新请求一个正确的生产代码。

  demo查看:https://github.com/GerryIsWarrior/SSE/blob/master/js-demo/index_hotRepair.html

 

测试(模拟一个线上案例):

 

总结:这个案例就是这样,以前咱们写死了线上代码要打8折,因此咱们最后计算金额的时候就是按照8折去计算的。可是产品过来讲,不该该这样啊,这TM不是打9折吗?这样公司会亏损严重的。而后你们都蒙蔽,修正发布。而后发现用户的页面仍是8折,用户没刷新。尴尬.....

so,经过正在线上的推送,来进行代码的热修复。好比我推送了一个9折的信息,而后客户端接受处理了,哪怕正在线上的用户都按正确的9折来计算。挽回了公司的损失。

PS:若是线上改动太多太多了,很差进行局部热修复方案,只能让正在用的用户强制更新,拉新数据。

就像上图同样,用户点肯定就从新刷新页面,拉取新代码。

 

浏览器的测试兼容结果:chrome,safair,opera,firefox均可以跑

 

 

结语:这是sse技术研究的第二阶段,能够在非IE系列的浏览器上都跑起来了,并且针对不一样场景的操做和优化都有不少的想法。每一个想法真正写成一个成熟的东西,都能让前端变得更好。第三阶段,暂时定为SSE的收尾阶段,解决一些兼容性问题和之后用户提出的新问题。其实,搞前端这么久了,我感受针对每一个技术自己,都不是什么很牛逼和好玩的技术,可是把许多单个的技术融合起来,这样就会发现新的大陆同样,能创造奇迹。

github地址: https://github.com/GerryIsWarrior/SSE   若是感受好,或者能解决你的问题,能够点个star

 

最近感悟最深的一句话:我可能推进不了前端技术的进步,可是我可让前端技术变得更美好.....共勉

相关文章
相关标签/搜索