我开始查看服务器发送的事件,并有兴趣尝试使用我的首选工具,Python,Flask和Twisted。 我问是否正在按照我正在做的方式睡觉是好的,与gevent的greenlet.sleep做的方式相比,这是我非常简单的代码并且“移植”到Twisted(来自gevent):
#!/usr/bin/env python
import random
from twisted.web.server import Site
from twisted.web.wsgi import WSGIResource
from twisted.internet import reactor
import time
from flask import Flask, request, Response
app = Flask(__name__)
def event_stream():
count = 0
while True:
count += 1
yield 'data: %c (%d)\n\n' % (random.choice('abcde'), count)
time.sleep(1)
@app.route('/my_event_source')
def sse_request():
return Response(
event_stream(),
mimetype='text/event-stream')
@app.route('/')
def page():
return '''
<!DOCTYPE html>
<html>
<head>
<script type="text/javascript" src="//code.jquery.com/jquery-1.8.0.min.js"></script>
<script type="text/javascript">
$(document).ready(
function() {
sse = new EventSource('/my_event_source');
sse.onmessage = function(message) {
console.log('A message has arrived!');
$('#output').append('<li>'+message.data+'</li>');
}
})
</script>
</head>
<body>
<h2>Demo</h2>
<ul id="output"></ul>
</body>
</html>
'''
if __name__ == '__main__':
resource = WSGIResource(reactor, reactor.getThreadPool(), app)
site = Site(resource)
reactor.listenTCP(8001, site)
reactor.run()
尽管time.sleep是一个阻塞函数,但它不会阻止Twisted反应器,这应该通过多个不同的浏览器可以访问页面并正确接收事件来证明:如果需要使用不同的浏览器,正如Chromium所做的那样,具有相同URI的多个不同请求将排队,因为这是一个流响应,所以浏览器队列将一直忙,直到套接字或请求关闭。 你有什么勇气?有更好的方法吗?关于Twisted和Flask周围没有太多示例代码。
答案 0 :(得分:4)
您的示例仅使用扭曲为wsgi container。除了任何其他基于线程的wsgi容器,它允许您使用time.sleep(1)
。
允许扭曲直接处理/my_event_source
可能是有益的。以下是使用twisted:
def cycle(echo):
# Every second, sent a "ping" event.
timestr = datetime.utcnow().isoformat()+"Z"
echo("event: ping\n")
echo('data: ' + json.dumps(dict(time=timestr)))
echo("\n\n")
# Send a simple message at random intervals.
if random.random() < 0.1:
echo("data: This is a message at time {}\n\n".format(timestr))
class SSEResource(resource.Resource):
def render_GET(self, request):
request.setHeader("Content-Type", "text/event-stream")
lc = task.LoopingCall(cycle, request.write)
lc.start(1) # repeat every second
request.notifyFinish().addBoth(lambda _: lc.stop())
return server.NOT_DONE_YET
客户static/index.html
来自the same source:
<!doctype html>
<title>Using server-sent events</title>
<ol id="eventlist">nothing sent yet.</ol>
<script>
if (!!window.EventSource) {
var eventList = document.getElementById("eventlist");
var source = new EventSource('/my_event_source');
source.onmessage = function(e) {
var newElement = document.createElement("li");
newElement.innerHTML = "message: " + e.data;
eventList.appendChild(newElement);
}
source.addEventListener("ping", function(e) {
var newElement = document.createElement("li");
var obj = JSON.parse(e.data);
newElement.innerHTML = "ping at " + obj.time;
eventList.appendChild(newElement);
}, false);
source.onerror = function(e) {
alert("EventSource failed.");
source.close();
};
}
</script>
您可以将它与您的wsgi应用程序结合使用:
app = Flask(__name__)
@app.route('/')
def index():
return redirect(url_for('static', filename='index.html'))
if __name__ == "__main__":
root = resource.Resource()
root.putChild('', wsgi.WSGIResource(reactor, reactor.getThreadPool(), app))
root.putChild('static', static.File("./static"))
root.putChild('my_event_source', SSEResource())
reactor.listenTCP(8001, server.Site(root))
reactor.run()
WSGIResource
期望处理所有网址,因此需要重写路由代码以支持多个烧瓶处理程序。