记录--服务端推送到Web前端有哪几种方式?
这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助
这个问题?
这个问题一般会出现在面试题里面,然后回答一些诸如轮询、WebSocket之类的答案。当然,实际开发中,也会遇到类似别人给你赞了,要通知给你的情况。这时服务端推送给Web前端(先局限在Web前端,毕竟其他端还有一些特殊方法)到底有多少种方法?它们到底是怎么实现的?
写个Demo看看吧,这样正好把主要(不清楚是否还有漏的)的方案都实现一遍。先看效果:
其中的代码也上传到GitHub
了,在server-push( github.com/waiter/serv… )这里。
各种方案
从上面的截图也已经可以看出,本文主要写了5种方案,那么接下来也就一个一个简单介绍一下吧。
另外,本文涉及的Demo,后端直接使用原生的Node.js
开发,没有使用Koa
、Express
之类的,也没有使用额外的库,类似socket.io
,主要是想保持最精简的状态来呈现。前端也只是在最基础的HTML上,引入了jQuery
来方便做DOM操作,也引入了Bootstrap
来快速实现统一的样式,而未再引入类似Vue
、React
之类的框架。
还有,为了触发服务端推送,这边在前端页面上加了个输入框和按钮,来将消息发送给后端,后端会缓存消息,并触发推送,后端大体代码类似:
// 缓存需要推送的信息
const datas = [];
// 各种方案触发推送时的回调
const callbacks = {}; // 注册接口回调
server.on('request', (req, res) => {
const { pathname, query } = parse(req.url, true);
// 如果发现是前端触发推送接口
if (pathname === '/api/push') {
if (query.info) {
// 缓存推送信息
datas.push(query.info);
const d = JSON.stringify([query.info]);
// 触发所有推送回调
Object.keys(callbacks).forEach(k => callbacks[k](d));
}
res.end('ok');
}
});
1. 轮询(短轮询)
这是最简单直观的方法,就是每隔一段时间发起一个请求到后端询问是否有新信息。至于为什么又叫短轮询,其是相对于后续要说的长轮询来对比的。
这样前端只要设置一个setTimeout
来定时请求就行:
// 缓存前端已经获取的最新id
let id = 0; function poll() {
$.ajax({
url: '/api/polling',
data: { id },
}).done(res => {
id += res.length;
}).always(() => {
// 10s后再次请求
setTimeout(poll, 10000);
});
} poll();
后端也是否简单,根据前端给到的id
,看看有没有新消息,有就返回,没有就返回空
const id = parseInt(query.id || '0', 10) || 0;
res.writeHead(200, { 'Content-Type': 'application/json;' });
res.end(JSON.stringify(datas.slice(id)));
这个看起来其实时性与请求频率成正相关,但是当请求频率上来了,性能浪费也就越高,毕竟可能大部分请求都是无意义的。
2. 长轮询
在翻找资料的时候,发现有些资料会直接把这个当作短轮询
,有点匪夷所思。这里的长轮询相对前面的轮询来说,算是一种优化。具体就是前端发起请求到后端,后端不直接返回,而是等待有新信息时再返回。所以这样发起的一个请求,可能需要很长的时间才能等到返回,故而叫做长轮询。
其前端代码基本和短轮询一致,只不过把请求的超时时间设置较长(比如1分钟),然后无论请求成功或失败,马上再次发起请求即可。
相对来说,后端的写法就要稍微改动一下
const id = parseInt(query.id || '0', 10) || 0;
const cbk = 'long-polling';
delete callbacks[cbk];
const data = datas.slice(id);
res.writeHead(200, { 'Content-Type': 'application/json' });
// 发起请求时,正好有新消息就返回
if (data.length) {
return res.end(JSON.stringify(data));
}
req.on('close', () => {
delete callbacks[cbk];
});
// 注册新消息回调
callbacks[cbk] = (d) => {
res.end(d);
};
这样,**相对于短轮询,少了很多无意义的请求,而且消息的实时性也非常好。**不过,当服务端有异常时,会导致长轮询短时间内不断发起请求,可能让服务端承受更大的压力,所以两次长轮询之间最好有一定间隔,或者异常检测机制。
3. SSE(Server-sent events)
Traditionally, a web page has to send a request to the server to receive new data; that is, the page requests data from the server. With server-sent events, it's possible for a server to send new data to a web page at any time, by pushing messages to the web page. These incoming messages can be treated as Events + data inside the web page.
前面提到的轮询、长轮询都是一问一答式的,一次请求,无法推送多次消息到前端。而SSE就厉害了,一次请求,N次推送。
其原理,或者说类比,个人认为可以理解为下载一个巨大的文件,文件的内容分块传给前端,每块就是一次消息推送。
听起来很厉害,先看看后端代码要怎么写
const cbk = 'sse';
delete callbacks[cbk];
res.writeHead(200, {
// 这个是核心
'Content-Type': 'text/event-stream',
'Connection': 'keep-alive',
});
// 把缓存的信息推送给前端
res.write(`data: ${JSON.stringify(datas)}\n\n`);
// 注册新消息回调
callbacks[cbk] = (d) => {
res.write(`data: ${d}\n\n`);
};
req.on('close', () => {
delete callbacks[cbk];
});
后端代码很简单,核心在于Content-Type: text/event-stream
,这要让前端知道这是SSE,还有就是传输信息的格式比较特别一点,详细的可以看 MDN( developer.mozilla.org/en-US/docs/… )
而前端有专门的EventSource
来接收,使用起来很方便
const es = new EventSource('/api/sse');
es.onmessage = (e) => {
try {
const c = JSON.parse(e.data);
} catch (err) {
console.log(err);
}
}
这样就好了,如果你打开Chrome
的开发者工具中的网络标签,你就会发现Chrome
对于SSE请求,有专门的展示标签
另外,**SSE还支持自动重连!**服务器短时间异常,恢复之后,无需额外代码,SSE就自动重连上了。不过,本人在实际工作中却没有碰到过SSE,也就在面试题中见过。
4. WebSocket
既然有了SSE,那还要WebSocket干啥啊?因为WebSocket可以一次连接,双向推送,而SSE只能从服务端推送到前端。从这个角度来看,用WebSocket来单做服务端推送,有点大材小用了。
另外,初见WebSocket,可能会对其与Socket的联系有点疑惑。Socket协议是与HTTP协议平级的,而WebSocket协议是基于HTTP协议的,不过两者在使用层面上是十分相近的。
其前端使用写法与SSE类似,十分简单,只不过请求链接为ws://
或者wss://
开头(相当于http://
和https://
)
const ws = new WebSocket('ws://localhost:3000/ws');
ws.onmessage = e => {
try {
const c = JSON.parse(e.data);
} catch (err) {
console.log(err);
}
};
而如果要用原生Node.js
来写WebSocket服务,就会麻烦一些了,一般情况都会使用类似socket.io
之类的三方库来降低实现成本。这边也就在网上摘抄了一段代码来简单实现一下,详细的可以看Github
上的Demo代码
server.on('upgrade', (req, socket) => {
const cbk = 'ws';
delete callbacks[cbk];
const acceptKey = req.headers['sec-websocket-key'];
const hash = generateAcceptValue(acceptKey);
const responseHeaders = [ 'HTTP/1.1 101 Web Socket Protocol Handshake', 'Upgrade: WebSocket', 'Connection: Upgrade', `Sec-WebSocket-Accept: ${hash}` ];
// 告知前端这是WebSocket协议
socket.write(responseHeaders.join('\r\n') + '\r\n\r\n');
// 发送数据
socket.write(constructReply(datas));
callbacks[cbk] = (d) => {
socket.write(constructReply(d));
}
socket.on('close', () => {
delete callbacks[cbk];
});
});
这个在Chrome
浏览器中,也有专门的标签页展示
不过,它没有像SSE一样有自动重连,这块需要自行实现。
一般网页实时聊天之类需要双向推送的,都会使用WebSocket来实现。
5. iFrame
这算是找资料的时候意外发现的,之前并不知道还有这样的玩法。原理类似使用iFrame加载一个巨大的网页,利用浏览器会一边加载一边解析执行返回的HTML,通过分次返回Script标签来实现消息推送。其实现类似SSE,不过看起来就比较==hack==。
前端代码很简单,只不过要注册一个回调给iframe使用
// 注册给iframe使用的方法
window.change = function(data) { };
$('body').append('<iframe src="/api/iframe"></iframe>');
而后端也很简单,有消息的时候返回script
标签即可
const cbk = 'iframe';
delete callbacks[cbk];
// 返回缓存信息
res.write(`<script>window.parent.change(${JSON.stringify(datas)});</script>`);
callbacks[cbk] = (d) => {
res.write(`<script>window.parent.change(${d});</script>`);
};
req.on('close', () => {
delete callbacks[cbk];
});
相当奇淫巧技了。不过,似乎没找到怎么判断加载异常的情况,可能需要自行加心跳来实现了。
另外,很多文章在说使用iFrame方法时,会导致浏览器显示未加载完,图标一直转的样子。但是个人认为,图标一直转是因为页面一直没有onload
,那么在页面onload
之后,再创建iFrame就应该没有这个问题了。
总结一下
上面实现了5种推送的方案,弄了一个表格简单对比一下
方案 | (准)实时 | 单次连接 | 自动重连 | 断线检测 | 双向推送 | 无跨域 |
---|---|---|---|---|---|---|
短轮询 | ||||||
长轮询 | ||||||
SSE | ||||||
WebSocket | ||||||
iFrame |
本文转载于:
https://juejin.cn/post/7113813187727720461
如果对您有所帮助,欢迎您点个关注,我会定时更新技术文档,大家一起讨论学习,一起进步。
记录--服务端推送到Web前端有哪几种方式?的更多相关文章
- mqtt协议实现 java服务端推送功能(三)项目中给多个用户推送功能
接着上一篇说,上一篇的TOPIC是写死的,然而在实际项目中要给不同用户 也就是不同的topic进行推送 所以要写活 package com.fh.controller.information.push ...
- [译]servlet3.0与non-blocking服务端推送技术
Non-blocking(NIO)Server Push and Servlet 3 在我的前一篇文章写道如何期待成熟的使用node.js.假定有一个框架,基于该框架,开发者只需要定义协议及相关的ha ...
- 升级NGINX支持HTTP/2服务端推送
内容概览 NGINX从1.13.9版本开始支持HTTP/2服务端推送,上周找时间升级了下NGINX,在博客上试验新的特性. 升级工作主要包括: 升级NGINX 修改NGINX配置 修改wordpres ...
- 一文了解服务端推送(含JS代码示例)
常用的服务端推送技术,包括轮询.长轮询.websocket.server-sent-event(SSE) 传统的HTTP请求是由客户端发送一个request,服务端返回对应response,所以当服务 ...
- Spring Boot 集成 WebSocket 实现服务端推送消息到客户端
假设有这样一个场景:服务端的资源经常在更新,客户端需要尽量及时地了解到这些更新发生后展示给用户,如果是 HTTP 1.1,通常会开启 ajax 请求询问服务端是否有更新,通过定时器反复轮询服务端响应的 ...
- Netty实现一个简单聊天系统(点对点及服务端推送)
Netty是一个基于NIO,异步的,事件驱动的网络通信框架.由于使用Java提供 的NIO包中的API开发网络服务器代码量大,复杂,难保证稳定性.netty这类的网络框架应运而生.通过使用netty框 ...
- C# 服务端推送,十步十分钟,从注册到推送成功
目标 展示 C# 服务端集成极光推送的步骤,多图少字,有图有真相. 使用极光推送, C# 服务端推送到 Demo App,Android 手机收到推送,整理为十个步骤,使用十分钟左右,完成从注册账号到 ...
- java SDK服务端推送 --极光推送(JPush)
网址:https://blog.csdn.net/duyusean/article/details/86581475 消息推送在APP应用中越来越普遍,来记录一下项目中用到的一种推送方式,对于Andr ...
- 利用WebSocket和EventSource实现服务端推送
可能有很多的同学有用 setInterval 控制 ajax 不断向服务端请求最新数据的经历(轮询)看下面的代码: setInterval(function() { $.get('/get/data- ...
- mqtt协议实现 java服务端推送功能(二)java demo测试
上一篇写了安装mosQuitto和测试,但是用cmd命令很麻烦,有没有一个可视化软件呢? 有,需要在google浏览器下载一个叫MQTTLens的插件 打开MQTTLens后界面如下: 打开conne ...
随机推荐
- ABC 311
前四题过水 E 枚举正方形的上边界所在行.对于第 \(i\) 行一个没洞的位置 \((i,j)\),我们尝试求出以它为右上角的无洞正方形个数. 结论:设以 \((i,j-1)\) 为右上角的无洞正方形 ...
- CF1907
A 模拟. B 模拟. C 若原字符串中出现次数最多的次数为 \(cnt\),答案是 \(\max(n\%2,cnt\times 2-n)\). D 二分 \(k\),然后从后往前倒,计算出到达每个线 ...
- 【CVE-2024-21626】容器逃逸漏洞修复
哈喽大家好,我是咸鱼. 好久不见,最近有一个很火的 CVE--runc 容器逃逸漏洞.年前的时候我们已经在测试环境进行了相关操作打算年后线上进行修复. 因为今天咸鱼才开工,所以文章也就拖到了现在 漏洞 ...
- error C2039: "function": 不是 "std" 的成员的解决方法
这个错误通过某度没找到合适的解决方案,故记录下来 其实如果使用 google 搜索错误的英文关键词,大概第一条就是解决问题的链接 Large number of "'function' is ...
- win32 - 使用GDI+从资源中获取图像并加载
很多时候我们习惯使用GDI+中Image类来加载本地文件,但是有时候我们需要资源中从加载png格式的图片时,却无法使用该类. 我们可以使用FindResource,LoadResource和LockR ...
- 【Android 逆向】【攻防世界】easy-so
1. apk安装到手机,随便输入点内容,提示错误 2. jadx打开apk btn.setOnClickListener(new View.OnClickListener() { // from cl ...
- flex布局-20201028
改版自阮一峰的网络日志-Flex 布局教程:语法篇 在flex容器上设置的(即父元素上设置); flex-direction属性决定主轴的方向(即项目的排列方向). flex-direction: r ...
- 小程序中用css修改svg的颜色
记一下(#^.^#) <div class="svg"> <img src="./firefox-logo.svg" class=" ...
- elasticsearch 查询索引和清理索引命令
查询 curl --silent 'http://127.0.0.1:9200/_cat/indices' 删除 curl -X DELETE "localhost:9200/wifiloc ...
- 大众点评-CAT监控平台
前言 我们禀着发现问题,解决问题的方针,针对后台诸多的服务,如何实时监控接口性能和访问频率,还要统计大盘信息?CAT作为大众点评开源的系统监控平台项目,下面就介绍一下CAT平台的搭建步骤. CAT作为 ...