构建实时web应用程序有点挑战,我们需要考虑如何将数据从服务器发送到客户端。能够“主动”实现这一功能的技术已经存在了很长时间,并且仅限于两种通用方法:客户端请求或服务器请求。
实现这些的几种方法:
长/短轮询(客户端拉动)websockets(服务器推送)服务器发送的事件(服务器推送)客户端拉取-客户端以一定的定期间隔向服务器请求更新服务器推送-服务器正在主动将更新推送到客户端(客户端拉取的反向操作)
让我们以一个简单的用例来比较以上技术,然后选择合适的技术。
范例:
我们的示例用例非常简单。我们需要开发一个仪表板web应用程序,该应用程序可以流转来自(github / twitter / .. etc)等网站的活动列表。这个应用程序的目的是从上面列出的各种方法中选择合适的一种。
1.使用轮询:轮询是一种技术,客户端通过该技术定期向服务器请求新数据。我们可以通过两种方式进行轮询:短轮询和长轮询。简单来说,短轮询是基于ajax的计时器,它以固定的延迟进行调用,而长轮询则基于comet(即,当服务器事件发生时,服务器将无延迟地将数据发送到客户端)。两者都有优点和缺点,并根据用例进行调整。有关深入的详细信息,请阅读stackoverflow社区给出的答案。
让我们看看一个简单的客户端长轮询代码段的外观:
/* client - subscribing to the github events */
subscribe: (callback) => {
const polluserevents = () => {
$.ajax({
method: 'get',
url: 'http://localhost:8080/githubevents',
success: (data) => {
callback(data) // process the data
},
complete: () => {
polluserevents();
},
timeout: 30000
})
}
polluserevents()
}
这基本上是一个长轮询功能,它像往常一样第一次运行,但是它设置了三十(30)秒的超时,并且在每次对服务器进行async ajax调用之后,回调都会再次调用ajax。
ajax调用可在http协议上运行,这意味着默认情况下,对同一域的请求应进行多路复用。我们发现这种方法存在一些陷阱。
多路复用(轮询响应实际上无法同步)
轮询需要3次往返(tcp sin,ssl和数据)
超时(如果连接保持空闲时间太长,代理服务器将关闭连接)
您可以在这里阅读更多关于现实世界的挑战。
2.使用websockets:websocket只是客户端和服务器之间的持久连接。这是一种通过单个tcp连接提供全双工通信通道的通信协议。
rfc 6455声明websocket“旨在在http端口80和443上工作,并支持http代理和中介”,从而使其与http协议兼容。为了实现兼容性,websocket握手使用http升级标头将http协议更改为websocket协议。http和websocket都位于osi模型的应用程序层,因此依赖于第4层的tcp。
有一个mdn文档详细解释了websocket,我也建议您阅读它。
让我们看看一个非常简单的websocket客户端实现的样子:
$(function () {
// if user is running mozilla then use it's built-in websocket
window.websocket = window.websocket || window.mozwebsocket;
const connection = new websocket('ws://localhost:8080/githubevents');
connection.onopen = function () {
// connection is opened and ready to use
};
connection.onerror = function (error) {
// an error occurred when sending/receiving data
};
connection.onmessage = function (message) {
// try to decode json (i assume that each message
// from server is json)
try {
const githubevent = json.parse(message.data); // display to the user appropriately
} catch (e) {
console.log('this doesn't look like a valid json: '+ message.data);
return;
}
// handle incoming message
};
});
如果服务器支持websocket协议,它将同意升级,并将通过响应中的upgrade标头传达此信息。
让我们看看如何在node.js(服务器)中实现:
const express = require('express');
const events = require('./events');
const path = require('path');
const app = express();
const port = process.env.port || 5001;
const expressws = require('express-ws')(app);
app.get('/', function(req, res) {
res.sendfile(path.join(__dirname + '/static/index.html'));
});
app.ws('/', function(ws, req) {
const githubevent = {}; // sample github event from github event api https://api.github.com/events
ws.send('message', githubevent);
});
app.listen(port, function() {
console.log('listening on', port);
});
一旦我们从github事件api获得数据,就可以在建立连接后将其流式传输到客户端。对于我们的场景,这种方法也有一些陷阱。
使用websockets,我们需要自己处理许多由http处理的问题。websocket是用于传输数据的另一种协议,它不会通过http / 2连接自动多路复用。在服务器和客户端上实现自定义多路复用有点复杂。websocket是基于帧的,而不是基于流的。当我们打开网络标签。您可以看到websocket消息在frame中列出。
有关websocket的详细信息,请查看这篇很棒的文章,在这里您可以阅读有关碎片以及如何在后台进行处理的更多信息。
3.使用sse:sse是一种机制,一旦建立了客户端-服务器连接,服务器就可以将数据异步推送到客户端。然后,只要有新的“大块”数据可用,服务器就可以决定发送数据。可以将其视为单向发布-订阅模型。
它还提供了一个标准的javascript客户端api,称为eventsource,已在大多数现代浏览器中实现,作为w3c的html5标准的一部分。 polyfills可用于不支持eventsource api的浏览器。
我们可以看到edge和opera mini落后于此实现,对于sse而言,最重要的案例是针对移动浏览器设备,因为这些浏览器没有可行的市场份额。yaffle是事件源的众所周知的pollyfill。
由于sse是基于http的,因此它很自然地与http / 2相适应,并且可以结合使用以实现两者的最佳选择:http / 2处理基于多路复用流的有效传输层,而sse为应用程序提供api以实现 推。因此,开箱即用地通过http / 2实现多路复用。连接断开时会通知客户端和服务器。通过使用消息维护唯一的id,服务器可以看到客户端错过了n条消息,并在重新连接时发送了未完成消息的积压。
让我们看看示例客户端实现的外观:
const evtsource = new eventsource('/events');
evtsource.addeventlistener('event', function(evt) {
const data = json.parse(evt.data);
// use data here
},false);
此代码段非常简单。它连接到我们的源并等待接收消息。现在,示例nodejs服务器将如下所示。
// events.js
const eventemitter = require('eventemitter3');
const emitter = new eventemitter();
function subscribe(req, res) {
res.writehead(200, {
'content-type': 'text/event-stream',
'cache-control': 'no-cache',
connection: 'keep-alive'
});
// heartbeat
const nln = function() {
res.write('\\n');
};
const hbt = setinterval(nln, 15000);
const onevent = function(data) {
res.write('retry: 500\\n');
res.write(event: event\\n);
res.write(data: ${json.stringify(data)}\\n\\n);
};
emitter.on('event', onevent);
// clear heartbeat and listener
req.on('close', function() {
clearinterval(hbt);
emitter.removelistener('event', onevent);
});
}
function publish(eventdata) {
// emit events here recieved from github/twitter apis
emitter.emit('event', eventdata);
}
module.exports = {
subscribe, // sending event data to the clients
publish // emiting events from streaming servers
};
// app.js
const express = require('express');
const events = require('./events');
const port = process.env.port || 5001;
const app = express();
app.get('/events', cors(), events.subscribe);
app.listen(port, function() {
console.log('listening on', port);
});
我们从这种方法中获得的主要好处是:
实施更简单,数据效率更高开箱即用地通过http / 2自动多路复用将客户端上数据的连接数限制为一个如何在sse,websocket和polling中进行选择?
经过漫长而详尽的客户端和服务器实施之后,sse似乎是我们解决数据交付问题的最终答案。也有一些问题,但是可以解决。
可以利用服务器发送事件的应用程序的一些简单示例:
实时股价流图重要事件的实时新闻报道(发布链接,推文和图片)由twitter的流api提供的实时github / twitter仪表板墙监视服务器统计信息(如正常运行时间,运行状况和正在运行的进程)的监视器。但是,sse不仅是其他提供快速更新的方法的可行替代方案。在某些特定情况下,例如在sse被证明是理想解决方案的情况下,每个人都可以胜过其他人。考虑一个像mmo(大型多人在线)游戏这样的场景,该场景需要来自连接两端的大量消息。在这种情况下,websockets将压制sse。
如果您的用例需要显示实时的市场新闻,市场数据,聊天应用程序等,例如在我们的案例中,依靠http / 2 + sse将为您提供有效的双向通信渠道,同时又能获得留在其中的好处http世界。
红外接收二极管接收光的多少和输出信号的关系?
如何计算光耦合器中的LED寿命性能
阻碍工业机器人产业发展的原因
紫外光在缺陷检测中的应用
解决汽车行业风险,进一步确保电子系统的安全性
轮询、SSE 和WebSocket哪个更好
工信部发布下架侵害用户权益 APP 名单,涉红人直播等
诺基亚8或首发骁龙660!但蔡司镜头呢?
出光兴产将在成都设厂_如何布局
LED光源模组原来可以这么分类
聚云位智、农业大学共建实验室 大数据助力农业再创发展
如何改善散热结构以提高白光LED的使用寿命
【权威认证】飞凌嵌入式FET113i-S核心板国产化率达100%
基于GPS和GPRS技术实现公交智能调度系统的应用方案
AsiaB2B
BH66/67F HT67体温量测额温枪的详细说明
欧盟委员会将在下一代超级计算机上投资80亿欧元?
Dialog半导体公司在IoTMark™-Wi-Fi基准测试中达到行业最高排名
因芯片供应问题,西雅特已决定削减马托雷尔工厂的产量
谷歌AI道德委员会已解散 如何让人工智能明辨是非?