前段时间在考虑怎么把我们的自动部署工具 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来实现兼容。
资源
- 我的这个Demo源码:https://github.com/the5fire/practice_demo/tree/master/learn_eventsource/
- https://github.com/niwibe/django-sse
- http://www.w3.org/TR/eventsource/
- http://www.w3school.com.cn/html5/html_5_serversentevents.asp
微信公众号:Python程序员杂谈