Tôi liên tục nhận được ngoại lệ httplib.CannotSendRequest khi sử dụng một chuỗi các SimpleXMLRPCServers sử dụng SocketServer.ThreadingMixin.python: httplib.CannotSendRequest khi lồng các luồng SimpleXMLRPCServers
Những gì tôi có nghĩa là bởi 'chuỗi' như sau:
Tôi có một kịch bản client trong đó sử dụng xmlrpclib để gọi một chức năng trên SimpleXMLRPCServer. Máy chủ đó, đến lượt nó, gọi một SimpleXMLRPCServer khác. Tôi nhận ra âm thanh phức tạp như thế nào, nhưng có những lý do chính đáng mà kiến trúc này đã được chọn, và tôi không thấy lý do nó không nên có thể.
(testclient)client_script ---calls-->
(middleserver)SimpleXMLRPCServer ---calls--->
(finalserver)SimpleXMLRPCServer --- does something
- Nếu tôi không sử dụng SocketServer.ThreadingMixin thì vấn đề này không xảy ra (nhưng tôi cần yêu cầu phải đa luồng vì vậy đây không giúp.)
- Nếu tôi chỉ có một mức dịch vụ duy nhất (tức là chỉ có kịch bản lệnh gọi máy khách cuối cùng trực tiếp) điều này không xảy ra.
Tôi đã có thể tạo lại vấn đề trong mã thử nghiệm đơn giản bên dưới. Có ba đoạn:
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()
Để tái sản xuất, các bước sau đây sẽ được sử dụng:
- Run python finalserver.py
- Run python middleserver.py
- Run python testclient.py
- Trong khi (3) vẫn chạy, chạy một thể hiện của python testclient.py
Khá thường xuyên (hầu như mọi lúc) bạn sẽ gặp lỗi bên dưới lần đầu tiên bạn thử chạy bước 4. Thật thú vị, nếu bạn ngay lập tức cố gắng chạy bước (4) một lần nữa lỗi sẽ không xảy ra.
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 dường như nói rằng ngoại lệ này có thể do nhiều cuộc gọi đến httplib.HTTPConnection.request mà không can thiệp cuộc gọi getresponse. Tuy nhiên, internet không thảo luận điều này trong bối cảnh của SimpleXMLRPCServer. Bất kỳ con trỏ nào theo hướng giải quyết vấn đề httplib.CannotSendRequest sẽ được đánh giá cao.
============================================== ============================================= TRẢ LỜI:
Được rồi, tôi hơi ngu ngốc. Tôi nghĩ rằng tôi đã nhìn chằm chằm vào mã cho quá kéo dài một khoảng thời gian mà tôi bị mất các giải pháp rõ ràng nhìn chằm chằm vào mặt tôi (khá nghĩa đen, bởi vì câu trả lời là thực sự trong câu hỏi thực tế.)
Về cơ bản, các Can'tSendRequest xảy ra khi một httplib.HTTPConnection bị gián đoạn bởi một hoạt động 'yêu cầu' can thiệp.Mỗi httplib.HTTPConnection.request phải được ghép nối với một cuộc gọi .getresponse(). Nếu việc ghép nối đó bị gián đoạn bởi một thao tác yêu cầu khác, yêu cầu thứ hai sẽ tạo ra lỗi không thể xảy ra. như vậy:
connection = httplib.HTTPConnection(...)
connection.request(...)
connection.request(...)
sẽ không thành công vì bạn có hai yêu cầu trên cùng một kết nối trước khi có bất kỳ phản hồi nào được gọi.
Linking rằng trở lại câu hỏi của tôi:
- nơi duy nhất trong ba chương trình mà kết nối như vậy đang được thực hiện trong các cuộc gọi serverproxy.
- sự cố chỉ xảy ra trong quá trình tạo luồng, vì vậy, đó có thể là điều kiện chạy đua.
- nơi duy nhất một cuộc gọi serverproxy được chia sẻ là trong middleserver.py
Các giải pháp sau đó, rõ ràng là có mỗi thread tạo ra nó là serverproxy riêng. Phiên bản cố định của middleserver là dưới đây, và nó hoạt động:
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()
Kể từ phiên bản này kết quả trong mỗi thread có xmlrpclib.serverproxy riêng của mình, không có nguy cơ như nhau dụ của serverproxy gọi HTTPConnection.request hơn hơn một lần liên tiếp. Các chương trình hoạt động như dự định.
Xin lỗi vì sự phiền toái.