<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>RabbitMQ &#8212; ploshadka.net</title>
	<atom:link href="https://ploshadka.net/tag/rabbitmq/feed/" rel="self" type="application/rss+xml" />
	<link>https://ploshadka.net</link>
	<description>Мир интернет технологий</description>
	<lastBuildDate>Fri, 18 Nov 2022 19:49:36 +0000</lastBuildDate>
	<language>ru-RU</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.1</generator>
	<item>
		<title>Flask: Celery + RabbitMQ</title>
		<link>https://ploshadka.net/flask-celery-rabbitmq/</link>
					<comments>https://ploshadka.net/flask-celery-rabbitmq/#respond</comments>
		
		<dc:creator><![CDATA[Admin]]></dc:creator>
		<pubDate>Sun, 03 Jan 2021 18:47:59 +0000</pubDate>
				<category><![CDATA[Flask]]></category>
		<category><![CDATA[celery]]></category>
		<category><![CDATA[Homebrew]]></category>
		<category><![CDATA[RabbitMQ]]></category>
		<category><![CDATA[Основательные труды]]></category>
		<guid isPermaLink="false">https://ploshadka.net/?p=7203</guid>

					<description><![CDATA[Полное руководство по настройки очередей задач на Flask через Celery и RabbitMQ. В Интернете сложно найти полноценное описание настройки Celery через RabbitMQ и Flask. В основном попадаются примеры на Redis и Django. В этой статье попробую это исправить и расскажу...]]></description>
										<content:encoded><![CDATA[<p>Полное руководство по настройки очередей задач на <strong>Flask через Celery и RabbitMQ</strong>. <span id="more-7203"></span></p>
<p>В Интернете сложно найти полноценное описание настройки <strong>Celery</strong> через <strong>RabbitMQ</strong> и <strong>Flask</strong>. В основном попадаются примеры на <strong>Redis</strong> и <strong>Django</strong>. В этой статье попробую это исправить и расскажу о полной настройке и запуске <strong>Celery на Flask</strong>.</p>
<h2>Что такое очереди задач?</h2>
<p>Это что-то вроде <strong>cron-задач</strong>, только более продвинутое использование. <strong>Celery</strong> позволяет запускать задачи не перегружая процессор (умеет распределять нагрузку). В отличии от cron задач celery может запускать задачи параллельно. Также имеется обработчики ошибок и легко следить за результатом.</p>
<p>В Celery встроен свой cron-обработчик. С этим мы получаем все преимущества celery и одновременно можем запланировать события по типу cron. </p>
<h2>Зачем нужен RabbitMQ?</h2>
<p>Из-за объема информацию <a href="https://ploshadka.net/rabbitmq-ustanovka-i-upravleniem-brokerom-soobshhenijj/">RabbitMQ вынесен в отдельную статью</a>. </p>
<h2>А нужны ли вам очереди задач?</h2>
<p>Очереди задач довольно замороченная штука. Вы можете увидеть это посмотрев на объем текущей статьи. Я постарался учесть большую часть для того, чтобы все настроить, но описано тут далеко не все.</p>
<p>А что делает в конечном счете <strong>celery</strong>? Если отбросить нюансы, он делает то же самое что и <strong>cron</strong>, но требует гораздо больше дополнительных сущностей, зависимостей и настроек. </p>
<p>Конечно с этой статьей учиться будет быстрее, но никто не гарантирует что вы не столкнетесь с другими проблемами или задачами не описанными здесь. Или допустим у вас будет не RabbitMQ, а Redis или не Flask, а Django. Тогда придется разбираться во многих других нюансах самостоятельно.</p>
<p>Возможно вы сможете <a href="https://ploshadka.net/kak-zapustit-skripty-python-cherez-cron-na-linux/">обойтись cron-ом на питоне</a>.</p>
<p>Или может быть вам подойдет <a href="https://ploshadka.net/python-planirovanie-zadach-cherez-timer/">зацикливание таймером (timer)</a>? Несмотря на простоту таймера есть в нём одно большое преимущество: функция не будет выполнена второй раз прежде чем она завершится. Однако это можно решить с помощью кэша, об этом будет в этой статье.</p>
<h2>Установка Celery на Flask</h2>
<p>Установка:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">pip <span style="color: #c20cb9; font-weight: bold;">install</span> celery</div></div>
<p>Настройка &#171;очередей задач&#187; будет на той же архитектуре, что рассказывается в цикле основных статей в <a href="https://ploshadka.net/python-i-flask-ot-lokalnojj-razrabotki-do-servernojj-raskatki/">архитектуре проекта на Flask</a>.</p>
<p>Ниже приведены 2 конфигурации: та к которой я пришел и та которая описана в примерах на Flask.</p>
<h2>Моя конфигурация</h2>
<h3>__init__.py</h3>
<p>Это основной файл инициализации, который запускается вместе с запуском приложения на Flask. Смотрите <a href="https://ploshadka.net/python-delaem-prilozhenie-na-flask-v-lokalnojj-srede/#h2-2">структуру проекта на Flask</a>. В этом же файле <a href="https://ploshadka.net/flask-konfiguracionnye-fajjly/">настраиваются конфигурационные файлы Flask</a>.</p>
<p>Расположение файла</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">app/__init__.py</div></div>
<p>В функцию <strong>create_app</strong> (основная функция создания приложения на Flask) добавим новые конфигурационные файлы:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">def</span> create_app<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; app <span style="color: #66cc66;">=</span> Flask<span style="color: black;">&#40;</span>__name__<span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; app.<span style="color: black;">config</span>.<span style="color: black;">from_pyfile</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'../configs/private/celery.py'</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; app.<span style="color: black;">config</span>.<span style="color: black;">from_pyfile</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'../configs/beatschedule.py'</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> app<br />
<br />
app <span style="color: #66cc66;">=</span> create_app<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div>
<h3>beatschedule.py</h3>
<p>Расположение файла:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">configs/beatschedule.py</div></div>
<p>Это конфигурационный файл в котором будут планироваться задачи. Его содержимое:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">imports <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">'tasks.updates'</span><span style="color: #66cc66;">,</span><span style="color: black;">&#41;</span><br />
task_default_queue <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'ploshadka_queue'</span><br />
<br />
BEAT_SCHEDULE <span style="color: #66cc66;">=</span> <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #483d8b;">'updates'</span>: <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'options'</span>: <span style="color: black;">&#123;</span><span style="color: #483d8b;">'queue'</span>: <span style="color: #483d8b;">'ploshadka_queue'</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'task'</span>: <span style="color: #483d8b;">'task_update'</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'schedule'</span>: <span style="color: #ff4500;">30.0</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; <span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
<span style="color: black;">&#125;</span></div></div>
<h3>celery.py</h3>
<p>Расположение файла:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">configs/private/celery.py</div></div>
<p>Это приватный файл для сельдерея. Локальный конфиг будет отличаться от конфига продакшена. Этот конфиг обязательно должен быть добавлен в исключении файла <strong>.gitignore</strong>.</p>
<p>Его содержимое для <a href="https://ploshadka.net/ustanovka-i-podkljuchenie-postgresql-na-mac-os/">PostgreSQL</a> для локальной среды:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">from</span> configs.<span style="color: black;">beatschedule</span> <span style="color: #ff7700;font-weight:bold;">import</span> *<br />
<br />
broker_url <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'pyamqp://guest@localhost//'</span><br />
result_backend <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'db+postgresql+psycopg2://localhost/ploshadka_db'</span><br />
beat_schedule <span style="color: #66cc66;">=</span> BEAT_SCHEDULE</div></div>
<p>также будет работать:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">broker_url = 'pyamqp://localhost'</div></div>
<p>Для прода в <strong>result_backend</strong> добавляем пользователя БД и пароль, остальное аналогично тому что выше:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">from</span> configs.<span style="color: black;">beatschedule</span> <span style="color: #ff7700;font-weight:bold;">import</span> *<br />
<br />
broker_url <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'pyamqp://guest@localhost//'</span><br />
result_backend <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'db+postgresql+psycopg2://&lt;пользователь&gt;:&lt;пароль&gt;@localhost/ploshadka_db'</span><br />
beat_schedule <span style="color: #66cc66;">=</span> BEAT_SCHEDULE</div></div>
<p>В старых инструкциях вместо <strong>result_backend</strong> указана константа <strong>CELERY_RESULT_BACKEND</strong>, но сейчас она считается <strong>deprecated</strong> и <a href="https://ploshadka.net/oshibka-the-celery_result_backend-setting-is-deprecated-and-scheduled-for-removal-in/">будет в последствии удалена</a>.</p>
<p>В результате этой настройки брокер сообщений создаст две таблицы в БД, в которых будет хранить свои данные:</p>
<p><a href="https://ploshadka.net/wp-content/uploads/7203/celery.jpg" rel="lightbox-0"><img wpfc-lazyload-disable="true" decoding="async" loading="lazy" class="aligncenter size-full wp-image-7211" src="https://ploshadka.net/wp-content/uploads/7203/celery.jpg" alt="" width="250" height="50" srcset="https://ploshadka.net/wp-content/uploads/7203/celery.jpg 482w, https://ploshadka.net/wp-content/uploads/7203/celery-300x59.jpg 300w" sizes="(max-width: 250px) 100vw, 250px" /></a></p>
<p><a href="https://ploshadka.net/wp-content/uploads/7203/celery2.jpg" rel="lightbox-1"><img wpfc-lazyload-disable="true" decoding="async" loading="lazy" class="aligncenter size-medium wp-image-7212" src="https://ploshadka.net/wp-content/uploads/7203/celery2-600x81.jpg" alt="" width="600" height="81" srcset="https://ploshadka.net/wp-content/uploads/7203/celery2-600x81.jpg 600w, https://ploshadka.net/wp-content/uploads/7203/celery2-1200x162.jpg 1200w, https://ploshadka.net/wp-content/uploads/7203/celery2-1536x207.jpg 1536w, https://ploshadka.net/wp-content/uploads/7203/celery2-300x40.jpg 300w, https://ploshadka.net/wp-content/uploads/7203/celery2.jpg 1920w" sizes="(max-width: 600px) 100vw, 600px" /></a></p>
<h3>Задачи</h3>
<p>Расположение задач:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">tasks<br />
--__init__.py<br />
--updates.py</div></div>
<p>Внутри файла updates.py:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">from</span> celery <span style="color: #ff7700;font-weight:bold;">import</span> Celery<br />
celery <span style="color: #66cc66;">=</span> Celery<span style="color: black;">&#40;</span><span style="color: #483d8b;">'updates'</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #66cc66;">@</span>celery.<span style="color: black;">task</span><span style="color: black;">&#40;</span>name<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'task_update'</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> task_update<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #483d8b;">&quot;task_update: success&quot;</span></div></div>
<p><strong>name=&#8217;task_update&#8217;</strong> должно равняться имени <strong>&#8216;task&#8217;: &#8216;task_update&#8217;</strong> в <strong>BEAT_SCHEDULE</strong>.</p>
<h3>Запуск</h3>
<p>Обязательно запускать с опцией конфига. Обо всех этих и других командах (и опциях для них) будет описано подробнее ниже в текущей статье.</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery <span style="color: #660033;">-A</span> tasks.updates.celery <span style="color: #660033;">--config</span>=configs.private.celery beat <span style="color: #660033;">--detach</span> <span style="color: #660033;">--logfile</span>=logs<span style="color: #000000; font-weight: bold;">/</span>beat.log <span style="color: #660033;">-l</span> INFO<br />
<br />
celery <span style="color: #660033;">-A</span> tasks.updates.celery <span style="color: #660033;">--config</span>=configs.private.celery worker <span style="color: #660033;">-D</span> <span style="color: #660033;">--logfile</span>=logs<span style="color: #000000; font-weight: bold;">/</span>celery.log <span style="color: #660033;">-l</span> INFO <span style="color: #660033;">--purge</span></div></div>
<h2>Классическая конфигурация Flask</h2>
<p>Ближе к официальной доке для Flask, но использую то что выше, а это сохранил на память.</p>
<h3>__init__.py</h3>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">from</span> celery <span style="color: #ff7700;font-weight:bold;">import</span> Celery<br />
<br />
<span style="color: #ff7700;font-weight:bold;">def</span> make_celery<span style="color: black;">&#40;</span>app<span style="color: black;">&#41;</span>:<br />
celery <span style="color: #66cc66;">=</span> Celery<span style="color: black;">&#40;</span><br />
&nbsp; &nbsp; app.<span style="color: black;">import_name</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; broker<span style="color: #66cc66;">=</span>app.<span style="color: black;">config</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'CELERY_BROKER_URL'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; backend<span style="color: #66cc66;">=</span>app.<span style="color: black;">config</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'RESULT_BACKEND'</span><span style="color: black;">&#93;</span><span style="color: #66cc66;">,</span><br />
<span style="color: black;">&#41;</span><br />
celery.<span style="color: black;">conf</span>.<span style="color: black;">update</span><span style="color: black;">&#40;</span>app.<span style="color: black;">config</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Настройки для cron</span><br />
celery.<span style="color: black;">conf</span>.<span style="color: black;">beat_schedule</span> <span style="color: #66cc66;">=</span> app.<span style="color: black;">config</span><span style="color: black;">&#91;</span><span style="color: #483d8b;">'BEAT_SCHEDULE'</span><span style="color: black;">&#93;</span><br />
<br />
<span style="color: #ff7700;font-weight:bold;">return</span> celery</div></div>
<h3>config.py</h3>
<p>Доступы к серверу сообщений RabbitMQ пропишем внутри файла <strong>config.py</strong></p>
<p><strong>Для локалки подключение к PostgreSQL:</strong></p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #808080; font-style: italic;"># Celery</span><br />
CELERY_BROKER_URL<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'pyamqp://guest@localhost//'</span><br />
RESULT_BACKEND<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'db+postgresql+psycopg2://localhost/bd_name'</span></div></div>
<p><strong>На удаленном сервере:</strong></p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #808080; font-style: italic;"># Celery</span><br />
CELERY_BROKER_URL <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'pyamqp://localhost'</span><br />
RESULT_BACKEND <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'db+postgresql+psycopg2://user_name:password@localhost/bd_name'</span></div></div>
<h3>Cron настройки</h3>
<p>Если мы хотим запускать наши задачи через cron celery, то здесь же укажем настройки:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">imports <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">'tasks.updates'</span><span style="color: #66cc66;">,</span><span style="color: black;">&#41;</span><br />
task_default_queue <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'ploshadka_queue'</span><br />
<br />
BEAT_SCHEDULE <span style="color: #66cc66;">=</span> <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #483d8b;">'updates'</span>: <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'options'</span>: <span style="color: black;">&#123;</span><span style="color: #483d8b;">'queue'</span>: <span style="color: #483d8b;">'ploshadka_queue'</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'task'</span>: <span style="color: #483d8b;">'task_update'</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'schedule'</span>: <span style="color: #ff4500;">30.0</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; <span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
<span style="color: black;">&#125;</span></div></div>
<p>Вместо <strong>BEAT_SCHEDULE</strong> раньше было <strong>CELERYBEAT_SCHEDULE</strong>, но сейчас является устаревшей.</p>
<h3>Создаём задачи</h3>
<p>Под задачи отведем отдельную директорию, назовем её <strong>tasks</strong>. В ней создадим файл <strong>updates.py</strong>.</p>
<p>Внутри файла. Функция с аргументами:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">from</span> app <span style="color: #ff7700;font-weight:bold;">import</span> make_celery<span style="color: #66cc66;">,</span> app<br />
<br />
celery <span style="color: #66cc66;">=</span> make_celery<span style="color: black;">&#40;</span>app<span style="color: black;">&#41;</span><br />
<br />
<span style="color: #66cc66;">@</span>celery.<span style="color: black;">task</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> add<span style="color: black;">&#40;</span>a<span style="color: #66cc66;">,</span> b<span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> a + b<br />
<br />
add.<span style="color: black;">delay</span><span style="color: black;">&#40;</span><span style="color: #ff4500;">1</span><span style="color: #66cc66;">,</span> <span style="color: #ff4500;">5</span><span style="color: black;">&#41;</span></div></div>
<p>Метод <strong>delay</strong>() отвечает за запуск нашей задачи (функции add). </p>
<p>Функция с именем:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #66cc66;">@</span>celery.<span style="color: black;">task</span><span style="color: black;">&#40;</span>name<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'Update Balance'</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> celery_update_balance<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; update_deposit<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#123;</span><span style="color: #483d8b;">'status'</span>: <span style="color: #483d8b;">'Задача завершена!'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'result'</span>: <span style="color: #ff4500;">100</span><span style="color: black;">&#125;</span><br />
<br />
celery_update_balance.<span style="color: black;">delay</span><span style="color: black;">&#40;</span><span style="color: black;">&#41;</span></div></div>
<p>Функция для запуска по крону:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #66cc66;">@</span>celery.<span style="color: black;">task</span><span style="color: black;">&#40;</span>name<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'tasks.update_balance'</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> update_balance<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; update<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span><br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: black;">&#123;</span><span style="color: #483d8b;">'status'</span>: <span style="color: #483d8b;">'Задача завершена!'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'result'</span>: <span style="color: #ff4500;">100</span><span style="color: black;">&#125;</span></div></div>
<h2>Варианты конфига cron</h2>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #ff7700;font-weight:bold;">from</span> celery.<span style="color: black;">schedules</span> <span style="color: #ff7700;font-weight:bold;">import</span> crontab<br />
<br />
BEAT_SCHEDULE <span style="color: #66cc66;">=</span> <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #483d8b;">'updates'</span>: <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'options'</span>: <span style="color: black;">&#123;</span><span style="color: #483d8b;">'queue'</span>: <span style="color: #483d8b;">'ploshadka_queue'</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'task'</span>: <span style="color: #483d8b;">'task_update'</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'schedule'</span>: <span style="color: #ff4500;">30.0</span><span style="color: #66cc66;">,</span> &nbsp;<span style="color: #808080; font-style: italic;"># Раз в 30 секунд</span><br />
<br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #808080; font-style: italic;"># Другие варианты:</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #808080; font-style: italic;"># 'schedule': crontab(hour=5, minute=20, day_of_week=1),</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #808080; font-style: italic;"># 'schedule': crontab(*), # Раз в минуту</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span style="color: #808080; font-style: italic;"># 'args': (16, 16) # Аргументы у функции</span><br />
&nbsp; &nbsp; <span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
<span style="color: black;">&#125;</span></div></div>
<h2>Запуск задач вне крона (worker)</h2>
<p>а) Мы запустили наше приложении.<br />
б) Находимся внутри виртуального окружения python.</p>
<p>После этого запускаем:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery -A tasks.<span style="color: black;">updates</span>.<span style="color: black;">celery</span> worker -l info</div></div>
<p>где:<br />
tasks &#8212; директория<br />
updates &#8212; файл (модуль)<br />
-l info &#8212; вывести мониторинг в консоли</p>
<p>Эта команда запустит <strong>worker</strong> и выполнит функцию с <strong>delay</strong>. Если все настроено было верно в базе данных появится новая запись, а в консоли отобразится информация о ходе работ:</p>
<p><a href="https://ploshadka.net/wp-content/uploads/7203/celery-1.jpg" rel="lightbox-2"><img wpfc-lazyload-disable="true" decoding="async" loading="lazy" class="aligncenter size-medium wp-image-7219" src="https://ploshadka.net/wp-content/uploads/7203/celery-1-600x260.jpg" alt="" width="600" height="260" srcset="https://ploshadka.net/wp-content/uploads/7203/celery-1-600x260.jpg 600w, https://ploshadka.net/wp-content/uploads/7203/celery-1-1200x519.jpg 1200w, https://ploshadka.net/wp-content/uploads/7203/celery-1-1536x665.jpg 1536w, https://ploshadka.net/wp-content/uploads/7203/celery-1-300x130.jpg 300w, https://ploshadka.net/wp-content/uploads/7203/celery-1.jpg 1920w" sizes="(max-width: 600px) 100vw, 600px" /></a></p>
<p>Если в команду выше докинуть атрибут <strong>-B</strong>, то в этом файле одновременно будут запущены все задачи, как с кроном, так и без него.</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery -A tasks.<span style="color: black;">updates</span>.<span style="color: black;">celery</span> worker -B -l info</div></div>
<p>Предварительно надо запустить beat (об этом будет ниже).</p>
<p>Запуск воркера с -B использовать только для отладки, в противном случае задача может запускаться более одного раза. На боевом сервере команды запуска обычных задач и запланированных надо осуществлять отдельными командами. </p>
<h2>Запуск задач по крону (beat)</h2>
<div class="highlight">Есть важная особенность в работе планировщика <strong>celery beat</strong>, в которой сразу сложно разобраться.</p>
<p><em>Для запуска планировщика нужны две команды <strong>worker и beat</strong>. Важно запускать сначала worker, а потом beat. Иначе задача будет сделана 2 раза.</p>
<p>Задвоение задач можно избежать также, если запускать команды с очисткой очередей <strong>&#8212;purge</strong>.<br />
</em></div>
<p>Запускаем воркер:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery -A tasks.<span style="color: black;">updates</span>.<span style="color: black;">celery</span> worker --purge -l info</div></div>
<p>В другой вкладке:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery -A tasks.<span style="color: black;">updates</span>.<span style="color: black;">celery</span> beat --detach</div></div>
<p>где:<br />
<strong>detach</strong> &#8212; запуск в &#171;тихом&#187; (демон) режиме (без блокировки консоли). Для worker можно использовать вместо <strong>detach</strong> опцию <strong>-D</strong>.</p>
<p>worker тоже можно запускать с detach:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery -A tasks.<span style="color: black;">updates</span>.<span style="color: black;">celery</span> worker --purge -D<br />
celery -A tasks.<span style="color: black;">updates</span>.<span style="color: black;">celery</span> beat --detach</div></div>
<p>Сначала запускается worker и он начинает мониторинг и обработку очередей задач. Затем туда с помощью beat добавляются задачи, которые подхватываются и выполняются worker-ом.</p>
<h2>Запуск на сервере</h2>
<p>На сервере запуск можно сделать через <a href="https://ploshadka.net/zapusk-celery-kak-daemon/">демонов</a>. Но мне нравится запускать через <strong>detach</strong>. Так больше контроля в одном месте.</p>
<p>Если используется &#171;моя конфигурация&#187;, то запуск осуществляется:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery <span style="color: #660033;">-A</span> tasks.updates.celery <span style="color: #660033;">--config</span>=configs.private.celery beat <span style="color: #660033;">-l</span> INFO <span style="color: #660033;">--logfile</span>=logs<span style="color: #000000; font-weight: bold;">/</span>beat.log <span style="color: #660033;">--detach</span><br />
<br />
celery <span style="color: #660033;">-A</span> tasks.updates.celery <span style="color: #660033;">--config</span>=configs.private.celery worker <span style="color: #660033;">--purge</span> <span style="color: #660033;">-l</span> INFO <span style="color: #660033;">--logfile</span>=logs<span style="color: #000000; font-weight: bold;">/</span>celery.log <span style="color: #660033;">-D</span></div></div>
<p>Если используется &#171;классическая конфигурация Flask&#187;, то для beat:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery <span style="color: #660033;">-A</span> tasks.updates.celery beat <span style="color: #660033;">-l</span> INFO <span style="color: #660033;">--logfile</span>=logs<span style="color: #000000; font-weight: bold;">/</span>beat.log <span style="color: #660033;">--detach</span></div></div>
<p>А вот команда worker может не видеть всего что указано в <strong>make_celery</strong> и для этого надо прописать в ней все опции. При этом т.к. мы не создавали конфиг отдельный, то конфиг тоже указываем, но ссылаемся на модуль с задачей. Не знаю почему такие костыли, но только так оно работает в этом случае:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery <span style="color: #660033;">-A</span> tasks.updates.celery <span style="color: #660033;">--config</span>=tasks.updates worker <span style="color: #660033;">--purge</span> <span style="color: #660033;">-l</span> INFO <span style="color: #660033;">--logfile</span>=logs<span style="color: #000000; font-weight: bold;">/</span>celery.log <span style="color: #660033;">-D</span> <span style="color: #660033;">-Q</span> ploshadka_queue <span style="color: #660033;">--result-backend</span>=db+postgresql+psycopg2:<span style="color: #000000; font-weight: bold;">//</span>localhost<span style="color: #000000; font-weight: bold;">/</span>ploshadka_db</div></div>
<div class="highlight">Для этих целей удобно сделать скрипт и добавить в <a href="https://ploshadka.net/ubuntu-avtozapusk-skriptov-posle-perezagruzki/">автозапуск после перезагрузки сервера</a>.</div>
<h2>Работа с очередями</h2>
<p>Очереди нужны, чтобы не запутаться внутри кролика, если сайтов на сервере несколько или задач очень много, то их лучше поделить на категории.</p>
<p>По умолчанию используется очередь с названием <strong>celery</strong>.</p>
<p>Для того чтобы добавить очередь к задачи надо добавить в конфиг следующее:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">BEAT_SCHEDULE <span style="color: #66cc66;">=</span> <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #483d8b;">'updates'</span>: <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'options'</span>: <span style="color: black;">&#123;</span><span style="color: #483d8b;">'queue'</span>: <span style="color: #483d8b;">'ploshadka.net'</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; <span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
<span style="color: black;">&#125;</span></div></div>
<p>В случае запуска <strong>worker</strong> с определенной очередью:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery <span style="color: #660033;">-A</span> tasks.updates.celery worker <span style="color: #660033;">-Q</span> ploshadka.net <span style="color: #660033;">-l</span> info</div></div>
<p>Можно избежать запуск с указанием очереди прописав в файле <strong>__init__.py</strong> (там где мы инициализируем функцию celery):</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery.<span style="color: black;">conf</span>.<span style="color: black;">task_default_queue</span> <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'ploshadka.net'</span></div></div>
<p>Или указать в конфигурационном файле:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">task_default_queue <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'ploshadka.net'</span></div></div>
<h2>Проверка очередей</h2>
<p>Предварительно должен быть запущен worker.</p>
<p>Показать какие задачи сейчас в работе:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery inspect registered</div></div>
<p>Для проверки есть ещё такие команды, но они не покажут название текущей рабочей задачи как это сделает команда выше:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery status<br />
celery inspect active<br />
celery inspect scheduled</div></div>
<h2>Логирование в Celery</h2>
<p>Celery перехватывает логирование тех задач, которые она выполняет. Даже если для тех задач <a href="https://ploshadka.net/logirovanie-v-python-logging/">настроено логирование</a>, оно будет записываться в лог celery, а не в тот который был ранее указан в логировании.</p>
<p>Это можно переопределить добавив в конфигурацию создания make_celery такую директиву:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #483d8b;">'worker_hijack_root_logger'</span>: <span style="color: #008000;">False</span></div></div>
<div class="highlight">Но я бы не советовал, потому что такое переопределение может привести к непредсказуемым результатам, да и после небольшого понимания становится удобнее читать все что касается задачи в одном месте &#8212; в логах сельдерея.</div>
<p>Для того чтобы логи записывались мы можем указать их в консольной команде:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery -A tasks.<span style="color: black;">updates</span>.<span style="color: black;">celery</span> worker -D --logfile<span style="color: #66cc66;">=</span>logs/celery.<span style="color: black;">log</span> -l INFO</div></div>
<p>Стоит обратить внимание, что одновременно с указанием файла <strong>&#8212;logfile=logs/celery.log</strong> надо указать, что именно будет записываться <strong>-l INFO</strong>. </p>
<p>Вместо info можно записывать дебаг: <strong>-l DEBUG</strong>. Использовать на случай отладки, иначе слишком много дополнительной информации будет записано.</p>
<p>Для beat точно такая же система.</p>
<h2>Задвоения и дубликаты задач</h2>
<p>Как было сказано выше дубли задач celery будут, если сначала добавляются beat, а потом запускать worker или если перед запуском воркера не очищать задачи. </p>
<p>Удалить повторы задач можно командами ниже. Иногда не сразу происходит удаление, нужно подождать около минуты.</p>
<p>Как удалить и очистить задачи ниже.</p>
<h2>Как удалить задачи</h2>
<div class="highlight">Удаление задач не стоит путать с очисткой очередей. <strong>Удаление задач</strong> &#8212; это удаление их из планировщика (beat). Все что ранее добавилось в очередь будет находится в очереди и ждать выполнения. </div>
<p>Показать все планировщики celery:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #c20cb9; font-weight: bold;">ps</span> <span style="color: #660033;">-ef</span> <span style="color: #000000; font-weight: bold;">|</span> <span style="color: #c20cb9; font-weight: bold;">grep</span> celery</div></div>
<p>Вывод:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">&nbsp; <span style="color: #000000;">501</span> <span style="color: #000000;">82926</span> &nbsp; &nbsp; <span style="color: #000000;">1</span> &nbsp; <span style="color: #000000;">0</span> <span style="color: #000000;">11</span>:51PM ?? &nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #000000;">0</span>:<span style="color: #000000;">00.23</span> <span style="color: #000000; font-weight: bold;">/</span>usr<span style="color: #000000; font-weight: bold;">/</span>local<span style="color: #000000; font-weight: bold;">/</span>Cellar<span style="color: #000000; font-weight: bold;">/</span>python<span style="color: #000000; font-weight: bold;">@</span><span style="color: #000000;">3.8</span><span style="color: #000000; font-weight: bold;">/</span>3.8.5<span style="color: #000000; font-weight: bold;">/</span>Frameworks<span style="color: #000000; font-weight: bold;">/</span>Python.framework<span style="color: #000000; font-weight: bold;">/</span>Versions<span style="color: #000000; font-weight: bold;">/</span><span style="color: #000000;">3.8</span><span style="color: #000000; font-weight: bold;">/</span>Resources<span style="color: #000000; font-weight: bold;">/</span>Python.app<span style="color: #000000; font-weight: bold;">/</span>Contents<span style="color: #000000; font-weight: bold;">/</span>MacOS<span style="color: #000000; font-weight: bold;">/</span>Python <span style="color: #000000; font-weight: bold;">/</span>venv<span style="color: #000000; font-weight: bold;">/</span>bin<span style="color: #000000; font-weight: bold;">/</span>celery <span style="color: #660033;">-A</span> tasks.updates.celery beat <span style="color: #660033;">--detach</span><br />
&nbsp; <span style="color: #000000;">501</span> <span style="color: #000000;">83252</span> <span style="color: #000000;">59866</span> &nbsp; <span style="color: #000000;">0</span> <span style="color: #000000;">11</span>:52PM ttys001 &nbsp; &nbsp;<span style="color: #000000;">0</span>:<span style="color: #000000;">00.00</span> <span style="color: #c20cb9; font-weight: bold;">grep</span> celery</div></div>
<p>Завершить все задачи и планирощик:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">pkill <span style="color: #660033;">-9</span> <span style="color: #660033;">-f</span> tasks.updates.celery</div></div>
<p>Или эту (они должны были бы заменять друг друга, но на деле иногда одна из команд отказывается работать корректно):</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery <span style="color: #660033;">-A</span> tasks.updates.celery control shutdown</div></div>
<p>После этой команды возникает надпись:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">Error: No nodes replied within time constraint.</div></div>
<p>Можно не обращать внимание. Все воркеры и планировщики останавливаются. Это можно проверить командой выше (grep). </p>
<h2>Как очистить или удалить очереди</h2>
<p>Если запустить планировщик <strong>beat</strong> без <strong>worker</strong>, он все равно начнет работать. Это значит, что в нужное время будет добавлена задача в очередь. Иными словами, если была запланирована задача, которая каждую минуту что-то делает, то каждую минуту будет добавлена выполнение этой задачи. Очередь задач можно увидеть в RebbitMQ:</p>
<p><a href="https://ploshadka.net/wp-content/uploads/7203/queues.jpg" rel="lightbox-3"><img wpfc-lazyload-disable="true" decoding="async" loading="lazy" src="https://ploshadka.net/wp-content/uploads/7203/queues-600x141.jpg" alt="" width="600" height="141" class="aligncenter size-medium wp-image-7363" srcset="https://ploshadka.net/wp-content/uploads/7203/queues-600x141.jpg 600w, https://ploshadka.net/wp-content/uploads/7203/queues-1200x282.jpg 1200w, https://ploshadka.net/wp-content/uploads/7203/queues-1536x361.jpg 1536w, https://ploshadka.net/wp-content/uploads/7203/queues-300x71.jpg 300w, https://ploshadka.net/wp-content/uploads/7203/queues.jpg 1598w" sizes="(max-width: 600px) 100vw, 600px" /></a></p>
<p>В тот момент когда будет запущен воркер, все накопленные задачи будут запущены друг за другом без того интервала, который раньше подразумевался (например, 1 минута). Это может привести к неприятным последствиям.</p>
<div class="highlight">Есть несколько способов очистки очереди. Надо понимать, что в случае очистки очереди, если не отключен планировщик beat, задачи будут снова накапливаться.</div>
<h3>1</h3>
<p>Удалить очереди конкретной задачи:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery <span style="color: #660033;">-A</span> tasks.updates.celery purge <span style="color: #660033;">-f</span></div></div>
<p>Должно показать что-то вроде:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">Purged <span style="color: #000000;">108</span> messages from <span style="color: #000000;">1</span> known task queue.</div></div>
<h3>2</h3>
<p>Удалить очередь по её названию.</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery amqp queue.delete название_очереди</div></div>
<p>Теперь до запуска нового worker ничего накапливаться не будет, потому что мы вовсе удаляем очередь, а не просто очищаем ее.</p>
<h3>3</h3>
<p>Можно сразу запускать воркер с очищением очереди:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery <span style="color: #660033;">-A</span> tasks.updates.celery worker <span style="color: #660033;">-l</span> info <span style="color: #660033;">--purge</span></div></div>
<h2>Как не повторять задачу пока она не завершится</h2>
<p>Встроенных инструментов для этого не придумано. Надо самостоятельно делать блокировку внутри функции <a href="https://ploshadka.net/kehshirovanie-na-flask/">с помощью кэша</a>. Можно сделать так:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #808080; font-style: italic;"># Проверяем есть ли кэш</span><br />
is_running <span style="color: #66cc66;">=</span> cache.<span style="color: black;">get</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;running&quot;</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Если кэш есть, значит предыдущая функция не закончена, выйдем из функции</span><br />
<span style="color: #ff7700;font-weight:bold;">if</span> is_running:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Ставим кэш.</span><br />
cache.<span style="color: #008000;">set</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">'running'</span><span style="color: #66cc66;">,</span> <span style="color: #483d8b;">'1'</span><span style="color: black;">&#41;</span><br />
<br />
<span style="color: #808080; font-style: italic;"># Удалим кэш в конце функции</span><br />
cache.<span style="color: black;">delete</span><span style="color: black;">&#40;</span><span style="color: #483d8b;">&quot;running&quot;</span><span style="color: black;">&#41;</span></div></div>
<h2>Дебаг</h2>
<p>Если ничего не работает, попробовать найти ошибку через команды с дебагом.</p>
<p>Для worker в консоли или в режиме демона:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery <span style="color: #660033;">-A</span> tasks.updates.celery worker <span style="color: #660033;">--purge</span> <span style="color: #660033;">-l</span> DEBUG<br />
celery <span style="color: #660033;">-A</span> tasks.updates.celery worker <span style="color: #660033;">--purge</span> <span style="color: #660033;">-l</span> DEBUG <span style="color: #660033;">--logfile</span>=logs<span style="color: #000000; font-weight: bold;">/</span>celery.log <span style="color: #660033;">-D</span></div></div>
<p>Для beat:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery <span style="color: #660033;">-A</span> tasks.updates.celery beat <span style="color: #660033;">-l</span> DEBUG<br />
celery <span style="color: #660033;">-A</span> tasks.updates.celery beat <span style="color: #660033;">-l</span> DEBUG <span style="color: #660033;">--logfile</span>=logs<span style="color: #000000; font-weight: bold;">/</span>beat.log <span style="color: #660033;">--detach</span></div></div>
<h2>Celery Flower</h2>
<p>Для просмотра задач воспользуетесь <a href="https://ploshadka.net/celery-flower/">flower</a>. Альтернативные варианты <a href="https://ploshadka.net/celery-vyvod-na-front/">вывести на фронт данные из бд таблицы celery_taskmeta</a> или просматривать данные там напрямую, например, через <a href="https://ploshadka.net/ustanovka-i-podkljuchenie-postgresql-na-mac-os/">DataGrip</a>.</p>
<h2>Настройка конфигов</h2>
<div class="highlight">
Если запускать <strong>worker</strong> в режиме демона с помощью <strong>detach</strong>, он может перестать видеть конфиги. При этом, если запускать его обычным образом с мониторингом в консоли, то все прекрасно работает. Для исправления этой ситуации надо прописывать дополнительные настройки в файле конфигурации и указывать дополнительные параметры в консольной команде.
</div>
<p>При запуске worker в режиме detach может возникнуть такая ошибка:</p>
<blockquote><p>[2020-12-31 11:11:35,802: ERROR/MainProcess] Received unregistered task of type &#8216;task_update&#8217;.<br />
The message has been ignored and discarded.</p>
<p>Did you remember to import the module containing this task?<br />
Or maybe you&#8217;re using relative imports?</p>
<p>Please see<br />
http://docs.celeryq.org/en/latest/internals/protocol.html<br />
for more information.</p>
<p>The full contents of the message body was:<br />
&#8216;[[], {}, {&#171;callbacks&#187;: null, &#171;errbacks&#187;: null, &#171;chain&#187;: null, &#171;chord&#187;: null}]&#8217; (77b)<br />
Traceback (most recent call last):<br />
  File &#171;venv/lib/python3.9/site-packages/celery/worker/consumer/consumer.py&#187;, line 562, in on_task_received<br />
    strategy = strategies[type_]<br />
