python: httplib.CannotSendRequest при вложении резьбовых SimpleXMLRPCServers



Я периодически получаю httplib.Исключение CannotSendRequest при использовании цепочки Simplexmlrpcserver, использующих SocketServer.ThreadingMixin.



Под "цепью" я подразумеваю следующее:



У меня есть клиентский скрипт, который использует xmlrpclib для вызова функции на SimpleXMLRPCServer. Этот сервер, в свою очередь, вызывает другой SimpleXMLRPCServer. Я понимаю, как запутанно это звучит, но есть веские причины, по которым эта архитектура была выбрана, и я не вижу почему это не должно быть возможно.



(testclient)client_script ---calls--> 
(middleserver)SimpleXMLRPCServer ---calls--->
(finalserver)SimpleXMLRPCServer --- does something



  • Если я не использую SocketServer.ThreadingMixin тогда эта проблема не возникает (но мне нужно, чтобы запросы были многопоточными, поэтому это не помогает.)

  • Если у меня есть только один уровень служб (т. е. просто клиентский скрипт, вызывающий конечный сервер напрямую), этого не происходит.


Я смог воспроизвести эту проблему в простом тестовом коде ниже. Их всего три фрагменты:



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


Для воспроизведения следует использовать следующие шаги:




  1. запуск python finalserver.py

  2. запуск python middleserver.py

  3. запуск python testclient.py

  4. пока (3) все еще работает, запустите другой экземпляр python testclient.py


Довольно часто (почти каждый раз) вы получите ошибку ниже при первой попытке выполнить Шаг 4. Интересно, что если вы немедленно попытаетесь выполнить Шаг (4) снова, ошибка не возникнет.



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'>:">


Интернет, кажется, говорит, что это исключение может быть вызвано несколькими вызовами httplib.HTTPConnection.запрос без вмешательства getresponse вызовов. Однако интернет не обсуждает это в контексте SimpleXMLRPCServer. Любые указатели в направлении разрешения httplib.Вопрос CannotSendRequest был бы оцененный.



===========================================================================================
Ответ:



Ладно, я немного туповат. Я думаю, что я смотрел на код слишком долго, что я пропустил очевидное решение, смотрящее мне в лицо (в буквальном смысле, потому что ответ на самом деле находится в самом вопросе.)

В основном, CannotSendRequest возникает, когда httplib.HTTPConnection прерывается промежуточной операцией' request'. Каждый httplib.HTTPConnection.запрос должен быть сопряжен с a .вызов getresponse (). Если это сопряжение прерывается другой операцией запроса, то второй запрос вызовет ошибку CannotSendRequest. Итак:



connection = httplib.HTTPConnection(...)
connection.request(...)
connection.request(...)


Произойдет сбой, потому что у вас есть два запроса на одно и то же соединение, прежде чем будет вызван какой-либо getresponse.



Связывая это с моим вопросом:




  1. единственное место в трех программах, где выполняются такие соединения, - это вызовы serverproxy.

  2. проблема возникает только во время потоковой обработки, поэтому, скорее всего, это состояние гонки.

  3. единственное место, где вызов serverproxy является общим, находится в middleserver.py


Тогда решение, очевидно, состоит в том, чтобы каждый поток создавал свой собственный serverproxy. Исправленная версия 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()

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


Поскольку эта версия приводит к тому, что каждый поток имеет свой собственный xmlrpclib.serverproxy, нет никакого риска того же экземпляра вызова serverproxy HTTPConnection.запрос более одного раза подряд. Программы работают так, как задумано.



Извините за беспокойство.

488   1  

1 ответ:

Ладно, я немного туповат. Я думаю, что я смотрел на код в течение длительного периода времени, что я пропустил очевидное решение, глядя мне в лицо (в буквальном смысле, потому что ответ на самом деле находится в самом вопросе.)

В основном, CannotSendRequest возникает, когда httplib.HTTPConnection прерывается промежуточной операцией' request'. В основном, каждый httplib.HTTPConnection.запрос должен быть сопряжен с a .вызов getresponse (). Если это сопряжение прерывается другая операция запроса, второй запрос вызовет ошибку CannotSendRequest. Итак:

connection = httplib.HTTPConnection(...)
connection.request(...)
connection.request(...)

Завершится ошибкой, так как у вас есть два запроса на одно и то же соединение перед вызовом любого getresponse.

Связывая это с моим вопросом:

  1. единственное место в трех программах, где выполняются такие соединения, - это вызовы serverproxy.
  2. проблема возникает только во время потоковой обработки, поэтому, скорее всего, это состояние гонки.
  3. единственное место а вызов serverproxy является общим находится в middleserver.py

Тогда решение, очевидно, состоит в том, чтобы каждый поток создавал свой собственный serverproxy. Исправленная версия 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()

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

Поскольку эта версия приводит к тому, что каждый поток имеет свой собственный xmlrpclib.serverproxy, нет никакого риска того, что serverproxy вызовет HTTPConnection.запрос более одного раза подряд. Программы работают так, как задумано.

Извините за беспокойство.

Comments

    Ничего не найдено.