the5fire

关注Python、Django、Vim、Linux、Web开发、团队管理和互联网--Life is short, we need Python.


使用EventSource实现页面消息推送

作者:the5fire | 标签:     | 发布:2014-10-11 7:29 a.m. | 阅读量: 32938, 31380

前段时间在考虑怎么把我们的自动部署工具 essay <https://github.com/SohuTech/essay>_ 包一层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 <http://www.w3.org/TR/eventsource/>_

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

怎么使用EventSource

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

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

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

<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这个文件, 改成如下代码:

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代码如下:

# 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需要配置下:

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来实现兼容。

资源

- from the5fire.com
----EOF-----

微信公众号:Python程序员杂谈


其他分类: