{"id":23498364,"url":"https://github.com/cucumberian/tutorial_celery","last_synced_at":"2026-04-09T20:44:47.317Z","repository":{"id":225036627,"uuid":"764914435","full_name":"cucumberian/tutorial_celery","owner":"cucumberian","description":"celery","archived":false,"fork":false,"pushed_at":"2024-05-16T17:14:49.000Z","size":13,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-16T15:18:27.483Z","etag":null,"topics":["celery","django","docker","docker-compose","tutorial"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cucumberian.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-02-28T23:54:19.000Z","updated_at":"2024-05-16T17:14:53.000Z","dependencies_parsed_at":"2024-02-29T00:43:39.793Z","dependency_job_id":"9f6030c6-969d-43fa-9ba7-730e079cb473","html_url":"https://github.com/cucumberian/tutorial_celery","commit_stats":null,"previous_names":["cucumberian/tutorial_celery"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cucumberian%2Ftutorial_celery","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cucumberian%2Ftutorial_celery/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cucumberian%2Ftutorial_celery/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cucumberian%2Ftutorial_celery/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cucumberian","download_url":"https://codeload.github.com/cucumberian/tutorial_celery/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250348872,"owners_count":21415907,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["celery","django","docker","docker-compose","tutorial"],"created_at":"2024-12-25T05:31:29.252Z","updated_at":"2026-04-09T20:44:47.301Z","avatar_url":"https://github.com/cucumberian.png","language":"Python","readme":"# Celery\n\n## Часть 1\n\n[статья](https://habr.com/ru/articles/770020/)\n\n### Добавление задачи в очередь\n\n```python\nimport time\nfrom celery import Celery\n\napp = Celery(\"myapp\", broker=\"pyamqp://quest@192.168.0.122//\")\n\n\n@app.task\ndef generate_report_task(arg1, arg2):\n    print(\"Start generating report\")\n    time.sleep(10)\n    print(\"Report generated\")\n\n\narg_value_1 = \"value1\"\narg_value_2 = \"value2\"\n\ngenerate_report_task.apply_async(\n    args=[\n        arg_value_1,\n    ],\n    kwargs={\"arg2\": arg_value_2},\n)\n```\n\n`.apply_async` - добавление задачи в очередь для выполнения. В данном случае, задача `generate_report_task` будет выполнена асинхронно с переданными аргументами.\n\n### Выполнение задачи через час\n\n- самый простой способ - аргумент `countdown` в `apply_async`.\n\n```python\n@app.task\ndef publish_article(arg1, arg2):\n    print(f\"Publish time: {datetime.datetime.now()}\")\n\npublish_article_after = 60 * 60  # 60 минут\nresult = publish_article.apply_async(\n    kwargs={\"arg1\": \"article title\", \"arg2\": \"article body\"},\n    countdown=publish_article_after,\n)\n```\n\n\u003e __Важно для Redis Backend__\nДанный способ не подойдет, если вы используете Redis в качестве брокера. Дело в том, что Redis помещает отложенные задачи в очередь `unacked`, из которой по истечение времени, указанного в аргументе `VISIBILITY_TIMEOUT`, задача будет назначена еще одному обработчику. Например, `countdown` у нас равен 120 минутам, а `VISIBILITY_TIMEOUT` по умолчанию 60. В таком случае есть риск, что задача будет назначена сразу трём обработчикам (первому сразу, второму через 60 минут, третьему - если задача через 120 минут будет еще в очереди). В результате, мы получим выполнение одной и той же задачи несколько раз. Подробнее в документации [тут](https://docs.celeryq.dev/en/stable/getting-started/backends-and-brokers/redis.html#redis-caveats) и [тут](https://docs.celeryq.dev/en/latest/userguide/calling.html#eta-and-countdown).\n\n\u003e __Важно для RabbitMQ Backend__\nПараметр `consumer_timeout` по умолчанию равен 30 минутам. Не желательно устанавливать `countdown` больше этого времени, иначе будет возбуждено исключение `PRECONDITION_FAILED`. Если есть такая необходимость, необходимо увеличить время в `rabbitmq.conf`. Подробнее - [тут](https://docs.celeryq.dev/en/latest/userguide/calling.html#eta-and-countdown).\n\n### Выполнение задачи в определённое время\n\n- при использовании Redis отложенные задачи с помощью `eta` столкнутся с той же проблемой, что и `countdown` из-за `VISIBILITY_TIMEOUT`.\n\n- `eta` - это не точное время, в которое будет выполнена задача. Задача будет выполнена не раньше этого времени в порядке очереди.\n\n### Статусы задач\n\n#### Результат и статус задачи\n\nДля получения статуса задачи можно использовать её `id`.\n\n```python\n\ntask = publish_article.apply_async(kwargs={\"arg1\": 1, \"arg2\": 2})\ntask_id = task.id\n\n\ndef get_task_status_with_result(task_id: str):\n    task = AsyncResult(id=task_id)\n    return {\n        \"id\": task_id,\n        \"status\": task.status,\n        \"result\": TaskResponse.model_validate_json(task.result),\n    }\n```\n\n#### Статусы всех задач\n\n```python\nPENDING, STARTED, RETRY, FAILURE, SUCCESS, REVOKED\n```\n\nЧтобы получить статусы всех лучше знать и id. Далее можно опрашивать напрямую редис или через python-библиотеку celery.\n\nТакже можно использовать `celery_app.control.inspect`.\n\n- `python celery` клиент - самый лучший способ, но надо знать ид задачи\n- `redis` - redis не гарантирует стабильный формат ключей и данных\n- `flower api` - можно получить статусы задач через рест апи.\n- `celery.inspect` - общается с активными воркерами и показывает только те задачи, которые известны воркерам. Т.е. не показывает завершенные задачи (`SUCCESS` `FAILURE`).\n\nПример получения задач через inpect:\n\n```python\ni = app.control.inspect()\nactive = i.active()       # Выполняющиеся задачи\nreserved = i.reserved()   # взяты воркером из брокера и ожидающие выполнения\nscheduled = i.scheduled() # отложенные задачи\nrevoked = i.revoked()     # отмененные задачи\n```\n\nДанные запрос через inspect довольно длительный и работает только с активными воркерами.\n\n## Celery beat\n\n[link](https://habr.com/ru/articles/820073/)\n\nОсновные компоненты:\n\n- __планировщик (Scheduler)__ - управляет периодическими задачами. Проверяет расписание и отправляет задачи в очередь в нужное время.\n\n- __рабочие узлы (worker nodes)__ - забирают задачи и выполняют их. Каждый узел может выполнять множество задач параллельно.\n\n- __посредник (broker)__ - используется для передачи сообщений между планировщиком и рабочими узлами. Управляет очередями и доставляет сообщения.\n\n\u003e Мы можем использовать стандартный планировщик или подключить другой. Вот два сторонних планировщика для примера:\n\n- [DatabaseScheduler](https://github.com/celery/django-celery-beat/blob/main/django_celery_beat/schedulers.py) из django-celery-beat. Хранит расписание в базе данных.\n\n- [RedBeatScheduler](https://github.com/sibson/redbeat/tree/main) из RedBeat. Хранит расписание в Redis.\n\n### Настройка\n\n```python\nimport logging\nimport time\nimport datetime\nfrom celery import Celery\nfrom celery.signals import after_setup_logger\n\nlogger = logging.getLogger(__name__)\n\napp = Celery(\"tasks\", broker=\"pyamqp://quest@localhost//\")\n\napp.conf.update(\n    task_serializer=\"json\",\n    accept_content=[\"json\"],\n    timezone=\"UTC\",\n    enable_utc=True,\n    worker_hijack_root_logger=False,    # переопределяем настройки логирования\n)\n\napp.autodiscover_tasks()\n\n\n@after_setup_logger.connect\ndef setup_loggers(logger, *args, **kwargs):\n    formatter = logging.Formatter(\n        \"%(asctime)s - %(name)s - %(levelname)s - %(message)s\"\n    )\n    fh = logging.FileHandler(\"logs.log\")\n    fh.setFormatter(formatter)\n    logger.addHandler(fh)\n\n\n@app.task\ndef test():\n    logger.info(\"Its working\")\n    return True\n\n\napp.conf.beat_schedule = {\n    \"test\": {\n        \"task\": \"celery_app.test\",\n        \"schedule\": datetime.timedelta(seconds=10),\n    }\n}\n```\n\nВ `beat_schedule` мы передаем словарь с настройками расписания для задач.\n\nПолный список возможных параметров\n\n- `task`: Имя задачи в формате строки. Например, `'celery_app.test'`\n\n- `schedule`: Объект, определяющий расписание выполнения задачи.\nНапример, `timedelta(seconds=10), crontab(minute='*/5')`.\n\n- `args`: Список или кортеж с позиционными аргументами для задачи. Например, `(1, 2, 3)`.\n\n- `kwargs`: Словарь с именованными аргументами для задачи. Например, `{\"foo\": \"bar\"}`.\n\n- `options`: Словарь с дополнительными параметрами выполнения задачи. Принимает всё, что поддерживает `apply_async()`. Например, `{\"queue\": \"default\", \"priority\": 10}`.\n\n- `relative`: Флаг, указывающий на использование относительного расписания. Например, `True`.\n\nВ `schedule` мы передаем объект `timedelta`. Это основной способ, с помощью которого мы будем указывать временной интервал для задач. Его альтернатива - `crontab`. С ним бы было вот так:\n\n```python\napp.conf.beat_schedule = {\n    \"test\": {\n        \"task\": 'celery_app.test',  # путь к задаче\n        'schedule': crontab(hour=8, minute=0),  # Ежедневно в 8 утра\n    }\n}\n```\n\nМожно вместо `beat_schedule` использовать `add_periodic_task`. Это позволяет добавлять задачи динамически. [документация](https://docs.celeryq.dev/en/stable/userguide/periodic-tasks.html)\n\n```python\n@celery_app.on_after_finalize.connect\ndef setup_periodic_tasks(sender: Celery, **kwargs):\n    sender.add_periodic_task(\n        timedelta(seconds=5),\n        run.s(),\n        name=\"run scheduler service every 5 seconds\",\n    )\n```\n\n### Запуск периодических задач\n\nТеперь пора перейти непосредственно к запуску. У нас есть возможность использовать две разных команды:\n\n```sh\ncelery -A celery_app worker -B --loglevel=INFO\n```\n\n```sh\ncelery -A celery_app beat --loglevel=INFO\n```\n\n__Первая команда__ запускает рабочий узел, который одновременно будет являться и планировщиком. Эта команда лучше всего подходит для отладки и не рекомендуется для запуска в production среде. Дело в том, что в этом случае на работу планировщика могут повлиять выполняемые задачи, что может привести к сбоям.\n\n__Вторая команда__ запускает только планировщик. В таком случае он занимается только назначением задач в нужную очередь и не занимается выполнением задач. Такая схема работы более надёжна. Для того, чтобы задачи начали выполняться, нам понадобится запустить worker отдельно.\n\nСам рабочий узел мы будем запускать с помощью команды:\n\n```sh\ncelery -A celery_app worker\n```\n\nЗапускаем beat в первом терминале, worker во втором.\n\n## Django + Celery\n\n[статья](https://habr.com/ru/companies/otus/articles/503380/)\n\n1. https://habr.com/ru/companies/otus/articles/503380/  (https://github.com/testdrivenio/django-celery)\n2. https://realpython.com/asynchronous-tasks-with-django-and-celery/\n\nCelery это отдельная очередь задач, которая может собирать, получать, планировать и выполнять задачи вне основной программы.\nЧтобы получить и отдавать готовые задачи celery нужен брокер сообщений для коммуникации.\nОбычно вместе с Celery используется Redis и RabbitMQ.\n\n- Celery workers - это рабочие процессы, которые выполняют задачи независимо вне основной программы.\n\n- Celery beat - планировщик, который определяет когда запускать задачи.\n\n### Установка\n\n```shell\npip3 install django\n...\npip3 install celery\npip3 install redis\n```\n\nТеперь можно запустить worker командой `celery worker`. Но получим сообщение об ошибке, что celery не может работать с брокером сообщений.\nCelery будет безуспешно пытаться подключиться к локальному хосту по протоколу amqp - advanced message queuing protocol (https://en.wikipedia.org/wiki/Advanced_Message_Queuing_Protocol).\n\n#### Redis\n\nУстановим redis-server\n\n```shell\nsudo apt update\nsudo apt install redis\n```\n\nКонечно можно ставить отдельно в виде докер-контейнера.\n\nМожно запустить redis-server\n\n```shell\nredis-server\n```\n\nПроверить работает ли redis\n\n```shell\nps aux | grep redis\n```\n\nОстановить\n\n```shell\nsudo service redis-server stop\n```\n\nПингануть\n\n```shell\nredis-cli ping\n```\n\nЗапустить клиент\n\n```shell\nredis-cli\n```\n\nУстановим питоновский клиент\n\n```shell\npip install redis\npip install celery\npip install flower\n```\n\nТ.е. нам нужен редис сервер, как отдельное приложение и пакет для питоновских программ для работы с ним.\n\n### Добавление celery у джанго проекту\n\nСоздадим файл `celery.py` в папке корневого приложенияЮ рядом c `settings.py`.\n\n```python\n# django_celery/celery.py\nimport os\nfrom celery import Celery\n\nos.environ.setdefault(\"DJANGO_SETTINGS_MODULE\", \"django_celery.settings\")\n\napp = Celery(\"django_celery\")\n# app = Celery(\"hello\", backend=\"redis://localhost:6379\", broker=\"pyamqp://quest@127.0.0.1:6379/\")\napp.config_from_object(\"django.conf.settings\", namespace=\"CELERY\")\napp.autodiscover_tasks()\n\n\n@app.task\ndef add(x, y):\n    return x + y\n```\n\nЗдесь мы устанавливаем переменную окружения, чтобы получить модуль джанго `project_name.settings.py` через переменную окружения `DJANGO_SETTINGS_MODULE`.\n\nЗатем создаём экземпляр приложения Celery и передаём внутрь имя нашего приложения (главного модуля).\n\nДалее мы задаем путь до файла настроек и имя неймспейса с настройками celery. В файле конфигурации `settings.py` все настройки начинающиеся с `CELERY_` будут прочтены этим приложением. При желании можно определить и другой файл конфигурации.\n\nЧерез автодисковер мы говорим приложению celery искать задачи в каждом приложении джанго.\n\nДалее добавляем настройки для celery в `settings.py`:\n\n```python\n# settings.py\n\n# Celery settings\nCELERY_BROKER_URL = \"redis://localhost:6379/0\"\nCELERY_RESULT_BACKEND = \"redis://localhost:6379/0\"\n```\n\nДанные строки дают инстансу celery достаточно информации, чтобы понять куда отправлять сообщения и куда записывать результат.\nЗаметим, что начинаются эти строки на имя `CELERY_`, где название `CELERY` задается как namespace в файле `celery.py` в строке `app.config_from_object(\"django.conf:settings\", namespace=\"CELERY\")`.\n\nДобавим celery.app в загрузку модуля через файл `main_app/__init__.py`:\n\n```python\n# __init__.py\n\nfrom .celery import app as celery_app\n\n__all__ = (\"celery_app\", )\n```\n\nзапуск приложения `celery_app` при запуске джанго будет гарантировать нам, что декоратор `@shared_task` будет использовать его корректно.\n\nТеперь можно протестировать приложение. Напомним что наша связка с celery-django будет состоять из трёх модулей:\n\n- producer - приложение джанго\n- message-broker - сервер редис\n- consumer - приложение celery_app в джанго\n\n#### Запуск\n\n- Запускаем сервер redis  `redis-server` если еще не запущен как сервис или в докере\n- запускаем джанго `python manage.py runserver`\n- запускаем воркер `python -m celery -A django_celery worker --loglevel=INFO`\n  При запуске воркера передаём celery имя нашего джанго модуля в котором есть инстанс Celery.\n\n  - `-A` = `--app=`\n  - `-l` = `--loglevel=`\n  - `-b` = `--broker=`\n\n  Можно явно указать инстанс Celery:\n  \n  ```shell\n  python -m celery --app=django_celery:celery_app worker --loglevel=INFO\n  ```\n\n- запускаем flower на порту 5555\n\n```shell\npython -m celery --broker=redis://127.0.0.1:6379/0 flower -A django_celery --port=5555\n```\n\nили если подтягиваем настройки для фловера из аппы\n\n```shell\npython -m celery -A django_celery flower --port=5555\n```\n\n#### Использование\n\nДля использование надо в тексте программы добавить задачу для воркера\n\n```python\ntask = add.delay(1, 2)\n```\n\n, где `add` - функция задекорированная `@celery_app.task` - специальным декоратором от инстанса Celery, который сы создали в `celery.py`.\n\nПолучить статус задачи и результат выполнения:\n\n```python\nprint(task.result)\nprint(task.status)\n```\n\nВ случае если работа ведётся с базой данных, например создаваться объект, сохраняется и потом добавляется задача, то может случиться, что воркер получит задачу и начнёт её выполнять, а значения в базе данных ещё не будет.\nТогда лучше запускать воркер после [транзакции](https://docs.djangoproject.com/en/5.0/topics/db/transactions/) в базу данных, например так:\n\n```python\nfrom django.db import transaction\n\ntransaction.on_commit(lambda: some_celery_task.delay(obj.id))\n```\n\nПо самому celery. Уже много раз обсуждалось и везде предупреждают, но повторю — не используйте в качестве аргументов для тасков сложные объекты, например модели django. Передавайте лучше id и уже в таске получайте объект из БД. Ещё один важный момент, который может смутить начинающего разработчика на django — вьюхи, как правило, выполняются в транзакции. Это может привести к тому, сохранив новый объект и сразу отправь его id в таск вы можете получить object not found. Чтобы такого избежать, нужно использовать конструкцию типа `transaction.on_commit(lambda: some_celery_task.delay(obj.id))`\n\nТак же можно смотреть текущие задачи и их статусы с помощью\n\n```shell\npython -m celery -A worker events\n```\n\n##### Django\n\nВот пример использования в django:\n\n```python\n# для запуска после транзакции\nfrom django.db import transaction\n# для декорирования задачи\nfrom django_cel.celery import app as celery_app\n\nclass SimpleView(View):\n    def post(self, request):\n        value = request.POST.get(\"value\")\n        if value:\n            simple = Simple(value=value)\n            simple.save()\n            # отдаём задачу воркеру после выполнения транзакции,\n            # когда объект уже будет создан\n            transaction.on_commit(lambda: simple_task.delay(simple.id))\n            return JsonResponse({\"id\": simple.id})\n        return JsonResponse({\"error\": \"Value is required\"})\n\n# регистрируем задачу для воркера\n@celery_app.task\ndef simple_task(simple_id):\n    print(f\"Simple task started with id {simple_id}\")\n    simple = Simple.objects.get(id=simple_id)\n    simple.result = len(Simple.objects.all())\n    simple.is_completed = True\n    simple.save()\n    print(f\"Simple task finished with id {simple_id}\")\n    return simple_id\n```\n\nЕсли celery не может найти задачу (ошибка Not registered), то просто перезапустите celery.\n\n### Конструкции celery\n\n```python\nfrom celery import Celery\nfrom celery import shared_task\n...\ncelery_app = Celery(\n    \"project-name\",\n    broker=\"redis://localhost:6379/0\",  # можно задать потом\n    backend=\"redis://localhost:6379/0\", # можно задать потом    \n)\ncelery_app.autodiscover_tasks(force=True)\n...\n\n# регистрация таски\n@celery_app.task\ndef add(x, y):\n    return x + y\n\n# регистрация shared_task\n@shared_task\ndef sub(x, y):\n    return x - y\n\ntask_sub = sub.delay(2, 1)\n\ntask = add.delay(1, 2)\nwhile not task.ready():\n    pass\nprint(task.get())\n```\n\n#### `@shared_task` vs `@app.task`\n\nhttps://docs.celeryq.dev/en/stable/userguide/tasks.html\n\nПри использовании shared_task нет необходимости икспортировать экземпляр `Celery`.\nТакже можно использовать `@app.task(shared=True)`.\nВ случае если есть несколько экземпляров Celery\n\n```python\napp1 = Celery()\n\napp2 = Celery()\n\n@app1.task\ndef test():\n    pass\n```\n\n, то __таска__ `test` будет зарегистрирована в обоих иснтансах Celery, но __имя__ `test` будет относиться только к app1.\nОднако `@shared_task` позволяет использовать таск в обоих инстансах.\n\n#### bind=True\n\nТакие задачи первым аргументом принимают себя и позволяют повторять себя при необходимости повторов.\n\n```python\nlogger = get_task_logger(__name__)\n\n@app.task(bind=True)\ndef add(self, x, y):\n    logger.info(self.request.id)\n```\n\nBound tasks are needed for retries (using app.Task.retry()), for accessing information about the current task request, and for any additional functionality you add to custom task base classes.\n\n#### delay\n\n`task.delay()` - это метод который является псевдонимом более мощного метода `.apply_async()`, у которого есть опции выполнения.\n\n```python\n@shared_task\ndef add_task(x, y):\n    return x + y\n\nadd_task.apply_async(\n    args=[1, 2]\n)\n```\n\nХотя для многих простых случаев использование `delay` является предпочтительнее, использование метода `apply_async` иногда оправдано, например со счётчиками или повторами.\n\n#### tasks.py\n\nМожно задать выполняемые задачи в файле tasks.py.\n\n```python\n#tasks.py\nfrom celery import shared_task\n\n\n@shared_task\ndef hello():\n    print(\"Hello Celery\")\n\n@shared_task\ndef add(x, y):\n    return x + y\n```\n\n### docker-compose\n\n```shell\nservices:\n  redis:\n    image: redis:7.2.4-alpine3.19\n    # ports:\n    #   - 6379:6379\n  \n  postgres:\n    image: postgres:13.14-alpine3.19\n    restart: always\n    # ports:\n    #   - \"5444:5432\"\n    env_file: .env\n    environment:\n      - POSTGRES_USER=$POSTGRES_USER\n      - POSTGRES_PASSWORD=$POSTGRES_PASSWORD\n      - POSTGRES_DB=$POSTGRES_DB\n    volumes:\n      - postgresql_volume:/var/lib/postgresql/data\n\n  django:\n    build:\n      context: celery2\n      dockerfile: Dockerfile\n    command: sh -c \"python manage.py makemigrations \u0026\u0026 python manage.py migrate --noinput \u0026\u0026 python manage.py runserver 0.0.0.0:8000\"\n    ports:\n      - 8080:8000\n    env_file: .env\n    environment:\n      - DJANGO_SECRET_KEY=${DJANGO_SECRET_KEY}\n      - CELERY_BROKER_URL=redis://redis:6379/0\n      - CELERY_RESULT_BACKEND=redis://redis:6379/0\n      - POSTGRES_HOST=postgres\n      - POSTGRES_PORT=5432\n    depends_on:\n      - redis\n      - postgres\n\n    \n  worker:\n    build:\n      context: celery2\n      dockerfile: Dockerfile\n    command: python -m celery -A celery2:celery_app worker\n    env_file: .env\n    environment:\n      - DJANGO_SECRET_KEY=${DJANGO_SECRET_KEY}\n      - CELERY_BROKER_URL=redis://redis:6379/0\n      - CELERY_RESULT_BACKEND=redis://redis:6379/0\n      - POSTGRES_HOST=postgres\n      - POSTGRES_PORT=5432\n    depends_on:\n      - redis\n      - postgres\n  \n  flower:\n    build:\n      context: celery2\n      dockerfile: Dockerfile\n    command: python -m celery -A celery2 flower --port=5555\n    ports:\n      - \"5555:5555\"\n    env_file: .env\n    environment:\n      - DJANGO_SECRET_KEY=${DJANGO_SECRET_KEY}\n      - CELERY_BROKER_URL=redis://redis:6379/0\n      - CELERY_RESULT_BACKEND=redis://redis:6379/0\n      - POSTGRES_HOST=postgres\n      - POSTGRES_PORT=5432\n    depends_on:\n      - redis\n      - postgres\n\n\n\nvolumes:\n  postgresql_volume:\n    name: postgresql_volume\n```\n\nЧтобы изменить количество запущенных контейнеров с воркерами можно воспользоваться командой\n\n```shell\ndocker-compose up -d --build --scale worker=3\n```\n\n### Celery для произвольного проекта\n\nЗадача - запустить асинхронный расчет хэша от строки.\nТ.к. задача асинхронная, то выполнять её можно в отдельном процессе.\nА в главной программе мы будем асинхронно ожидать выполнения этого процесса через `asyncio.sleep`.\n\n1. Создаем экземпляр celery приложения в файле `celery_app.py`\n\n    ```python\n    import time\n    import hashlib\n    from celery import Celery\n\n    celery_app = Celery(\n        \"tasks\", broker=\"redis://localhost:6379/0\", backend=\"redis://localhost:6379/0\"\n    )\n\n    celery_app.conf.broker_url = Config.CELERY_BROKER_URL\n    celery_app.conf.result_backend = Config.CELERY_RESULT_BACKEND\n    celery_app.conf.update(result_expires=3600)\n\n    @celery_app.task\n    def calc_hash(string: str) -\u003e str:\n        time.sleep(10)\n        hash_str = hashlib.sha256(string.encode()).hexdigest()\n        return hash_str\n    ```\n\n2. Используем эту задачу в коде:\n\n    ```python\n    import asyncio\n    from celery_app import celery_hash\n\n    async def calc_hash(string: str) -\u003e str:\n    \"\"\"\n    Асинхронный расчёт хэша в отдельном процессе Celery воркера\n    \"\"\"\n    task = celery_hash.delay(string=string)\n    while not task.ready():\n        asyncio.sleep(0.1)\n    return task.result\n    ```\n\n3. Запускаем воркера\n\n    ```shell\n    celery -A celery_app worker --loglevel=INFO\n    ```\n\n4. Запускаем наше приложение\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcucumberian%2Ftutorial_celery","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcucumberian%2Ftutorial_celery","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcucumberian%2Ftutorial_celery/lists"}