Ricevo a intermittenza un'eccezione httplib.CannotSendRequest quando si utilizza una catena di SimpleXMLRPCServers che utilizza SocketServer.ThreadingMixin.python: httplib.CannotSendRequest durante l'annidamento di thread SimpleXMLRPCServers
Cosa intendo per 'catena' è la seguente:
Ho uno script client che utilizza xmlrpclib di chiamare una funzione su un SimpleXMLRPCServer. Quel server, a sua volta, chiama un altro SimpleXMLRPCServer. Mi rendo conto di quanto suoni contorto, ma ci sono buone ragioni per cui questa architettura è stata selezionata, e non vedo una ragione per cui non dovrebbe essere possibile.
(testclient)client_script ---calls-->
(middleserver)SimpleXMLRPCServer ---calls--->
(finalserver)SimpleXMLRPCServer --- does something
- Se io non uso SocketServer.ThreadingMixin allora questo problema non si verifica (ma ho bisogno le richieste di essere multi-threaded quindi questo non aiuta.)
- Se ho solo un singolo livello di servizi (cioè solo script client che chiama direttamente il server finale) questo non accade.
sono stato in grado di riprodurre il problema nella semplice codice di prova di seguito. Ci sono tre frammenti:
finalserver:
import SocketServer
import time
from SimpleXMLRPCServer import SimpleXMLRPCServer
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler
class AsyncXMLRPCServer(SocketServer.ThreadingMixIn,SimpleXMLRPCServer): pass
# Create server
server = AsyncXMLRPCServer(('', 9999), SimpleXMLRPCRequestHandler)
server.register_introspection_functions()
def waste_time():
time.sleep(10)
return True
server.register_function(waste_time, 'waste_time')
server.serve_forever()
middleserver:
import SocketServer
from SimpleXMLRPCServer import SimpleXMLRPCServer
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler
import xmlrpclib
class AsyncXMLRPCServer(SocketServer.ThreadingMixIn,SimpleXMLRPCServer): pass
# Create server
server = AsyncXMLRPCServer(('', 8888), SimpleXMLRPCRequestHandler)
server.register_introspection_functions()
s = xmlrpclib.ServerProxy('http://localhost:9999')
def call_waste():
s.waste_time()
return True
server.register_function(call_waste, 'call_waste')
server.serve_forever()
TestClient:
import xmlrpclib
s = xmlrpclib.ServerProxy('http://localhost:8888')
print s.call_waste()
Per riprodurre, devono essere utilizzati i seguenti passi:
- Run pitone finalserver.py
- Run pitone middleserver.py
- Run pitone testclient.py
- Mentre (3) è ancora in funzione, eseguire un'altra istanza di pitone testclient.py
abbastanza spesso (quasi ogni volta) si verifica l'errore sotto la prima volta che si tenta di eseguire il passaggio 4. È interessante notare che se si tenta immediatamente di eseguire il passaggio (4) di nuovo l'errore non si verificherà.
Traceback (most recent call last):
File "testclient.py", line 6, in <module>
print s.call_waste()
File "/usr/lib64/python2.7/xmlrpclib.py", line 1224, in __call__
return self.__send(self.__name, args)
File "/usr/lib64/python2.7/xmlrpclib.py", line 1578, in __request
verbose=self.__verbose
File "/usr/lib64/python2.7/xmlrpclib.py", line 1264, in request
return self.single_request(host, handler, request_body, verbose)
File "/usr/lib64/python2.7/xmlrpclib.py", line 1297, in single_request
return self.parse_response(response)
File "/usr/lib64/python2.7/xmlrpclib.py", line 1473, in parse_response
return u.close()
File "/usr/lib64/python2.7/xmlrpclib.py", line 793, in close
raise Fault(**self._stack[0])
xmlrpclib.Fault: <Fault 1: "<class 'httplib.CannotSendRequest'>:">
Internet sembri dire che questa eccezione può essere causato da più chiamate a httplib.HTTPConnection.request senza intervenire chiamate GetResponse. Tuttavia, Internet non ne discute nel contesto di SimpleXMLRPCServer. Sarebbe apprezzato qualsiasi suggerimento nella direzione di risolvere il problema httplib.CannotSendRequest.
============================================== ============================================= RISPOSTA:
Ok, sono un po 'stupido. Penso che stavo fissando il codice per un periodo di tempo troppo lungo che mi mancava l'ovvia soluzione che mi guardava in faccia (letteralmente, perché la risposta è in realtà nella domanda reale.)
Fondamentalmente, la CannotSendRequest si verifica quando un httplib.HTTPConnection viene interrotto da un'operazione "richiesta" intervenuta.Ogni httplib.HTTPConnection.request deve essere associato a una chiamata .getresponse(). Se tale associazione viene interrotta da un'altra operazione di richiesta, la seconda richiesta genererà l'errore CannotSendRequest. quindi:
connection = httplib.HTTPConnection(...)
connection.request(...)
connection.request(...)
non riuscirà perché si hanno due richieste sulla stessa connessione prima che venga richiamata qualsiasi risposta ricevuta.
Collegamento che di nuovo alla mia domanda:
- l'unico posto nei tre programmi in cui tali collegamenti sono stati fatti sono nelle chiamate ServerProxy.
- il problema si verifica solo durante il threading, quindi è probabile che si tratti di una race condition.
- l'unico luogo una chiamata ServerProxy è condivisa è in middleserver.py
La soluzione quindi, è ovviamente avere ogni thread creare il proprio ServerProxy. La versione fissa di middleserver è inferiore, e funziona:
import SocketServer
from SimpleXMLRPCServer import SimpleXMLRPCServer
from SimpleXMLRPCServer import SimpleXMLRPCRequestHandler
import xmlrpclib
class AsyncXMLRPCServer(SocketServer.ThreadingMixIn,SimpleXMLRPCServer): pass
# Create server
server = AsyncXMLRPCServer(('', 8888), SimpleXMLRPCRequestHandler)
server.register_introspection_functions()
def call_waste():
# Each call to this function creates its own serverproxy.
# If this function is called by concurrent threads, each thread
# will safely have its own serverproxy.
s = xmlrpclib.ServerProxy('http://localhost:9999')
s.waste_time()
return True
server.register_function(call_waste, 'call_waste')
server.serve_forever()
Da questa versione risultati in ogni filo con la propria xmlrpclib.serverproxy, non v'è alcun rischio che il stessa istanza di ServerProxy invocare HTTPConnection.request più di una volta in successione. I programmi funzionano come previsto.
Scusate per il disturbo.