03-PubSubHubbub 和 twisted 的 Persistent connections 能力 | 07.杂项 | Python

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.杂项 。

 

posted @ 2010-08-10 16:53  老兵笔记  阅读(1692)  评论(0编辑  收藏  举报