исключение gevent.hub.LoopExit на примере SSE фляги - PullRequest
0 голосов
/ 27 мая 2018

Я пытаюсь запустить следующий пример, который я скопировал с 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

Я запустил сервер с env FLASK_APP=stream.py flask run, и сервер работает нормально.Но когда я пытаюсь подключиться к серверу с помощью веб-браузера, как описано выше, я получаю следующую ошибку:

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>)

Что пошло не так?Нужно ли заранее объявить подпрограмму publish как зеленую?Чего мне не хватает (и фрагмента pocoo)?

1 Ответ

0 голосов
/ 28 мая 2018

Я нашел решение, и оно связано с веб-сервером, используемым для обслуживания приведенного выше фрагмента.Сервер, предоставленный flask run, не работает.На данный момент единственным сервером, который может обслуживать вышеуказанное приложение, является gunicorn следующим образом:

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

Это прекрасно работает!(К сожалению, это решает только половину моей проблемы, так как я хотел запустить свой сервер в Windows, на которой там не поддерживается gunicorn.)

...