Celery:组中的一个子任务总是超时

时间:2014-10-21 19:21:12

标签: python parallel-processing timeout celery

我在Celery的group功能中遇到了令人烦恼的行为。

我经常需要检查一堆主机解析的IP,以确保所述IP无法更改。为了做到这一点,我有一个我需要验证的< hostname, IPs >字典。例如:

REQUIRED_HOSTS = {
    'google.com': {'173.194.46.64', '173.194.46.70', '173.194.46.71'},
    'stackoverflow.com': {'198.252.206.16'}
}

所以唯一要做的就是定期迭代REQUIRED_HOSTS.keys(),解析名称并查看它解析的任何IP是否与我记录的不同。 (这里没什么好事的)

为了提高效率,每个名称都是并行解析的。我为它创建了一个子任务(它使用dnspython解析):

@my_tasks.task
def resolve_hostname(hostname, resolver=None):
    """ This subtask resolves the 'hostname' to its IP addresses. It's
    intended to be used in the 'compare_required_ips' function to resolve
    names in parallel """
    if resolver is None:
        resolver = dns.resolver.Resolver()
        resolver.nameservers = ['8.8.8.8' + '4.2.2.2'] + resolver.nameservers

    try:
        return (hostname,
                {hst.address for hst in resolver.query(hostname)})
    except Exception, e:
        logger.exception("Got %s when trying to resolve hostname=%s"
                         % (type(e), hostname))
        raise e

现在,查询所有主机名并生成子任务的方法如下:

@my_taks.task
def compare_required_ips():
    """ This method verifies that the IPs haven't changed. """
    retval = []
    resolver = dns.resolver.Resolver()
    resolver.nameservers = ['8.8.8.8' + '4.2.2.2'] + resolver.nameservers
    retrieved_hosts = dict.fromkeys(required_hosts.REQUIRED_HOSTS.keys())
    logger.info("Going to compare IPs for %s hostnames=%s"
                % (len(required_hosts.REQUIRED_HOSTS.keys()),
                   required_hosts.REQUIRED_HOSTS.keys()))
    ip_subtasks = group(
        [resolve_hostname.s(hostname, resolver=resolver)
         for hostname in required_hosts.REQUIRED_HOSTS.keys()]
    )()
    for hostname, ips in ip_subtasks.get(timeout=90):
        retrieved_hosts[hostname] = ips

    for hostname in required_hosts.REQUIRED_HOSTS:
        if (required_hosts.REQUIRED_HOSTS[hostname]
                != retrieved_hosts[hostname]):
            retval.append(hostname)
            logger.error(
                "IP resolution mismatch. hostname=%s resolve_target=%s"
                ", resolve_actual=%s (mismatch=%s)"
                % (hostname,
                   required_hosts.REQUIRED_HOSTS[hostname],
                   retrieved_hosts[hostname],
                   (required_hosts.REQUIRED_HOSTS[hostname]
                    ^ retrieved_hosts[hostname]))
            )
    return retval

再次,相当容易......只需走REQUIRED_HOSTS个键(又名主机名),生成一个子任务来解析每个键,然后以90秒超时收集结果(它出现在for hostname, ips in ip_subtasks.get(timeout=90)}行

现在,令人讨厌的是除了一个子任务之外的所有子任务都在90秒窗口内成功完成。然后父任务(compare_required_ips)由于timeout=90而失败,并且当这发生时,子任务成功完成(在父项失败后立即)。我已经尝试增加和减少超时,并且子任务总是采用我在group创建中指定的任何超时,使主任务报告失败。

我还手动运行名称解析(不使用芹菜任务,但使用常规线程),并以毫秒为单位解析。每一次,我尝试做的每一次测试。我不认为这是dns.resolver.Resolver() class的问题。一切似乎都指出这个类快速地解决了,但是子任务,或者组,或者...... Celery中的某个人并不知道它(仅限其中一个子任务)

我正在使用celery==3.1.9celery-with-redis==3.0flower==0.6.0进行监控。

任何帮助,提示或要测试的东西都将非常感激。

1 个答案:

答案 0 :(得分:1)

由于启动同步子任务,一个问题可能是死锁。 compare_required_ips是一项芹菜任务。在此任务中,您正在等待group resolve_hostname个任务完成,这实际上是低效的。

所以你必须改变这个

ip_subtasks = group(
        [resolve_hostname.s(hostname, resolver=resolver)
         for hostname in required_hosts.REQUIRED_HOSTS.keys()]
    )()

ip_subtasks = group(
        [resolve_hostname.s(hostname, resolver=resolver)
         for hostname in required_hosts.REQUIRED_HOSTS.keys()]
    ).delay()

通过避免死锁来异步启动所有任务。

你不应该ip_subtasks.get() compate_required_ipsip_subtask任务(即使{{1}}只需要一秒纳秒)。您必须为此编写新函数或使用celery task_success signal