简体   繁体   English

flask SSE示例上的gevent.hub.LoopExit异常

[英]gevent.hub.LoopExit exception on flask SSE example

I'm trying to run the following example that I copied from http://flask.pocoo.org/snippets/116/ : 我正在尝试运行从http://flask.pocoo.org/snippets/116/复制的以下示例:

# author: oskar.blom@gmail.com
#
# Make sure your gevent version is >= 1.0
import gevent
from gevent.wsgi import WSGIServer
from gevent.queue import Queue

from flask import Flask, Response

import time


# SSE "protocol" is described here: http://mzl.la/UPFyxY
class ServerSentEvent(object):

    def __init__(self, data):
        self.data = data
        self.event = None
        self.id = None
        self.desc_map = {
            self.data : "data",
            self.event : "event",
            self.id : "id"
        }

    def encode(self):
        if not self.data:
            return ""
        lines = ["%s: %s" % (v, k) 
                 for k, v in self.desc_map.iteritems() if k]

        return "%s\n\n" % "\n".join(lines)

app = Flask(__name__)
subscriptions = []

# Client code consumes like this.
@app.route("/")
def index():
    debug_template = """
     <html>
       <head>
       </head>
       <body>
         <h1>Server sent events</h1>
         <div id="event"></div>
         <script type="text/javascript">

         var eventOutputContainer = document.getElementById("event");
         var evtSrc = new EventSource("/subscribe");

         evtSrc.onmessage = function(e) {
             console.log(e.data);
             eventOutputContainer.innerHTML = e.data;
         };

         </script>
       </body>
     </html>
    """
    return(debug_template)

@app.route("/debug")
def debug():
    return "Currently %d subscriptions" % len(subscriptions)

@app.route("/publish")
def publish():
    #Dummy data - pick up from request for real data
    def notify():
        msg = str(time.time())
        for sub in subscriptions[:]:
            sub.put(msg)

    gevent.spawn(notify)

    return "OK"

@app.route("/subscribe")
def subscribe():
    def gen():
        q = Queue()
        subscriptions.append(q)
        try:
            while True:
                result = q.get()
                ev = ServerSentEvent(str(result))
                yield ev.encode()
        except GeneratorExit: # Or maybe use flask signals
            subscriptions.remove(q)

    return Response(gen(), mimetype="text/event-stream")

if __name__ == "__main__":
    app.debug = True
    server = WSGIServer(("", 5000), app)
    server.serve_forever()
    # Then visit http://localhost:5000 to subscribe 
    # and send messages by visiting http://localhost:5000/publish

I ran the server with env FLASK_APP=stream.py flask run and the server starts fine. 我运行了env FLASK_APP=stream.py flask run服务器,服务器正常启动。 But when I try to connect to the server with a web browser as described above, I get the following error: 但是,当我如上所述尝试使用Web浏览器连接到服务器时,出现以下错误:

127.0.0.1 - - [27/May/2018 21:37:49] "GET / HTTP/1.1" 200 -
127.0.0.1 - - [27/May/2018 21:37:49] "GET /subscribe HTTP/1.1" 500 -
Error on request:
Traceback (most recent call last):
  File "/usr/lib/python2.7/site-packages/werkzeug/serving.py", line 209, in run_wsgi
    execute(self.server.app)
  File "/usr/lib/python2.7/site-packages/werkzeug/serving.py", line 199, in execute
    for data in application_iter:
  File "/usr/lib/python2.7/site-packages/werkzeug/wsgi.py", line 704, in __next__
    return self._next()
  File "/usr/lib/python2.7/site-packages/werkzeug/wrappers.py", line 81, in _iter_encoded
    for item in iterable:
  File "/home/dov/git/learning/flask/stream/stream.py", line 88, in gen
    result = q.get()
  File "/usr/lib64/python2.7/site-packages/gevent/queue.py", line 283, in get
    return self.__get_or_peek(self._get, block, timeout)
  File "/usr/lib64/python2.7/site-packages/gevent/queue.py", line 260, in __get_or_peek
    result = waiter.get()
  File "/usr/lib64/python2.7/site-packages/gevent/hub.py", line 898, in get
    return self.hub.switch()
  File "/usr/lib64/python2.7/site-packages/gevent/hub.py", line 630, in switch
    return RawGreenlet.switch(self)
LoopExit: ('This operation would block forever', <Hub at 0x7f434fb2daf0 epoll default pending=0>)

What went wrong? 什么地方出了错? Does the publish routine need to be declared as a greenlet in advance? 是否需要提前将publish例程声明为greenlet? What am I (and the pocoo snippet) missing? 我缺少什么(以及pocoo代码段)?

I found a solution, and it is related to the web server used for serving the above snippet. 我找到了一个解决方案,它与用于提供上述代码段的Web服务器有关。 The server supplied by flask run doesn't work. flask run提供的服务器不起作用。 So far the only server that I have found that can serve the above application is gunicorn as follows: 到目前为止,我发现可以为上述应用程序提供服务的唯一服务器是gunicorn,如下所示:

gunicorn sse:app --worker-class gevent --bind 127.0.0.1:5000

This works great! 这很棒! (Unfortunately it only solves half my problem, as I wanted to run my server on Windows, on which there gunicorn is not supported.) (不幸的是,它只能解决一半的问题,因为我想在Windows上运行服务器,但不支持gunicorn。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM