2012-11-23 16 views
7

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:

  1. Run python finalserver.py
  2. Run python middleserver.py
  3. Run python testclient.py
  4. 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:

  1. 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.
  2. 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.
  3. 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.

Trả lời

11

Đượ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 để 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. Về cơ bản, 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:

  1. 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.
  2. 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.
  3. 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ó rủi ro của serverproxy gọi HTTPConnection.request nhiều 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.