Nieregularnie odbieram wyjątek httplib.CannotSendRequest, gdy używam łańcucha SimpleXMLRPCServers korzystającego z SocketServer.ThreadingMixin.python: httplib.CannotSendRequest podczas zagnieżdżania wątkowanych SimpleXMLRPCServers
Co mam na myśli przez „łańcuch” jest następujący:
Mam skrypt klienta, który wykorzystuje xmlrpclib wywołać funkcję na SimpleXMLRPCServer. Ten serwer z kolei wywołuje inny serwer SimpleXMLRPC. Zdaję sobie sprawę, że brzmi jak zawiłe, ale istnieją powody, że architektura ta została wybrana, a nie widzę powodu, to nie powinno być możliwe.
(testclient)client_script ---calls-->
(middleserver)SimpleXMLRPCServer ---calls--->
(finalserver)SimpleXMLRPCServer --- does something
- Jeśli nie używam SocketServer.ThreadingMixin to ten problem nie występuje (ale muszę żądania być wielowątkowy więc to nie pomoże.)
- Gdybym tylko pojedynczy poziom usług (tj. po prostu skrypt klienta wywołujący bezpośrednio serwer końcowy) tak się nie dzieje.
Byłem w stanie odtworzyć problem w prostym kodzie testu poniżej. Są trzy fragmenty:
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()
Aby odtworzyć, należy stosować następujące kroki:
- Run pyton finalserver.py
- Run pyton middleserver.py
- Run pyton testclient.py
- Chociaż (3) nadal działa, należy uruchomić inne wystąpienie Pythona testclient.py
Dość często (prawie za każdym razem) otrzymasz błąd poniżej po raz pierwszy spróbować uruchomić krok 4. Co ciekawe, jeśli natychmiast spróbować uruchomić etap (4) ponownie błąd nie występuje.
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 wydaje się, że wyjątek ten może być spowodowane przez wiele połączeń do httplib.HTTPConnection.request bez interwencji połączeń GetResponse. Jednak Internet nie omawia tego w kontekście SimpleXMLRPCServer. Dowolne wskazówki w kierunku rozwiązania problemu httplib.CannotSendRequest będą mile widziane.
============================================== ============================================= ODPOWIEDŹ:
Ok, jestem trochę głupie. Myślę, że patrzyłem na kod przez zbyt długi czas, że przegapiłem oczywiste rozwiązanie, patrząc mi w twarz (całkiem dosłownie, ponieważ odpowiedź brzmi właściwie w rzeczywistym pytaniu).
Zasadniczo pojawia się CannotSendRequest gdy połączenie httplib.HTTPC przerwane jest przez interweniującą operację "request".Każde httplib.HTTPConnection.request musi być sparowane z wywołaniem .getresponse(). Jeśli parowanie zostanie przerwane przez inną operację żądania, drugie żądanie spowoduje błąd CannotSendRequest. tak:
connection = httplib.HTTPConnection(...)
connection.request(...)
connection.request(...)
zakończy się niepowodzeniem, ponieważ masz dwa żądania na tym samym połączeniu, zanim zostanie wywołana jakakolwiek reakcja.
Łączenie tego z powrotem do mojego pytania:
- jedyne miejsce w trzech programach, w których takie połączenia są wykonane w zaproszeniach serverproxy.
- Problem występuje tylko podczas gwintowania, więc prawdopodobnie jest to stan wyścigu.
- jedyne miejsce połączenia serverproxy dzielone jest w middleserver.py
Rozwiązaniem jest więc oczywiście mieć każdy wątek stworzyć swój własny serverproxy. Poprawiona wersja middleserver jest poniżej, a to działa:
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()
Od tego Wersja wyników w każdym wątku posiadającego własną xmlrpclib.serverproxy, nie ma ryzyka samej instancji z serverproxy powołuje HTTPConnection.request więcej niż jeden raz z rzędu. Programy działają zgodnie z przeznaczeniem.
Przepraszamy za kłopot.