2015-07-02 12 views
5

Gram w gevent i websockets. Jest to prosty serwer echo:dlaczego gevent-websocket jest synchroniczny?

from gevent.pywsgi import WSGIServer 
from geventwebsocket.handler import WebSocketHandler 
from gevent import sleep 
from datetime import datetime 
def app(environ, start_response): 
    ws = environ['wsgi.websocket'] 
    while True: 
     data = ws.receive() 
     print('{} got data "{}"'.format(
      datetime.now().strftime('%H:%M:%S'), data)) 
     sleep(5) 
     ws.send(data) 

server = WSGIServer(("", 10004), app, 
    handler_class=WebSocketHandler) 
server.serve_forever() 

a klient:

<html> 
    <body> 
     <button type="button" id="push_data">Push</button> 
    </body> 
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.11.3.js"></script> 
    <script> 
     var ws = new WebSocket("ws://localhost:10004"); 
     ws.onmessage = function(evt) { 
      console.log(evt) 
     }; 
     $('#push_data').click(function(){ 
      console.log('sending data...'); 
      ws.send('sample data'); 
     }); 
    </script> 
</html> 

powodu gevent Spodziewałem się mieć kilka greenlets obsługujących dane asynchronicznie; to znaczy, gdy kilka razy popchnąłem dane do websocket (szybko klikając na przycisk Push), spodziewałem się, że wszystkie dane zostaną zwrócone jednocześnie po 5 sekundach oczekiwania.

Jednak bez względu na to, jak szybko kliknięciu przycisku, to co mam w konsoli:

18:28:07 got data "sample data" 
18:28:12 got data "sample data" 
18:28:17 got data "sample data" 
18:28:22 got data "sample data" 
18:28:27 got data "sample data" 

dlaczego odbierać moich danych synchronicznie, zatrzymując się co 5 sekund? Jak przekształcić go w serwer asynchroniczny?

Odpowiedz

6

Zachowanie jest synchroniczne, ponieważ twój własny kod jest synchroniczny. gevent to tylko biblioteka coroutine wykorzystująca pętlę zdarzeń. To nie magicznie zmienia kod synchroniczny w kod asynchroniczny.

Proszę przyjrzeć się dokumentacji pod adresem: http://www.gevent.org/servers.html

Mówi się, że serwery tarło jeden Greenlet na połączenie (nie na życzenie). Wykonanie wielu żądań dla tego samego połączenia jest dlatego szeregowane.

Jeśli chcesz jednocześnie obsłużyć wiele żądań dla tego samego połączenia, musisz odradzić nowe zielone tablice lub przekazać przetwarzanie do puli zielonych.

Oto przykład (tarła Greenlet na każde żądanie)

import gevent 
from gevent.pywsgi import WSGIServer 
from gevent.lock import Semaphore 
from geventwebsocket.handler import WebSocketHandler 
from datetime import datetime 

def process(ws,data,sem): 
    print('{} got data "{}"'.format(datetime.now().strftime('%H:%M:%S'), data)) 
    gevent.sleep(5) 
    with sem: 
     ws.send(data) 

def app(environ, start_response): 
    ws = environ['wsgi.websocket'] 
    sem = Semaphore() 
    while True: 
     data = ws.receive() 
     gevent.spawn(process,ws,data,sem) 

server = WSGIServer(("", 10004), app,handler_class=WebSocketHandler) 
server.serve_forever() 

zauważyć obecność semafora. Ponieważ przetwarzanie jest równoczesne, konieczne jest zapobieganie jednoczesnemu zapisywaniu w tym samym czasie dwóch współbieżnych tablic w gnieździe, powodując uszkodzenie komunikatów.

Ostatnia kwestia, z tą implementacją, nie ma gwarancji, że odpowiedzi będą wysyłane w kolejności żądań.

+1

Czy łatanie małp z geventami nie "magicznie przekształca kod synchroniczny w kod asynchroniczny"? – FullStack

+2

Tylko jeśli masz wiele zielonych i połączeń. Jeśli kod ma do czynienia z pojedynczymi połączeniami z pojedynczej greenletu, mechanizm coroutine gevent nie pomoże.Innymi słowy, magia dzieje się tylko wtedy, gdy w nią wierzysz, i odpowiednio ustrukturyzuj swój kod ;-) –

0

Rzeczywisty problem polega na tym: data = ws.receive()

Co się tu dzieje jest Twój websocket czeka teraz na jedno połączenie podczas gdy cała aplikacja po prostu zawiesza się.

Masz dwa rozwiązania, albo dodać timeout ws.receive() lub ustawić go jako aplikacja wysokim poziomie:

from geventwebsocket import WebSocketServer, WebSocketApplication, Resource 

class EchoApplication(WebSocketApplication): 
    def on_open(self): 
     print "Connection opened" 

    def on_message(self, message): 
     self.ws.send(message) 

    def on_close(self, reason): 
     print reason 

WebSocketServer(('', 8000), Resource({'/': EchoApplication}).serve_forever() 

jako przykładowa tutaj: https://pypi.python.org/pypi/gevent-websocket/

ten będzie następnie skonfigurować urządzenie proces całkowicie asynchroniczny, a zatem wysyłanie i odbieranie nie konkurowałoby o ten sam zasób.

Powiązane problemy