KeyError: &#8216;task_update&#8217;</p></blockquote>
<p>Дело в том, что worker не видит путь к задачам. Исправить можно двумя вариантами.</p>
<h3>Вариант 1</h3>
<p>Использовать в командной строке указатель на файл конфигурации, в котором описать настройки. Для этого в консольной команде надо указать:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">––config=configs.celery-config</div></div>
<p>где configs это директория в которой находится файл конфигурации <strong>celery-config.py</strong>.</p>
<p> Содержимое этого файла:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #808080; font-style: italic;"># List of modules to import when the Celery worker starts.</span><br />
imports <span style="color: #66cc66;">=</span> <span style="color: black;">&#40;</span><span style="color: #483d8b;">'tasks.updates'</span><span style="color: #66cc66;">,</span><span style="color: black;">&#41;</span></div></div>
<p>В импорте указываем путь до файла с задачами.</p>
<h3>Вариант 2</h3>
<p>Во втором варианте больше писанины, но так тоже будет работать: можно без указания дополнительных настроек в файле.</p>
<p>В самом <strong>BEAT_SCHEDULE</strong> указать полный путь до задачи:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">BEAT_SCHEDULE <span style="color: #66cc66;">=</span> <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; <span style="color: #483d8b;">'every_minute'</span>: <span style="color: black;">&#123;</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'options'</span>: <span style="color: black;">&#123;</span><span style="color: #483d8b;">'queue'</span>: <span style="color: #483d8b;">'task'</span><span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'task'</span>: <span style="color: #483d8b;">'tasks.updates.task_update'</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; &nbsp; &nbsp; <span style="color: #483d8b;">'schedule'</span>: <span style="color: #ff4500;">59.0</span><span style="color: #66cc66;">,</span><br />
&nbsp; &nbsp; <span style="color: black;">&#125;</span><span style="color: #66cc66;">,</span><br />
<span style="color: black;">&#125;</span></div></div>
<p>где <strong>tasks.updates</strong> это директория и файл:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">tasks<br />
--__init__.py<br />
--updates.py</div></div>
<p>А <strong>task_update</strong> название функции в файле <strong>updates.py</strong>.</p>
<p>В этом случае в самой задаче тоже обязательно указать полный путь:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #66cc66;">@</span>celery.<span style="color: black;">task</span><span style="color: black;">&#40;</span>name<span style="color: #66cc66;">=</span><span style="color: #483d8b;">'tasks.updates.task_update'</span><span style="color: black;">&#41;</span><br />
<span style="color: #ff7700;font-weight:bold;">def</span> task_update<span style="color: black;">&#40;</span><span style="color: black;">&#41;</span>:<br />
&nbsp; &nbsp; <span style="color: #ff7700;font-weight:bold;">return</span> <span style="color: #483d8b;">&quot;Все ок&quot;</span></div></div>
<p>Также все равно придется добавлять в командную строку опцию, но указывать уже на сам модуль с задачей:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #660033;">--config</span>=tasks.updates</div></div>
<h2>Несколько очередей для разных сайтов на одном сервере</h2>
<p>Добавляем такой атрибут к команде по запуска worker:</p>
<div class="codecolorer-container bash dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="bash codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #660033;">-n</span> worker1<span style="color: #000000; font-weight: bold;">@%</span>h</div></div>
<p>где каждая цифра под отдельный сайт, т.е. для второго сайта будет 2</p>
<p>Ошибка когда не указан параметр:</p>
<blockquote><p>
celery@domain1 ready.warnings.warn(W_PIDBOX_IN_USE.format(node=self))you give each node a unique node name!Or if you meant to start multiple nodes on the same host please make sure</p>
<p>Maybe you forgot to shutdown the other node or did not do so properly?<br />
2021-01-10 22:38:19,893: venv/lib/python3.8/site-packages/kombu/pidbox.py:72:<br />
UserWarning:<br />
A node named celery@domain1 is already using this process mailbox!<br />
2021-01-10 22:38:19,875: mingle: all alone2021-01-10 22:38:18,816: mingle: searching for neighbors<br />
2021-01-10 22:38:18,802: Connected to amqp://guest:**@127.0.0.1:5672//</p></blockquote>
<h2>Не работает сохранение в БД</h2>
<p>Если через worker detach не работает сохранение в БД, то ошибка аналогично предыдущей. Мы или указываем в файле конфигурации такие настройки:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #808080; font-style: italic;"># Using the database to store task state and results.</span><br />
result_backend <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'db+postgresql+psycopg2://localhost/ploshadka_net_db'</span></div></div>
<p>Или к консольной команде worker добавляем наш бэкенд:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">--result-backend<span style="color: #66cc66;">=</span>db+postgresql+psycopg2://localhost/ploshadka_net_db</div></div>
<h2>Не запускается очередь</h2>
<p>Ровно такая же ситуация с очередями. Можно указывать в консольной команде:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">-Q название_очереди</div></div>
<p>Или занести в файл конфигурации:</p>
<div class="codecolorer-container python dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="python codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;"><span style="color: #808080; font-style: italic;"># Queue by default</span><br />
task_default_queue <span style="color: #66cc66;">=</span> <span style="color: #483d8b;">'ploshadka_queue'</span></div></div>
<p>Если не указывать очередь по умолчанию в конфигурации и не указывать ее в консольной команде, то в логах можно наблюдать такие надписи:</p>
<blockquote><p>DEBUG/MainProcess] heartbeat_tick : for connection</p></blockquote>
<h2>Другие ошибки в процессе настройки</h2>
<h3>Scheduler: Sending due task every_minute</h3>
<p>Ошибка:</p>
<blockquote><p>LocalTime -> 2020-12-16 10:12:49<br />
Configuration -><br />
    . broker -> amqp://guest:**@localhost:5672//<br />
    . loader -> celery.loaders.app.AppLoader<br />
    . scheduler -> celery.beat.PersistentScheduler<br />
    . db -> celerybeat-schedule<br />
    . logfile -> [stderr]@%INFO<br />
    . maxinterval -> 5.00 minutes (300s)<br />
