03-PubSubHubbub 和 twisted 的 Persistent connections 能力
鄭昀 201005 隸屬於《07.雜項》
關於上節《02-Twisted 構建 Web Server 的 Socket 長連結問題》,還可以繼續探討為何會保持 Socket 長連結。
該關閉的串連沒關閉?
有人在twisted郵件清單中也反映:
『We close the render_POST with a request.write('data') & a request.finish() but the connection stays open』調用了request.finish(),socket串連依然保持著,這現象和我們遇到的一樣。
在 twisted.web.server 裡,Request 的 finish 函數是這麼定義的:
def finish(self):
http.Request.finish(self)
for d in self.notifications:
d.callback(None)
self.notifications = []
它調用了 twisted.web.http 的 Request 類之 finish 方法:
def finish(self):
"""We are finished writing data."""
if self.finished:
warnings.warn("Warning! request.finish called twice.", stacklevel=2)
return
if not self.startedWriting:
# write headers
self.write('')
if self.chunked:
# write last chunk and closing CRLF
self.transport.write("0\r\n\r\n")
# log request
if hasattr(self.channel, "factory"):
self.channel.factory.log(self)
self.finished = 1
if not self.queued:
self._cleanup()
可以看出 request.finish() 只是說要結束寫資料了,把緩衝區內的資料都發送給對方,並沒有去中斷連線,否則就應該主動執行 self.transport.loseConnection() 。所以不主動斷開socket串連也是設計使然。
PubSubHubbub 的持久串連
本來 PubSubHubbub 的 Hub 本來就是要保持長串連,從而重用串連。它的文檔 PublisherEfficiency 上稱:
『HTTP persistent connections and pipelining
By default in HTTP 1.1, TCP connections will be reused between requests. For a publisher serving many separate Atom feeds, this allows Hubs to get around the expense of creating a new TCP connection every time an event happens. Instead, Hubs MAY leave their TCP connections open and reuse them to make HTTP requests for freshly published events. 』
twisted.web.server 也支援持久串連
twisted.web.server 是支援 support persistent connections and pipelining 的。
文檔指出:
『Alternatively, they can return a special constant,twisted.web.server.NOT_DONE_YET, which tells the web server not to close the connection』即通過返回一個NOT_DONE_YET來告知Web Server不要關閉socket串連。
所以會看到 Google Code 上不少代碼都是在 render 函數內返回 server.NOT_DONE_YET 。
twisted.web.server.py 中, Request.render 函數定義中有這麼一句判斷:
if body == NOT_DONE_YET:
return
...
self.finish()
也就是說如果你返回 NOT_DONE_YET ,就不會再調用 request.finish() 了。
這個 NOT_DONE_YET 標誌有幾種解釋:
http://www.olivepeak.com/blog/posts/read/simple-http-pubsub-server-with-twisted 說:
『returns server.NOT_DONE_YET. This tells Twisted to not return anything to the client, but leave the connection open for later processing.』
http://www.ibm.com/developerworks/library/l-twist2.html?S_TACT=105AGX52&S_CMP=cn-a-l 則認為:
『The odd-looking return value server.NOT_DONE_YET is a flag to the Twisted server to flush the page content out of the request object.』
http://blog.csdn.net/gashero/archive/2007/03/02/1519045.aspx 說:
『你可以使用魔術值 twisted.web.server.NOT_DONE_YET ,可以告知Resource有些事情是非同步而且尚未完成,直到你調用了request.finish()。然後調用request.finish()直到寫完了所有的響應資料。』
總之,不管如何解釋,return server.NOT_DONE_YET from the render_GET/render_POST method, so the connection keeps open是沒錯的。
所以身為 PubSubHubbub Subscriber 角色的 Web Server ,我們的 render_POST 方法可以這麼寫:
# 處理 PubSubHubbub Hub 推送過來的資料
def render_POST(self, request):
try:
body = request.content.read()
def finish_success(request):
if(request._disconnected or request.finished):
print('***>This request is already finished.')
pass
else:
print('***>This request wiil be finished.')
request.setResponseCode(http.NO_CONTENT)
request.write("")
request.finish()
def finish_failed(request):
print('-=->fail in parseData?')
threads.deferToThread(self.parseData, body).addCallbacks(lambda x: finish_success(request), lambda x: finish_failed(request))
"""deferToThread 方法預設用 reactor.getThreadPool() 開闢的線程池。
它調用這個線程池的 threadpool.callInThreadWithCallback
方法,實際效果和 reactor.callInThread 一樣。區別只是 deferToThread 可以返回一個 deferred ,能夠 addCallback。
"""
except:
traceback.print_exc()
request.setResponseCode(http.NO_CONTENT)#即204
return NOT_DONE_YET
也就是,接收到 POST 過來的資料後,非同步扔給另一個方法解析和處理,這廂立刻 return NOT_DONE_YET , 等處理成功了,回調裡再 finish 掉當前的 request 。
參考資源:
1、關於Windows頻繁開啟關閉連接埠時出現的問題 ;
2、Choosing a TCP Port for a Network Service ;
3、02-Twisted 構建 Web Server 的 Socket 長連結問題 | 07.雜項 | Python ;
4、03-PubSubHubbub 和 twisted 的 Persistent connections 能力 | 07.雜項 | Python ;
5、Windows頻繁開啟和關閉連接埠可能引發的問題 | 07.雜項 。