python twisted代理超时

时间:2013-02-20 16:30:12

标签: python timeout twisted agent

我很擅长扭曲,我正在尝试创建一个异步客户端来获取一些网址并将结果保存到每个网址的不同文件中。当我使用有限数量的服务器运行程序时,假设10,反应器循环正确结束并且程序终止。但是当我用例如Alexa top 2500运行程序时,程序开始获取URL但是然后没有终止。我已设置超时但它不起作用,我相信必须有一些打开的套接字不会因错误或成功而触发任何回调。我的目标是,一旦程序获取了页面,或者每个连接超时已到期,程序必须终止并关闭所有活动的文件描述符。

我很抱歉,但是在复制和粘贴时没有保留代码缩进,现在我已经检查过并且已经修复了。代码是最基本的例子,请注意,我的问题是当我启动程序时,反应堆没有停止,需要抓取大量站点。

#!/usr/bin/env python

from pprint import pformat
from twisted.internet import reactor
import twisted.internet.defer
import sys
from twisted.internet.protocol import Protocol
from twisted.web.client import Agent
from twisted.web.http_headers import Headers

class PrinterClient(Protocol):
    def __init__(self, whenFinished, output):
         self.whenFinished = whenFinished
         self.output = output

    def dataReceived(self, bytes):
         #print '##### Received #####\n%s' % (bytes,)
         self.output.write('%s' % (bytes,))

    def connectionLost(self, reason):
        print 'Finished:', reason.getErrorMessage()
        self.output.write('Finished: %s \n'%(reason.getErrorMessage()))
        self.output.write('#########end########%s\n'%(reason.getErrorMessage()))
        self.whenFinished.callback(None)

def handleResponse(r, output, url):
    output.write('############start############\n')
    output.write('%s\n'%(url))
    #print "version=%s\ncode=%s\nphrase='%s'" % (r.version, r.code, r.phrase)
    output.write("version=%s\ncode=%s\nphrase='%s'"\
             %(r.version, r.code, r.phrase))
    for k, v in r.headers.getAllRawHeaders():
        #print "%s: %s" % (k, '\n  '.join(v))
        output.write("%s: %s\n" % (k, '\n  '.join(v)))
    whenFinished = twisted.internet.defer.Deferred()
    r.deliverBody(PrinterClient(whenFinished, output))
    return whenFinished

def handleError(reason):
    print reason
    #reason.printTraceback()
    #reactor.stop()

def getPage(url, output):
    print "Requesting %s" % (url,)
    d = Agent(reactor).request('GET',
                       url,
    Headers({'User-Agent': ['Mozilla/4.0 (Windows XP 5.1) Java/1.6.0_26']}),
                       None)
    d._connectTimeout = 10
    d.addCallback(handleResponse, output, url)
    d.addErrback(handleError)
    return d

if __name__ == '__main__':
    semaphore = twisted.internet.defer.DeferredSemaphore(500)
    dl = list()
    ipset = set()
    queryset =  set(['http://www.google.com','http://www.google1.com','http://www.google2.com', "up to 2500 sites"])
    filemap = {}
    for q in queryset:
        fpos = q.split('http://')[1].split(':')[0]
        dl.append(semaphore.run(getPage, q, filemap[fpos]))
    dl = twisted.internet.defer.DeferredList(dl)
    dl.addCallbacks(lambda x: reactor.stop(), handleError)
    reactor.run()
    for k in filemap:
        filemap[k].close()

感谢。 Jeppo

1 个答案:

答案 0 :(得分:6)

您的超时代码至少有两个问题。

首先,您设置的唯一超时是_connectTimeout,并且您将其设置为从Deferred返回的Agent.request。这是一个毫无意义的属性,Agent实现中的任何内容或Twisted的任何部分都不会尊重它。我认为您打算在Agent实例上设置此属性,而不会产生一些影响。但是,它是一个私有属性,不适合您直接进行交互。相反,您应该将connectTimeout=10传递给Agent初始化程序。

其次,此超时仅影响TCP连接设置超时。将其设置为10意味着如果在不到10秒的时间内无法建立到特定URL的HTTP服务器的TCP连接,则请求尝试将因超时错误而失败。但是,如果在不到10秒的时间内成功建立连接,则超时没有其他意义。如果服务器需要10个小时向您发送回复,Agent将坐在那里等待10个小时。您需要额外的超时,整个请求超时。

这是使用reactor.callLater和可能Deferred.cancel单独实现的内容。例如,

...
d = agent.request(...)
timeoutCall = reactor.callLater(60, d.cancel)
def completed(passthrough):
    if timeoutCall.active():
        timeoutCall.cancel()
    return passthrough
d.addBoth(completed)
...