不希望的芹菜过程延迟

时间:2018-02-17 22:58:38

标签: python rabbitmq celery

我在芹菜过程中遇到了不希望的延迟,我无法解释。我的目的是管理传入数据的实时处理(以每秒10到60个数据的速率)。一个数据的处理被分成两个完全顺序的任务,但是并行化用于开始处理下一个数据(具有任务1),而处理当前数据(具有任务2)尚未完成。由于它是一个实时应用程序,因此在此过程中获得最短的延迟至关重要。

有一段时间,我在这个过程中遇到了冻结。为了看看这个问题来自哪里,我开始监视我的工人的职业。它似乎发生在工人之间的沟通中。我设计了最轻巧,最简单的例子来说明这一点。

这是我的代码,你可以看到我有两个任务,除了每个等待10毫秒。我每隔20ms就用一次芹菜链给他们打电话。我使用prerun和postrun以及日志来跟踪每个工人的职业。在大多数情况下,所有工作都按顺序发生,因为两个工人花费的时间都不超过发送速率。

from __future__ import absolute_import

import time
from celery import chain
from celery.signals import task_prerun, task_postrun
from celery import Celery
from kombu import Queue, Exchange


N_ITS = 100000  # Total number of chains sent
LOG_FILE = 'log_file.txt'  # Path to the log file


def write_to_log_file(text):
    with open(LOG_FILE, 'a') as f:
        f.write(text)


# Create celery app
app = Celery('live')
app.config_from_object('celeryconfig')

default_exchange = Exchange('default', type='direct')
app.conf.task_queues = tuple(Queue(route['queue'], default_exchange, routing_key=route['queue'])
                                    for route in app.conf.task_routes.values() + [{'queue': 'default'}])
app.conf.update(result_expires=3600)


# Define functions that record timings
@task_prerun.connect()
def task_prerun(signal=None, sender=None, task_id=None, task=None, **kwargs):
    text = 'task_prerun; {0}; {1:.16g}\n'.format(task.name, time.time())
    write_to_log_file(text)


@task_postrun.connect()
def task_postrun(signal=None, sender=None, task_id=None, task=None, **kwargs):
    text = 'task_postrun; {0}; {1:.16g}\n'.format(task.name, time.time())
    write_to_log_file(text)


# Define tasks
@app.task
def task_1(i):
    print 'Executing task_1: {}'.format(i)
    time.sleep(0.01)


@app.task
def task_2(i):
    print 'Executing task_2: {}'.format(i)
    time.sleep(0.01)


# Send chained tasks
def main():
    celery_chains = []
    for i in range(N_ITS):
        print '[{}] - Dispatching tasks'.format(i)
        celery_chains.append(chain(task_1.si(i) | task_2.si(i))())
        time.sleep(0.02)

    # wait for all tasks to complete
    [c.get() for c in celery_chains]


if __name__ == '__main__':
    main()

如果需要,我也会给出芹菜的配置:

from __future__ import absolute_import

import os

name = 'live'

broker_url = 'pyamqp://{}'.format(os.environ.get('RMQ_HOST', 'localhost'))
print 'broker_url:', broker_url
include = ['live']

DEFAULT_QUEUE = 'celery'

# A named queue that's not already defined in task_queues will be created automatically.
task_create_missing_queues = True

broker_pool_limit = 10000

task_routes = {
    'live.task_1': {'queue': 'worker_1'},
    'live.task_2': {'queue': 'worker_2'}
}

# We always set the routing key to be the queue name so we do it here automatically.
for v in task_routes.values():
    v.update({'routing_key': v['queue']})

task_serializer = 'pickle'
result_serializer = 'pickle'
accept_content = ['json', 'pickle']
timezone = 'Europe/Paris'
enable_utc = True

对于经纪人,我使用docker image rabbitmq:3.6-alpine与基本配置appart,我启用了rabbitmq_management。

这将在以下工人职业计时表中重现:(颜色表示正在处理的数据的索引,因此您可以链接属于同一链的任务)

enter image description here

正如您所看到的,通常一切顺利,任务1在任务1完成后立即调用。但是,有时(图中箭头所示)任务2即使工作人员2没有被占用也不会立即开始。它推迟了27ms的延迟,这是单个任务持续时间的两倍多。这在执行期间大约每2秒发生一次。

我使用firehose进行了一些额外的调查,以研究rabbitmq中的消息交换,结果是消息有效地按时发送。根据我的理解,工作人员等待获取消息并处理任务,但我无法理解为什么。

我尝试将代理池限制设置为较高的数字,但问题仍然存在。

0 个答案:

没有答案