the5fire的技术博客

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


使用EventSource实现页面消息推送

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

前段时间在考虑怎么把我们的自动部署工具 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

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

怎么使用EventSource

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

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

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

<html><head><title>EventSource-Dango-Demo by the5fire</title><script>varsource=newEventSource("/eventsource/");source.onmessage=function(event){document.getElementById("result").innerHTML+=event.data+"<br />";};</script></head><body><divid="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这个文件, 改成如下代码:

fromdjango.conf.urlsimportpatternsfromdjango.views.genericimportTemplateViewurlpatterns=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-8importtimefromdjango.httpimportStreamingHttpResponsefromdjango.utils.timezoneimportnowdefeventsource(request):response=StreamingHttpResponse(stream_generator(),content_type="text/event-stream")response['Cache-Control']='no-cache'returnresponsedefstream_generator():whileTrue:# 发送事件数据# yield 'event: date\ndata: %s\n\n' % str(now())# 发送数据yieldu'data: %s\n\n'%str(now())time.sleep(2)

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

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

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

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

fromdjango.conf.urlsimportpatternsfromdjango.views.genericimportTemplateViewfrom.viewsimporteventsourceurlpatterns=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程序员杂谈

最后两周预售价


其他分类: