应用程序引擎DeadlineExceededError用于cron作业和维基百科爬虫的任务队列

时间:2010-10-12 21:52:32

标签: python google-app-engine cron wikipedia

我正在尝试在Google应用引擎上构建维基百科链接抓取工具。我想在数据存储区中存储索引。但是我遇到了cron作业和任务队列的DeadlineExceededError。

对于cron作业,我有这段代码:

def buildTree(self):

    start=time.time()
    self.log.info(" Start Time: %f" % start)
    nobranches=TreeNode.all()       

    for tree in nobranches:            
        if tree.branches==[]:
            self.addBranches(tree)
            time.sleep(1)
        if (time.time()-start) > 10 :                
            break
        self.log.info("Time Eclipsed: %f" % (time.time()-start))

    self.log.info(" End Time:%f" % time.clock())

start=time.time() self.log.info(" Start Time: %f" % start) nobranches=TreeNode.all() for tree in nobranches: if tree.branches==[]: self.addBranches(tree) time.sleep(1) if (time.time()-start) > 10 : break self.log.info("Time Eclipsed: %f" % (time.time()-start)) self.log.info(" End Time:%f" % time.clock())

我不明白为什么for循环在10秒后没有破坏。它在开发服务器上。服务器上的time.time()必定有问题。我可以使用其他功能吗?

对于任务队列我有这个代码:

def addNewBranch(self, keyword, level=0):

    self.log.debug("Add Tree")        
    self.addBranches(keyword)

    t=TreeNode.gql("WHERE name=:1", keyword).get()
    branches=t.nodes

    if level < 3:
        for branch in branches:
            if branch.branches == []:
                taskqueue.add(url="/addTree/%s" % branch.name)
                self.log.debug("url:%s" % "/addTree/%s" % branch.name)

日志显示它们都遇到DeadlineExceededError。对于页面请求,后台处理的时间不应超过30秒。有没有解决异常的方法?

以下是addBranch()的代码 self.log.debug("Add Tree") self.addBranches(keyword) t=TreeNode.gql("WHERE name=:1", keyword).get() branches=t.nodes if level < 3: for branch in branches: if branch.branches == []: taskqueue.add(url="/addTree/%s" % branch.name) self.log.debug("url:%s" % "/addTree/%s" % branch.name)

4 个答案:

答案 0 :(得分:2)

我在GAE上的约会时间取得了很大的成功。

from datetime import datetime, timedelta
time_start = datetime.now()
time_taken = datetime.now() - time_start

time_taken将是timedelta。您可以将它与另一个具有您感兴趣的持续时间的timedelta进行比较。

ten_seconds = timedelta(seconds=10)
if time_taken > ten_seconds:
    ....do something quick.

听起来使用mapreduce或任务队列会更好。处理大量记录都非常有趣。

您拥有的代码的清洁模式是仅获取一些记录。

nobranches=TreeNode.all().fetch(100)

此代码仅提取100条记录。如果你有一个完整的100,那么当你完成后,你可以在队列中抛出另一个项目以启动更多。

- 基于关于需要没有分支的树的评论 -

我没有在那里看到你的模型,但如果我试图创建一个没有分支的所有树的列表并处理它们,我会:只为100块左右的树中的树获取密钥。然后,我将使用In查询获取属于这些树的所有分支。按树键排序。扫描分支列表,第一次找到树的密钥时,从列表中提取密钥树。完成后,您将获得“无分支”树键列表。安排其中的每一个进行处理。

更简单的版本是在树上使用MapReduce。对于每个树,找到一个与其ID匹配的分支。如果你不能,请标记树以便跟进。默认情况下,此功能将同时为8个工人提取批次树(我认为25)。并且,它在内部管理作业队列,因此您不必担心超时。

答案 1 :(得分:1)

除了在正确的时间范围内执行代码之外,没有办法“绕过”截止日期。

答案 2 :(得分:1)

当DeadlineExcededErrors发生时,如果再次调用,您希望请求最终成功。这可能要求您的爬行状态保证已经取得了一些可以在下次跳过的进度。 (这里没有提到)

并行呼叫可以提供极大的帮助。

  • Urlfetch
  • 数据存储区(使用db.put将混合实体放在一起)
  • 数据存储区查询(并行查询 - asynctools)

<强>的URLFetch:

  • 当您进行urlfetch调用时,请确保使用异步模式来折叠循环。

<强>数据存储

  • 将实体合并为一次往返电话。

    # put newNodes+tree at the same time
    db.put(newNodes+tree)
    
  • 将TreeNode.gql从内部循环中拉入并行查询工具,如asynctools http://asynctools.googlecode.com

Asynctools示例

    if pyNode is not None:

        runner = AsyncMultiTask()
        for child in pyNode:
             title = child.attributes["title"].value
             query = db.GqlQuery("SELECT __key__ FROM TreeNode WHERE name = :1", title)
             runner.append(QueryTask(query, limit=1, client_state=title))

        # kick off the work
        runner.run()

        # peel out the results
        treeNodes = []
        for task in runner:
            task_result = task.get_result() # will raise any exception that occurred for the given query
            treeNodes.append(task_result)

        for node in treeNodes:
            if node is None:
                newNodes.append(TreeNode(name=child.attributes["title"].value))

            else:
                tree.branches.append(node.key())
        for node in newNodes:
            tree.branches.append(node.key())
            self.log.debug("Node Added: %s" % node.name)

        # put newNodes+tree at the same time
        db.put(newNodes+tree)
        return tree.branches

披露:我与asynctools有联系。

答案 3 :(得分:1)

这里的问题是您正在对文档中的每个链接执行查询操作。由于维基百科页面可以包含很多链接,这意味着很多查询 - 因此,您的处理时间不足。这种方法也会以惊人的速度消耗你的配额!

相反,您应该使用Wikipedia页面的页面名称作为实体的密钥名称。然后,您可以将文档中的所有链接收集到列表中,从中构造键(这是一个完全本地的操作),并为所有这些操作执行单个db.get批处理。在适当地更新和/或创建它们之后,您可以执行批处理db.put将它们全部存储到数据存储区 - 将数据存储区的总操作从numlinks * 2减少到只有2!