[2020-12-16 10:12:49,940: INFO/MainProcess] beat: Starting&#8230;<br />
[2020-12-16 10:13:00,017: INFO/MainProcess] Scheduler: Sending due task every_minute (tasks.update_balance)</p></blockquote>
<p>Запущен beat, но не запущен worker.</p>
<p>Ошибка:</p>
<blockquote><p>Error: no such option: &#8212;config</p></blockquote>
<p>В новой версии celery изменилось расположение конфигов. Надо поставить опцию с конфигом перед beat или worker.</p>
<p>Также могут быть такие ошибки:</p>
<ul>
<li><a href="https://ploshadka.net/warning-bottle-installation-failed-building-from-source/">Warning: Bottle installation failed: building from source</a></li>
<li><a href="https://ploshadka.net/attributeerror-flask-object-has-no-attribute-user_options/">AttributeError: ‘Flask’ object has no attribute ‘user_options’</a></li>
<li><a href="https://ploshadka.net/oshibka-consumer-cannot-connect-to-amqp-guest127-0-0-15672/">consumer: Cannot connect to amqp://guest:**@127.0.0.1:5672//</a></li>
<li><a href="https://ploshadka.net/oshibka-error-homebrew-core-is-a-shallow-clone/">Error: homebrew-core is a shallow clone</a></li>
<li><a href="https://ploshadka.net/oshibka-the-celery_result_backend-setting-is-deprecated-and-scheduled-for-removal-in/">The ‘CELERY_RESULT_BACKEND’ setting is deprecated and scheduled for removal in</a></li>
<li><a href="https://ploshadka.net/valueerror-invalid-width-2-must-be-0/">https://ploshadka.net/valueerror-invalid-width-2-must-be-0/</a></li>
</ul>
<h2>Сравнение версий Celery 4 и Celery 5</h2>
<p>В четвертой версии Celery при запуске создавал pid файл с номером процесса внутри. Это было и плюсом и минусом. Плюс в том, что нельзя было запустить команду beat или worker дважды, показывало ошибку:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">Stale pidfile exists - Removing it.<br />
Seems we're already running? (pid: 34944)</div></div>
<p>А минус, чтобы убить процесс надо было использовать команду:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">pkill -9 -f celery</div></div>
<p>А она убивала не только процессы одного сайта, но и процессы celery для всех других сайтов. Начиная с версии 5, можно использовать команду:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">pkill -9 -f tasks.updates.celery</div></div>
<p>Еще одно изменение в порядке опций в команде:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery -A tasks.updates.celery --config=configs.private.celery worker -D --logfile=logs/celery.log -l INFO --purge</div></div>
<p>Теперь <strong>config</strong> и некоторые другие опции надо ставить перед worker, как показано выше.</p>
<p>Еще изменились константы. Вместо них сейчас переменные с другим наименованием.</p>
<p>Конечно это не все изменения, остальные можно узнать в официальных источниках.</p>
<h2>Окончательный вариант запуска на сервере</h2>
<p>Запускаем несколько worker:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery --config=configs.private.celery worker -Q update -D --purge -l INFO --logfile=logs/celery.log<br />
celery --config=configs.private.celery worker -Q analyse -D --purge -l INFO --logfile=logs/celery.log</div></div>
<p>И запускаем только один beat:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">celery --config=configs.private.celery beat --detach -l INFO --logfile=logs/beat.log</div></div>
<p>Импорты указаны в файле <strong>beatschedule</strong> и в команде их дублировать не нужно:</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">imports = ('app.tasks.update', )</div></div>
<p>Для перезапуска используем (осторожно, убивает все воркеры и биты):</p>
<div class="codecolorer-container text dawn" style="overflow:auto;white-space:nowrap;border:1px solid #9F9F9F;width:435px;"><div class="text codecolorer" style="padding:5px;font:normal 12px/1.4em Monaco, Lucida Console, monospace;white-space:nowrap;">pkill -9 -f celery</div></div>
<p>и повторяем запуск снова.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://ploshadka.net/flask-celery-rabbitmq/feed/</wfw:commentRss>
			<slash:comments>0</slash:comments>
		
		
			</item>
	</channel>
</rss>
