我在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.9
,celery-with-redis==3.0
和flower==0.6.0
进行监控。
任何帮助,提示或要测试的东西都将非常感激。
答案 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_ips
内ip_subtask
任务(即使{{1}}只需要一秒纳秒)。您必须为此编写新函数或使用celery task_success signal。