1

服务器推送以及sse实战

 1 year ago
source link: http://fengliner.github.io/2016/09/02/%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%8E%A8%E9%80%81sse%E5%AE%9E%E6%88%98/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

最近在做crm系统,有一个需求是外呼系统收到来电通知时要给客户端(浏览器)实时推送一条信息,告诉客户端有来电了,也就是弹框显示来电电话号码。由于不是客户端主动请求服务端数据,所以就需要一个方案来解决服务端主动推送数据给客户端的问题。

服务器推送数据也算是一类问题了,目前的解决方法也有不少,主要可以分成两类。这两类方法的区别在于是否基于HTTP协议实现,不使用HTTP协议的做法是实用HTML5新增的WebSocket规范,而使用HTTP协议规范的做法则包括简易轮询、COMET技术和本文要介绍的HTML5服务器推送事件(Server-sent Events)。

服务端推送方案简介

  • WebSocket:WebSocket规范是HTML5中的一个重要组成部分,已经被很多主流浏览器所支持,也有不少基于WebSocket开发的应用。正如名称所表示的一样,WebSocket使用的是套接字连接,基于TCP协议。使用WebSocket之后,实际上在服务端和客户端之间建立了一个套接字连接,可以进行双向的数据传输。WebSocket的功能很强大,使用起来也灵活,可以适用不同的场景。不过WebSocket技术也比较复杂,包括服务器和浏览器端的实现都不同于一般的Web应用。

  • 简易轮询:浏览器定时向服务器端发出请求,来查询数据是否有更新。这种做法简单,在一定程度上可以解决问题。不过对于轮询的时间间隔需要进行仔细考虑。轮询的间隔过长会导致用户不能及时接收到更新的数据;轮询的间隔过短,会导致查询请求过多,增加服务端的负担。

  • COMET:改进了简易轮询的缺点,使用的是长轮询。长轮询的基本思想是在每次客户端发出请求后,服务器检查上次返回的数据与此次请求时的数据之间是否有更新,如果有更新则返回新数据并结束此次连接,否则服务器“hold”住此次连接,直到有新数据时再返回相应。而这种长时间的保持连接可以通过设置一个较大的HTTP timeout实现。这样做的好处是在连接处于打开状态的时间段内,服务端产生的数据更新可以被及时的返回给浏览器,当上一个长连接关闭之后,浏览器会打开一个新的长连接来继续请求。不过COMET技术的实现需要在服务端和浏览器端都需要第三方库的支持。

  • SSE:当使用服务器推送事件(SSE)进行通信的时候,服务器在应用需要数据的时候,并不需要初始化请求就能把数据推送给应用。换句话说,当更新需要进行的时候,它就从服务器端自动流向客户端。SSE在服务端和客户端打开了一个单方向的通道。

综合比较上面提到的4种不同的技术,简易轮询由于其本身的缺陷,并不推荐使用。COMET技术并不是HTML5标准的一部分,从兼容标准的角度出发,也不推荐使用。WebSocket规范和服务器推送技术都是HTML5标准的组成部分,在主流浏览器上都提供了原生的支持,是推荐使用的。不过WebSocket规范更加复杂一些,适用于需要进行复杂双向数据通讯的场景。对于简单的服务器数据推送的场景,使用服务器推送事件就足够了。

分享一个通(ji)俗(qi)易(dou)懂(bi)的介绍轮询和WebSocket的文章:知乎:WebSocket 是什么原理?为什么可以实现持久连接?

为什么选择SSE而不是WebSocket

SSE(服务器发送事件)总是处在不引人注目的阴暗地方的一个原因是因为像WebSocket这样的后来的应用提供了执行双向,双工通信的更丰富的协议。具有两个方向通道对哪些诸如游戏、消息应用以及需要几乎是双向实时更新的应用来说更具吸引力。然而在某些场景下,不需要从客户端发送数据。你只需要更新某些来自服务器的动作。有几个例子:朋友状态更新、股票行情更新、新闻推送或者其他自动数据推送机制。在我们的需求中引入WebSocket太重,并且只需要服务器向浏览器推数据就够了,所以选择使用服务器推送事件Server-sent Events而不是WebSocket。
业务决定需求,需求决定采用哪种技术,并不是哪种方案好或不好,在适合的场景选择最适合的技术就对了。

SSE客户端实现

<!DOCTYPE html>
<html>
<body>
<div>
server-sent events
</div>
<script type="text/javascript">
var source = new EventSource('http://www.example.com/api/message');
source.onmessage = function(e){
console.log(e.data);
};
</script>
</body>
</html>

SSE服务端实现(Node.js)

var http = require("http");

http.createServer(function (req, res) {

var fileName = "." + req.url;

if (fileName === "./stream") {
res.writeHead(200, {
"Content-Type":"text/event-stream",
"Cache-Control":"no-cache",
"Connection":"keep-alive"
});
res.write("retry: 10000\n");
res.write("event: connecttime\n");
res.write("data: " + (new Date()) + "\n\n");
res.write("data: " + (new Date()) + "\n\n");

interval = setInterval(function() {
res.write("data: " + (new Date()) + "\n\n");
}, 1000);

req.connection.addListener("close", function() {
clearInterval(interval);
}, false);
}
}).listen(80, "127.0.0.1");

参考链接及更多细节


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK