前段时间在考虑怎么把我们的自动部署工具 essay 包一层web的壳,这样每次发版就不用敲很多命令了,只需要点几个按钮就搞定,也可以减少发版是的心智负担。在做的时候主要的一个问题是如何更好的把本来在Terminal上输出的结果实时的输出到web界面上,最后发现了EventSource这个东西,除了IE浏览器不支持,其他浏览器都内置这个对象,可以很好得完成我的这个需求。
确切的说应该问什么是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的作用,那么怎么使用呢?
从这里 HTML 5 服务器发送事件 我们可以简单看到它的用法。这里我在用Django来演示一下。具体Django使用就不多说了,我使用Django的版本为1.6.7。
首先先来写页面,简陋的写就可以:
EventSource-Dango-Demo by the5fire id="result">
页面加载时会执行上面的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: 时间nn' 这时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来实现兼容。