2012-09-29 7 views
8

Ho creato un server HTTP filettato (con fili Python) con la creazione di una classe che eredita da HTTPServer e ThreadingMixIn:elaborazione simultanea/richieste asincrone con Python BaseHTTPServer

class ThreadedHTTPServer(ThreadingMixIn, HTTPServer): 
    pass 

Ho un gestore di classe che eredita da BaseHTTPRequestHandler, e comincio il server con qualcosa di simile:

class MyHandler(BaseHTTPRequestHandler): 
    ... 

server = ThreadedHTTPServer(('localhost', 8080), MyHandler) 
# Prevent issues with socket reuse 
server.allow_reuse_address = True 
# Start the server 
server.serve_forever() 

questo è tutto abbastanza semplice. Il problema che sto riscontrando è che, ThreadingMixIn, ForkingMixIn, o in altro modo, la richiesta termina il blocco sul gestore delle richieste da restituire. Questo può essere visto facilmente mediante l'attuazione di questo codice di esempio:

class MyHandler(BaseHTTPRequestHandler): 
    def respond(self, status_code): 
     self.send_response(status_code) 
     self.end_headers() 

    def do_GET(self): 
     print "Entered GET request handler" 
     time.sleep(10) 
     print "Sending response!" 
     respond(200) 

Se il server fosse lavorazione di questi contemporaneamente, allora saremmo in grado di inviare due richieste e vedere l'assistente Inserisci sia GET gestori delle richieste prima di inviare o di risposta. Invece, il server entrerà nel gestore di richieste GET per la prima richiesta, attenderà che ritorni, quindi lo inserirà per il secondo (quindi la seconda richiesta impiega circa 20 secondi per tornare invece di 10).

Esiste un modo semplice per implementare un sistema in cui il server non attende il ritorno del gestore? Nello specifico, sto provando a scrivere un sistema che aspetta di ricevere diverse richieste prima di restituirle (una forma di polling lungo) e di eseguire problemi in cui la prima richiesta in attesa blocca qualsiasi richiesta futura di connettersi al server.

risposta

11
class ThreadedHTTPServer(ThreadingMixIn, HTTPServer): 
    pass 

è sufficiente. Probabilmente il tuo cliente non fa richieste concorrenti. Se si effettuano le richieste in parallelo, il server con thread funziona come previsto. Ecco il cliente:

#!/usr/bin/env python 
import sys 
import urllib2 

from threading import Thread 

def make_request(url): 
    print urllib2.urlopen(url).read() 

def main(): 
    port = int(sys.argv[1]) if len(sys.argv) > 1 else 8000 
    for _ in range(10): 
     Thread(target=make_request, args=("http://localhost:%d" % port,)).start() 

main() 

e il server corrispondente:

import time 
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer, test as _test 
from SocketServer  import ThreadingMixIn 


class ThreadedHTTPServer(ThreadingMixIn, HTTPServer): 
    pass 

class SlowHandler(BaseHTTPRequestHandler): 
    def do_GET(self): 
     self.send_response(200) 
     self.send_header("Content-type", "text/plain") 
     self.end_headers() 

     self.wfile.write("Entered GET request handler") 
     time.sleep(1) 
     self.wfile.write("Sending response!") 

def test(HandlerClass = SlowHandler, 
     ServerClass = ThreadedHTTPServer): 
    _test(HandlerClass, ServerClass) 


if __name__ == '__main__': 
    test() 

Tutti i 10 richieste di finire in 1 secondo. Se si rimuove ThreadingMixIn dalla definizione del server, tutte e 10 le richieste impiegano 10 secondi per essere completate.

+2

Stavo testando questo inviando richieste al server tramite Google Chrome. Si è scoperto che Chrome stava serializzando le mie richieste allo stesso server, aspettando che si tornasse prima di inviare anche quella successiva. L'esecuzione di un semplice script Python lo ha risolto. Grazie! – Dylnuge

+0

Dalla mia esperienza time.sleep (1) non riflette il carico di lavoro reale di 1 secondo, quindi non penso che dovresti testare la latenza usando thread.sleep. Purtroppo non ho la spiegazione formale, ma suppongo che abbia qualcosa a che fare con l'ottimizzazione della condivisione delle risorse in caso di thread.sleep. – MikeL

+0

@MikeL: 'time.sleep (1)' è appropriato per il caso nella risposta. Tutto ciò che ci interessa qui è se il server può gestire più richieste simultanee. Come ha detto OP: il problema era che il browser non effettua più di una richiesta allo stesso url. – jfs