当前位置: 首页 > 工具软件 > PubSubHubbub > 使用案例 >

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

金嘉言
2023-12-01

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 。

 类似资料: