使用 EventSource 实现页面消息推送

Posted 前端熟练工

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用 EventSource 实现页面消息推送相关的知识,希望对你有一定的参考价值。

前段时间在考虑怎么把我们的自动部署工具 essay https://github.com/SohuTech/e..._ 包一层web的壳,这样每次发版就不用敲很多命令了,只需要点几个按钮就搞定,也可以减少发版是的心智负担。在做的时候主要的一个问题是如何更好的把本来在Terminal上输出的结果实时的输出到web界面上,最后发现了EventSource这个东西,除了IE浏览器不支持,其他浏览器都内置这个对象,可以很好得完成我的这个需求。

什么是EventSource

确切的说应该问什么是Server-Sent Events(简称SSE)?Wikipedia的介绍是这样的::

Server-sent events (SSE) is a technology where a browser receives automatic updates from a server via HTTP connection. The Server-Sent Events EventSource API is standardized as part of html5[1] by the W3C.

大致翻译下就是:SSE是一种能让浏览器通过HTTP连接自动收到服务器端更新的技术,SSE EventSource 接口被W3C制定为HTML5的一部分。
W3C部分的链接可以看这个: EventSource

这个技术的作用是可以完成从服务器端到客户端(浏览器)单向的消息传送。因此我们可以用这个来做推送。不过需要注意的是,IE并不支持该技术。

怎么使用EventSource

在上面我们知道了EventSource的作用,那么怎么使用呢?

从这里 HTML 5 服务器发送事件 http://www.w3school.com.cn/ht..._ 我们可以简单看到它的用法。这里我在用Django来演示一下。具体Django使用就不多说了,我使用Django的版本为1.6.7。

首先先来写页面,简陋的写就可以:
.. code:: html

<html>
<head>
<title>EventSource-Dango-Demo by the5fire</title>
<script>
    var source=new EventSource("/eventsource/");
    source.onmessage=function(event)
    {
        document.getElementById("result").innerHTML+=event.data + "<br />";
    };
</script>
</head>
<body>
<div id="result">
</div>
</body>
</html>

页面加载时会执行上面的js脚本,脚本会初始化一个EventSrouce,监听在 /eventsource/ 这个URI上,然后设置souce对象收到消息之后怎么处理。

页面很简单,下面来看Django代码。通过 django-admin.py startproject eventsource_django 创建一个django项目。结构如下::

.
└── eventsource_django

├── eventsource_django
│   ├── __init__.py
│   ├── settings.py
│   ├── templates
│   │   └── index.html
│   ├── urls.py
│   └── wsgi.py
└── manage.py

3 directories, 6 files
把上面的html代码放到index.html中,然后打开urls.py这个文件, 改成如下代码:

.. code:: python

from django.conf.urls import patterns

from django.views.generic import TemplateView


urlpatterns = patterns(
    \'\',
    (r\'^$\', TemplateView.as_view(template_name="index.html")),
)

这样启动django server之后,首页就是咱们写的代码了。这时你启动项目: python manage.py runserver ,浏览器访问localhost:8000会发现terminal上会接受到一个 /eventsource/ 的请求,结果是404,现在来处理这个请求,来展示下服务器端怎么就能发送消息到浏览器上。

先来创建一个views在eventsource_django下,和urls.py同级目录。(这时演示的写法,正式项目不会这么写的)

views.py代码如下:

.. code:: python

# coding:utf-8

import time

from django.http import StreamingHttpResponse
from django.utils.timezone import now


def eventsource(request):
    response = StreamingHttpResponse(stream_generator(), content_type="text/event-stream")
    response[\'Cache-Control\'] = \'no-cache\'
    return response


def stream_generator():
    while True:
        # 发送事件数据
        # yield \'event: date\\ndata: %s\\n\\n\' % str(now())

        # 发送数据
        yield u\'data: %s\\n\\n\' % str(now())
        time.sleep(2)

里面的StreamingHttpResponse可以简单理解为一个流式的response, 它的内容参数需要是一个生成器,所以下面用yield实现了一个生成器,每个两秒返回 \'data: 时间\\n\\n\' 这时Source-Send Event的一种规范,另外他还可以设置事件类型,如我注释掉的那个代码。对应事件的处理方式和简单的message不同。js得这么写::

source = EventSource(\'/eventsource/\')
source.addEventListener("date", function (event) {
    console.log(event.data);
});

通过这种方式可以过滤你需要的事件。

写完views.py的代码之后,urls.py需要配置下:

.. code:: python

from django.conf.urls import patterns

from django.views.generic import TemplateView

from .views import eventsource


urlpatterns = patterns(
    \'\',
    (r\'^eventsource/$\', eventsource),
    (r\'^$\', TemplateView.as_view(template_name="index.html")),
)

然后再次运行程序,刷新页面,你就能看到页面不断的输出时间了。这个逻辑跑通之后,试想一下,如果在yield的地方不是直接给个字符串,然后从一个队列中取出数据,那不就可以实现页面的消息通知了吗?

总结

时间也不早了,简单总结下。这个技术相对于Websocket简单很多,但是SSE只是从服务器端往客户端单向传输数据,因此和websocket场景的应用场景还有些差别。

SSE使用起来也非常简单,比如我们的这个场景,把Terminal的输出重定向到web界面上。

虽然IE本身不支持,但是可以通过EventSource.js来实现兼容。

资源

我的这个Demo源码:https://github.com/the5fire/p...
https://github.com/niwibe/dja...
http://www.w3.org/TR/eventsou...
http://www.w3school.com.cn/ht...

以上是关于使用 EventSource 实现页面消息推送的主要内容,如果未能解决你的问题,请参考以下文章

利用WebSocket和EventSource实现服务端推送

服务器推送的实现—基于EventSource

基于 SSE 实现服务端消息主动推送解决方案

基于 SSE 实现服务端消息主动推送解决方案

python web服务端主动推送消息到浏览器页面的具体实现代码

H5之EventSource推送