celery -A proj report
в выпуск.software -> celery:4.1.0 (latentcall) kombu:4.1.0 py:3.5.2
billiard:3.5.0.3 redis:2.10.5
platform -> system:Linux arch:64bit, ELF imp:CPython
loader -> celery.loaders.app.AppLoader
settings -> transport:redis results:redis://:**@****************
task_ignore_result: True
accept_content: {'pickle'}
result_serializer: 'pickle'
result_backend: 'redis://:********@************************'
task_serializer: 'pickle'
task_send_sent_event: True
broker_url: 'redis://:********@************************'
версия redis-server, как 2.x, так и 3.x
Здравствуйте, я не уверен, что может вызвать проблемы, и уже пытался найти аналогичное решение, но пока безуспешно. Поэтому открывающая проблема здесь, надеюсь, это поможет
Проблема также описана там (не мной): https://github.com/andymccurdy/redis-py/issues/612
Пока что опыт таков, что это происходит в обоих случаях, когда бэкэнд задействован и не задействован (имеется в виду только при вызове apply_async(...)
).
Исключение при вызове apply_async()
Исключение при вызове .get()
(также у этого есть int, а не list)
Надеюсь, это поможет
Чтобы не выкидывать ошибку.
AttributeError: объект 'list' не имеет атрибута 'decode'
Спасибо!
Также есть полная трассировка стека, которая включает все параметры
Это похоже на состояние гонки в пуле соединений Redis из-за одновременных рабочих операций. Какой тип пула рабочих вы используете? Думаю, что если вы воспользуетесь prefork pool, то не столкнетесь с этой проблемой. Дайте мне знать, каков будет результат, если вы попробуете.
Привет, @georgepsarakis ,
спасибо за ответ, похоже, мы работаем на prefork
при проверке вывода при запуске сельдерея (который работает под systemd) я получил этот вывод:
-------------- celery@autoscaled-dashboard-worker v4.1.0 (latentcall)
---- **** -----
--- * *** * -- Linux-4.4.0-45-generic-x86_64-with-Ubuntu-16.04-xenial 2017-11-04 20:41:15
-- * - **** ---
- ** ---------- [config]
- ** ---------- .> app: worker:0x7f984dbcce48
- ** ---------- .> transport: redis://:**@**
- ** ---------- .> results: redis://:**@**
- *** --- * --- .> concurrency: {min=3, max=12} (prefork)
-- ******* ---- .> task events: ON
некоторая другая информация:
celery multi start -A ... -E --autoscale=12,3 -Ofair
+ некоторые другие не очень важные агрументы, все указаны как Type=forking
service)дайте мне знать, могу ли я добавить что-нибудь еще, хотел бы как можно больше помочь решить эту проблему :)
Благодарность!
еще одна вещь (даже если я не уверен, может ли это быть связано) - иногда мы получаем это исключение (та же настройка, что и выше)
File "/usr/local/lib/python3.5/dist-packages/celery/app/base.py", line 737, in send_task
amqp.send_task_message(P, name, message, **options)
File "/usr/lib/python3.5/contextlib.py", line 77, in __exit__
self.gen.throw(type, value, traceback)
File "/usr/local/lib/python3.5/dist-packages/kombu/connection.py", line 419, in _reraise_as_library_errors
sys.exc_info()[2])
File "/usr/local/lib/python3.5/dist-packages/vine/five.py", line 178, in reraise
raise value.with_traceback(tb)
File "/usr/local/lib/python3.5/dist-packages/kombu/connection.py", line 414, in _reraise_as_library_errors
yield
File "/usr/local/lib/python3.5/dist-packages/celery/app/base.py", line 736, in send_task
self.backend.on_task_call(P, task_id)
File "/usr/local/lib/python3.5/dist-packages/celery/backends/redis.py", line 189, in on_task_call
self.result_consumer.consume_from(task_id)
File "/usr/local/lib/python3.5/dist-packages/celery/backends/redis.py", line 76, in consume_from
self._consume_from(task_id)
File "/usr/local/lib/python3.5/dist-packages/celery/backends/redis.py", line 82, in _consume_from
self._pubsub.subscribe(key)
File "/usr/local/lib/python3.5/dist-packages/redis/client.py", line 2482, in subscribe
ret_val = self.execute_command('SUBSCRIBE', *iterkeys(new_channels))
File "/usr/local/lib/python3.5/dist-packages/redis/client.py", line 2404, in execute_command
self._execute(connection, connection.send_command, *args)
File "/usr/local/lib/python3.5/dist-packages/redis/client.py", line 2408, in _execute
return command(*args)
File "/usr/local/lib/python3.5/dist-packages/redis/connection.py", line 610, in send_command
self.send_packed_command(self.pack_command(*args))
File "/usr/local/lib/python3.5/dist-packages/redis/connection.py", line 585, in send_packed_command
self.connect()
File "/usr/local/lib/python3.5/dist-packages/redis/connection.py", line 493, in connect
self.on_connect()
File "/usr/local/lib/python3.5/dist-packages/redis/connection.py", line 567, in on_connect
if nativestr(self.read_response()) != 'OK':
File "/usr/local/lib/python3.5/dist-packages/redis/connection.py", line 629, in read_response
raise response
kombu.exceptions.OperationalError: only (P)SUBSCRIBE / (P)UNSUBSCRIBE / PING / QUIT allowed in this context
мы попытались обновить py-redis (увидим, но это всего лишь второстепенный)
любой намек очень ценю :)
@Twista, можешь попробовать патч на
Если вы можете добавить сюда следующий код:
def on_after_fork(self):
logger.info('Resetting Redis client.')
del self.backend.client
Я надеюсь, что это заставит свойство client cached генерировать нового клиента Redis после каждой рабочей вилки.
Сообщите мне, если это даст какой-нибудь результат.
Привет, извините за долгий ответ.
Просто продолжение - мы просто пропатчили его и посмотрим, поможет ли. Надеюсь скоро :)
Спасибо за помощь :)
дайте нам знать ваш отзыв
Привет,
У меня примерно такая же проблема (с использованием Celery.send_task
), и у меня есть связанный с этим вопрос:
Почему при вызове асинхронной задачи (поэтому результат не ожидается ) Celery начинает прослушивать издателя Redis ( self._pubsub.subscribe(key)
вызывается обработчиком сигнала celery/backends/redis.py
, строка 189, в on_task_call
)?
@georgepsarakis, к сожалению, патч не помог. :-(
вот еще одна трассировка стека:
Пожалуйста, дайте мне знать, если вы хотите протестировать некоторые логи или другие исправления.
@wimby большое спасибо за отзыв. Подскажите пожалуйста какие варианты вы используете для запуска воркера?
Привет @georgepsarakis
Отвечаю на этот вместо @wimby
вот и вся команда, которую мы используем для запуска сельдерея
celery multi start worker -A ... --pidfile=... --logfile=... --loglevel=... -E --time-limit=300 --autoscale=10,3 --queues=... -Ofair
столкнулся с той же проблемой, когда решил попробовать Redis Broker, пока откатился на кролик ..
Есть новости по этому поводу? Выполнение этого с аналогичной настройкой для @Twista / @wimby, где у нас есть рабочие cron, работающие на отдельной машине, чем процесс, который планирует наши задачи.
сельдерей == 4.0.2
redis == 2.10.5.
Чтобы уточнить, это происходит на машине, которая отправляет задачи, а не на рабочем компьютере.
Я тоже это вижу. Сельдерей 4.2.0, комбу 4.2.1, редис 2.10.6. Использование redis как для брокера, так и для результатов. Трафик Redis проходит между серверами по зашифрованному stunnel-соединению через порты.
Запуск в него в Django 1.11, работающем на mod_wsgi (2 процесса, 15 потоков в каждом), и это не ограничивается только указанным выше исключением. Я не могу скопировать и вставить трассировки полного стека. Приложение загружает веб-страницу с помощью кучи запросов ajax (90 запросов на загрузку страницы), каждый из которых запускает фоновую задачу через сельдерей. Задания выполняются успешно, но вернуть результат сложно.
При отправке задач я получил ConnectionError
s, AttributeError
s (список не имеет атрибута encode).
При получении результатов я получил InvalidResponse
, ResponseError
, ValueError
, AttributeError
, TypeError
, IndexError
, почти все из redis. Я также видел ошибки декодирования из комбу при попытке проанализировать ответы json. Я подозреваю, что трафик между запросами смешивается - в некоторых случаях я видел то, что выглядело как частичные ответы.
Я возвращаюсь к выполнению более объемных задач, что должно, по крайней мере, минимизировать это. Однако я видел случаи, когда проблемы с протоколом все равно возникали при минимальной нагрузке. Я бы не удивился, если бы это было вызвано основными проблемами с сетью. Я также использую дополнительную логику повтора для отправки запросов.
@deterb, если вы установили task_ignore_result
в конфигурации сельдерея, это должно предотвратить это. Если вы не заботитесь о результатах задачи, в этом случае это явно не поможет.
Исправление, учитывающее ignore_result
при планировании задач, было сделано в 4.2.0, так что все должно быть хорошо.
@asgoel Вся суть в том, чтобы получить результаты для большей части запросов (для них он не столько выполняется в фоновом режиме, сколько выполняет тяжелые вычисления на сервере, созданном для него, а не на веб-сервере). Я добавлю результат игнорирования для остальных и посмотрю, поможет ли это.
@deterb это похоже на проблему с небезопасными для потоков операциями. Можно ли попробовать не использовать многопоточность?
@georgepsarakis Я согласен, что это похоже на проблему с многопоточностью. Я попробую настроить mod_wsgi для работы с 15 процессами, по 1 потоку на процесс, и посмотрю, по-прежнему ли я наблюдаю такое поведение. Я не использую никаких дополнительных потоков или многопроцессорности вне mod_wsgi. Я видел похожее поведение (хотя и с меньшей частотой), работающее с сервером Django. Единственное взаимодействие с клиентами Redis в веб-приложении осуществляется через Celery, а именно отправка задач и получение их результатов. redis-py утверждает, что он потокобезопасен.
Я постараюсь провести дополнительное тестирование завтра и посмотрю, смогу ли я воссоздать его вне Django и без прокси-сервера stunnel.
@georgepsarakis Сегодня у меня не было возможности попробовать, но я считаю, что # 4670 (а именно, общие объекты PubSub
не являющиеся потокобезопасными) связаны с # 4480. Скорее всего, то, что я вижу, отличается от исходного тикета и других упомянутых проблем. Хотя это, вероятно, связано с проблемами, поднятыми @asgoel , я думаю, что будет уместным отдельный билет для проблем параллелизма с бэкэндом результатов redis (оставьте # 4480, относящийся к бэкэнду результатов RPC). Я также могу начать извлекать соответствующие части трассировки стека.
Привет, ребята, я много раз сталкивался с этими ошибками.
Я использовал AWS Elasticache, и мое приложение было развернуто в AWS Elastic Beanstalk.
Мы изменили AWS Elasticache на Redis Labs и увеличили время ожидания AWS Elastic Load Balancer до 180 секунд, а сервера Apache - до 180 секунд.
Ошибок сильно уменьшилось.
Тем не менее время от времени они продолжались. Поэтому я решил изменить бэкэнд результата на PostgreSQL, и тогда ошибки полностью исчезли.
Интересная вещь: мы начали сталкиваться с этой проблемой сразу после развертывания нового выпуска, который
Итак, интересно знать, что это может быть что-то, что зависит от версии Python. Надеюсь, это поможет разобраться в проблеме.
(В остальном мы используем сельдерей 4.2.1 и redis 2.10.6)
Кроме того, проблемная задача запускается Celery beat, и у нас была такая же проблема в задаче, запущенной другой задачей.
Всем спасибо за отзывы. Просто чтобы прояснить несколько вещей:
ResultConsumer
- это компонент Redis Backend, который асинхронно получает результат.ResultConsumer
инициализирует экземпляр PubSub
ResultConsumer
Экземпляры результаты не игнорируются.PubSub
не может использоваться одновременно из нескольких потоковЯ не знаю, можно ли выполнить соответствующие операции при запуске Django, возможно, этот обратный вызов может помочь; вызов ResultConsumer.on_after_fork
затем создаст новые экземпляры, и проблема, скорее всего, не возникнет.
Хм.
В моем случае я попытался воспроизвести проблему, увеличив частоту выполнения задачи в Celery Beat. В настоящее время у меня происходит что-то вроде 1 сбоя на каждые 15 задач, но сбой происходит в рабочем, а не в Beat, и мой рабочий имеет concurrency
равный 1 (у меня есть несколько независимых процессов, но все они "одиночные" ). htop
сообщает мне, что есть основной процесс с единственным дочерним потоком. Это согласуется с вашей гипотезой?
Если я правильно понял ваш ответ (перечитываю, на всякий случай), абзац:
Я не знаю, можно ли выполнить соответствующие операции при запуске Django, возможно, этот обратный вызов может помочь; вызов ResultConsumer.on_after_fork затем создаст новые экземпляры, и проблема, скорее всего, не возникнет.
это было бы связано со случаем, когда у нас были бы ошибки при публикации задач из наших веб-процессов (gunicorn / uwsgi / ...), верно?
Что касается Рабочих, это изменение должно охватывать случай запуска новых вилок.
В наших случаях эти ошибки исходят от рабочих исключительно с Celery 4.2.1. Если я правильно понимаю ваше утверждение, то, возможно, там все еще есть проблема ...
В Celery 4.2.0 мне удалось обойти эту проблему, выполнив следующие действия:
result.backend.result_consumer.cancel_for(result.task_id)
result.backend.result_consumer.stop()
Я не понял, как убрать призыв к остановке; если я удалю его, соединения начнут просачиваться (обратите внимание, что я работаю в среде mod_wsgi, подключающейся к redis через stunnel; в конечном итоге возникают проблемы с подключением после нескольких сотен запросов). Через некоторое время я перестаю подключаться через туннель. Я также не понял, как подключиться к управлению потоками mod_wsgi.
В тестовом примере, который я использую, есть веб-страница, которая выполняет ~ 1500 запросов Ajax по 5 за раз, выполняемых полностью в команде Django run_server. Без ограничения скорости условия гонки для конечных объектов pubsub бэкэнда возникают постоянно. Они происходят регулярно и с ограничением скорости.
Я подозреваю, что использование локального потока ResultConsumers для redis также может помочь с проблемами рабочего процесса Greenlet, хотя я не уверен, как можно надежно закрыть pubsubs.
Переключение RedisBackend на использование опроса для веб-приложения работает без изменений для конкретного приложения. Я заменил миксин асинхронного бэкэнда на base.SyncBackendMixin и закомментировал строки, относящиеся к получателю результата. Пока не будет исправлено более чистое решение, я, вероятно, переключу веб-приложение на использование версии RedisBackend SyncBackend и позволю рабочим использовать обычную версию (которая, похоже, не имеет этой проблемы.
Учитывая, что я использую Django, я решил использовать django-celery-results. Использование Redis меньше соответствует другим дизайнерским решениям, и в любом случае мы действительно мало что храним. Мы еще не запустили это в производство.
Результаты тяжелые и достаточно транзитивные, я не хочу помещать их в базу данных. В качестве более консервативного варианта я могу просто сохранить результаты напрямую, а не позволять Celery делать это до тех пор, пока это не будет исправлено (я уже использую Redis для некоторого кеширования).
У меня аналогичная проблема. Я надеюсь, что дополнительная информация, которую я предоставляю, может пролить больше света на проблему.
У меня есть один работник сельдерея (concurrency = 1) и сервер, который помещает задачи в очередь и получает результаты. Я использую rabbitmq в качестве брокера и redis для бэкэнда результата.
Я использую сельдерей 4.2.1 и hiredis 0.2.0 (проблема возникает и без hiredis).
Вызов службы на более медленной скорости работает должным образом, но как только я отправляю ~ 100 одновременных запросов, я начинаю видеть проблемы, описанные в этом выпуске. У меня есть пара разных исключений, которые, как мне кажется, связаны между собой.
File "/___/___/celery_app.py", line 101, in send_task
task_result.wait(timeout=timeout, propagate=True)
File "/usr/lib/python3.6/site-packages/celery/result.py", line 224, in get
on_message=on_message,
File "/usr/lib/python3.6/site-packages/celery/backends/async.py", line 188, in wait_for_pending
for _ in self._wait_for_pending(result, **kwargs):
File "/usr/lib/python3.6/site-packages/celery/backends/async.py", line 255, in _wait_for_pending
on_interval=on_interval):
File "/usr/lib/python3.6/site-packages/celery/backends/async.py", line 56, in drain_events_until
yield self.wait_for(p, wait, timeout=1)
File "/usr/lib/python3.6/site-packages/celery/backends/async.py", line 65, in wait_for
wait(timeout=timeout)
File "/usr/lib/python3.6/site-packages/celery/backends/redis.py", line 119, in drain_events
m = self._pubsub.get_message(timeout=timeout)
File "/usr/lib/python3.6/site-packages/redis/client.py", line 2260, in get_message
response = self.parse_response(block=False, timeout=timeout)
File "/usr/lib/python3.6/site-packages/redis/client.py", line 2183, in parse_response
return self._execute(connection, connection.read_response)
File "/usr/lib/python3.6/site-packages/redis/client.py", line 2176, in _execute
return command(*args)
File "/usr/lib/python3.6/site-packages/redis/connection.py", line 579, in read_response
self.disconnect()
File "/usr/lib/python3.6/site-packages/redis/connection.py", line 530, in disconnect
self._sock.close()
AttributeError: 'NoneType' object has no attribute 'close'
А также
Traceback (most recent call last):
File "/usr/lib/python3.6/site-packages/redis/connection.py", line 344, in read_response
bufflen = self._sock.recv_into(self._buffer)
OSError: [Errno 9] Bad file descriptor
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3.6/site-packages/redis/client.py", line 2165, in _execute
return command(*args)
File "/usr/lib/python3.6/site-packages/redis/connection.py", line 577, in read_response
response = self._parser.read_response()
File "/usr/lib/python3.6/site-packages/redis/connection.py", line 357, in read_response
(e.args,))
redis.exceptions.ConnectionError: Error while reading from socket: (9, 'Bad file descriptor')
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "/usr/lib/python3.6/site-packages/redis/connection.py", line 577, in read_response
response = self._parser.read_response()
File "/usr/lib/python3.6/site-packages/redis/connection.py", line 362, in read_response
response = self._reader.gets()
redis.exceptions.InvalidResponse: Protocol error, got "t" as reply type byte
А также
File "/usr/lib/python3.6/site-packages/celery/result.py", line 224, in get
on_message=on_message,
File "/usr/lib/python3.6/site-packages/celery/backends/async.py", line 188, in wait_for_pending
for _ in self._wait_for_pending(result, **kwargs):
File "/usr/lib/python3.6/site-packages/celery/backends/async.py", line 255, in _wait_for_pending
on_interval=on_interval):
File "/usr/lib/python3.6/site-packages/celery/backends/async.py", line 56, in drain_events_until
yield self.wait_for(p, wait, timeout=1)
File "/usr/lib/python3.6/site-packages/celery/backends/async.py", line 65, in wait_for
wait(timeout=timeout)
File "/usr/lib/python3.6/site-packages/celery/backends/redis.py", line 119, in drain_events
m = self._pubsub.get_message(timeout=timeout)
File "/usr/lib/python3.6/site-packages/redis/client.py", line 2260, in get_message
response = self.parse_response(block=False, timeout=timeout)
File "/usr/lib/python3.6/site-packages/redis/client.py", line 2183, in parse_response
return self._execute(connection, connection.read_response)
File "/usr/lib/python3.6/site-packages/redis/client.py", line 2176, in _execute
return command(*args)
File "/usr/lib/python3.6/site-packages/redis/connection.py", line 577, in read_response
response = self._parser.read_response()
File "/usr/lib/python3.6/site-packages/redis/connection.py", line 359, in read_response
self._reader.feed(self._buffer, 0, bufflen)
AttributeError: 'NoneType' object has no attribute 'feed'
С точки зрения сельдерея, все эти задачи были выполнены успешно, и никаких исключений не было отправлено обратно. Я получаю эти исключения от процесса Python, который пытается получить результаты задачи.
Были ли какие-либо обновления по этой проблеме или возможные решения, которые я должен попробовать, не указанные в этой проблеме?
Вы работаете с несколькими потоками? Если это так, вы в некоторой степени соответствуете настройке Django, о которой я упоминал ранее. Если нет, то может быть что-то еще.
Мой текущий обходной путь заключается в том, чтобы никогда не использовать возвращаемые объекты результатов, а вместо этого отправлять и извлекать результаты непосредственно в / из структур данных Redis. Поскольку веб-запрос блокирует результаты, я обычно передаю «список» результатов, чтобы задача помещала ответы и вставляла в этот список в веб-запросе. Я буду использовать redis's blpop, чтобы получить результаты, пока я не получу их все или пока не закончу последний. Я использую флаг игнорирования результатов при отправке задачи, чтобы предотвратить создание канала прослушивания, и по-прежнему сохраню исходные задачи для регистрации идентификаторов и, возможно, возврата состояния позже.
Я не чувствовал себя комфортно с патчем, о котором я упоминал ранее; Мне нужно было что-то, что, как я знал, не будет иметь побочных эффектов.
@deterb Спасибо за ответ.
Я использую несколько потоков, поэтому считаю, что это очень похоже на то, что вы видите. Я потенциально мог бы сделать что-то подобное с дополнительным потоком, который, по сути, опросил бы redis на предмет результатов, но я бы очень хотел разобраться и в этом.
Я пошел дальше и собрал несколько контейнеров докеров, чтобы воспроизвести ошибку с помощью довольно простой настройки.
https://github.com/sihrc/celery_repro. Я сам продолжу изучать это через репо, но пока ничего не придумал. Мне потребуется немного времени, чтобы разобраться в тонкостях сельдерея и редиса.
Traceback (most recent call last):
File "/usr/lib/python3.6/runpy.py", line 193, in _run_module_as_main
"__main__", mod_spec)
File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
exec(code, run_globals)
File "/app/celery_race_condition/app.py", line 21, in <module>
results = [result.result() for result in futures]
File "/app/celery_race_condition/app.py", line 21, in <listcomp>
results = [result.result() for result in futures]
File "/usr/lib/python3.6/concurrent/futures/_base.py", line 432, in result
return self.__get_result()
File "/usr/lib/python3.6/concurrent/futures/_base.py", line 384, in __get_result
raise self._exception
File "/usr/lib/python3.6/concurrent/futures/thread.py", line 56, in run
result = self.fn(*self.args, **self.kwargs)
File "/app/celery_race_condition/app.py", line 11, in call_and_retrieve
async_result.wait(timeout=100, propagate=True)
File "/usr/lib/python3.6/site-packages/celery/result.py", line 224, in get
on_message=on_message,
File "/usr/lib/python3.6/site-packages/celery/backends/async.py", line 188, in wait_for_pending
for _ in self._wait_for_pending(result, **kwargs):
File "/usr/lib/python3.6/site-packages/celery/backends/async.py", line 255, in _wait_for_pending
on_interval=on_interval):
File "/usr/lib/python3.6/site-packages/celery/backends/async.py", line 56, in drain_events_until
yield self.wait_for(p, wait, timeout=1)
File "/usr/lib/python3.6/site-packages/celery/backends/async.py", line 65, in wait_for
wait(timeout=timeout)
File "/usr/lib/python3.6/site-packages/celery/backends/redis.py", line 119, in drain_events
m = self._pubsub.get_message(timeout=timeout)
File "/usr/lib/python3.6/site-packages/redis/client.py", line 2513, in get_message
response = self.parse_response(block=False, timeout=timeout)
File "/usr/lib/python3.6/site-packages/redis/client.py", line 2428, in parse_response
if not block and not connection.can_read(timeout=timeout):
File "/usr/lib/python3.6/site-packages/redis/connection.py", line 618, in can_read
return self._parser.can_read() or \
File "/usr/lib/python3.6/site-packages/redis/connection.py", line 372, in can_read
self._next_response = self._reader.gets()
redis.exceptions.InvalidResponse: Protocol error, got "\r" as reply type byte
С тех пор я занимаюсь отладкой и обнаружил, что как только поток, который использовался ранее, снова используется для получения результата, мы начинаем получать ошибки. Я проверил это, создав пул потоков из 100 и одновременно выполняя 101 задачу.
Для меня это сводится к потоковой безопасности ResultConsumer и, возможно, Redis 'клиента PubSub. В моем текущем обходном пути используется отдельный ResultConsumer для каждого локального контекста потока.
import threading
from celery.backends.redis import RedisBackend
local_context = threading.local()
class Backend(RedisBackend):
<strong i="7">@property</strong>
def result_consumer(self):
consumer = getattr(local_context, "consumer", None)
if consumer:
return consumer
local_context.consumer = self.ResultConsumer(
self, self.app, self.accept,
self._pending_results, self._pending_messages,
)
return local_context.consumer
@result_consumer.setter
def result_consumer(self, value):
local_context.consumer = value
Celery(
...,
result_backend=__name__ + ".Backend",
...
)
К сожалению, я не смог вникнуть в подробности, чтобы понять, почему это проблема для начала, но, надеюсь, этот обходной путь будет полезен для кого-то другого.
Celery довольно агрессивно относится к регистрации каналов в интерфейсе PubSub, который, как указано в документации redis-py,
Опубликованное вами решение имеет ту же тему, что и я, но выглядит чище.
@sihrc @deterb Я думаю, вы на правильном пути, это может быть связано с тем, что клиент PubSub не является потокобезопасным. @sihrc, использующий думали / пытались передать логику экземпляру клиента ResultConsumer
и PubSub
? В любом случае, если бы вы могли просто предоставить PR с этим решением, это было бы здорово!
@georgepsarakis Я попытался перенести логику в ResultConsumer и подбросил ее в PR. Полный отказ от ответственности: я не уверен, есть ли у меня время, чтобы вовремя активно протолкнуть этот пиар, но я сделаю все, что в моих силах.
@sihrc, спасибо! Это совершенно понятно, просто дайте нам знать, если вы не можете посвятить больше времени в какой-либо момент вперед.
Похоже, PR для решения неактивен. Есть ли какие-нибудь намерения решить эту проблему?
Наверное, после релиза.
@thedrow Спасибо за быстрый ответ. Вы хотите сказать, что после выпуска 4.3 (надеюсь, не 5.0) участники обратят внимание на эту ошибку?
да.
Мы также видели это в версии 4.2 недавно, в прошлом месяце.
@thedrow у вас есть приблизительная оценка сроков выхода версии 4.3?
4.3 выпущен еще в марте. Кроме того, 4.4rc2 находится на pypi, а 4.4 выйдет на этой неделе.
@auvipy Отлично , спасибо. Решается ли эта проблема в настоящее время?
не уверен но планируется на 4.5.
FWIW, даже с приведенным выше фрагментом кода , мы все еще видим периодические ошибки протокола, хотя и реже:
Traceback (most recent call last):
File "/opt/python/current/app/app/XXXX", line #, in _check_celery
result = async_result.get(timeout=self.service_timeout)
File "/opt/python/run/venv/local/lib/python3.6/site-packages/celery/result.py", line 226, in get
on_message=on_message,
File "/opt/python/run/venv/local/lib/python3.6/site-packages/celery/backends/asynchronous.py", line 188, in wait_for_pending
for _ in self._wait_for_pending(result, **kwargs):
File "/opt/python/run/venv/local/lib/python3.6/site-packages/celery/backends/asynchronous.py", line 255, in _wait_for_pending
on_interval=on_interval):
File "/opt/python/run/venv/local/lib/python3.6/site-packages/celery/backends/asynchronous.py", line 56, in drain_events_until
yield self.wait_for(p, wait, timeout=1)
File "/opt/python/run/venv/local/lib/python3.6/site-packages/celery/backends/asynchronous.py", line 65, in wait_for
wait(timeout=timeout)
File "/opt/python/run/venv/local/lib/python3.6/site-packages/celery/backends/redis.py", line 127, in drain_events
message = self._pubsub.get_message(timeout=timeout)
File "/opt/python/run/venv/local/lib/python3.6/site-packages/redis/client.py", line 3297, in get_message
response = self.parse_response(block=False, timeout=timeout)
File "/opt/python/run/venv/local/lib/python3.6/site-packages/redis/client.py", line 3185, in parse_response
response = self._execute(conn, conn.read_response)
File "/opt/python/run/venv/local/lib/python3.6/site-packages/redis/client.py", line 3159, in _execute
return command(*args, **kwargs)
File "/opt/python/run/venv/local/lib/python3.6/site-packages/redis/connection.py", line 700, in read_response
response = self._parser.read_response()
File "/opt/python/run/venv/local/lib/python3.6/site-packages/redis/connection.py", line 318, in read_response
(str(byte), str(response)))
redis.exceptions.InvalidResponse: Protocol Error: , b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00*3'
Ответ «все нули» - самый распространенный, хотя я только что видел, как проходит ProtocolError: 1, b'575932362]'
.
Я не сбрасываю со счетов возможность того, что это может быть просто сбой в Redis и не имеет ничего общего с Celery. Сложно сказать.
Это использует Celery 4.3.0, Kombu 4.6.6 и Redis 3.3.11.
Я начал часто получать эту ошибку после реализации асинхронного вызова. Приложение фляжки обращается к сельдерею. Иногда это срабатывает, а иногда я получаю результат x00:
web_1 | Traceback (most recent call last):
web_1 | File "/root/.local/share/virtualenvs/app-lp47FrbD/lib/python3.7/site-packages/flask/app.py", line 2463, in __call__
web_1 | return self.wsgi_app(environ, start_response)
web_1 | File "/root/.local/share/virtualenvs/app-lp47FrbD/lib/python3.7/site-packages/flask/app.py", line 2449, in wsgi_app
web_1 | response = self.handle_exception(e)
web_1 | File "/root/.local/share/virtualenvs/app-lp47FrbD/lib/python3.7/site-packages/flask/app.py", line 1866, in handle_exception
web_1 | reraise(exc_type, exc_value, tb)
web_1 | File "/root/.local/share/virtualenvs/app-lp47FrbD/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
web_1 | raise value
web_1 | File "/root/.local/share/virtualenvs/app-lp47FrbD/lib/python3.7/site-packages/flask/app.py", line 2446, in wsgi_app
web_1 | response = self.full_dispatch_request()
web_1 | File "/root/.local/share/virtualenvs/app-lp47FrbD/lib/python3.7/site-packages/flask/app.py", line 1951, in full_dispatch_request
web_1 | rv = self.handle_user_exception(e)
web_1 | File "/root/.local/share/virtualenvs/app-lp47FrbD/lib/python3.7/site-packages/flask/app.py", line 1820, in handle_user_exception
web_1 | reraise(exc_type, exc_value, tb)
web_1 | File "/root/.local/share/virtualenvs/app-lp47FrbD/lib/python3.7/site-packages/flask/_compat.py", line 39, in reraise
web_1 | raise value
web_1 | File "/root/.local/share/virtualenvs/app-lp47FrbD/lib/python3.7/site-packages/flask/app.py", line 1949, in full_dispatch_request
web_1 | rv = self.dispatch_request()
web_1 | File "/root/.local/share/virtualenvs/app-lp47FrbD/lib/python3.7/site-packages/flask/app.py", line 1935, in dispatch_request
web_1 | return self.view_functions[rule.endpoint](**req.view_args)
web_1 | File "/usr/src/app/web.py", line 81, in balances
web_1 | result = group(balance.s(name) for name in factories.LAZY_STRATEGY_MAP.keys())().get()
web_1 | File "/root/.local/share/virtualenvs/app-lp47FrbD/lib/python3.7/site-packages/celery/result.py", line 703, in get
web_1 | on_interval=on_interval,
web_1 | File "/root/.local/share/virtualenvs/app-lp47FrbD/lib/python3.7/site-packages/celery/result.py", line 822, in join_native
web_1 | on_message, on_interval):
web_1 | File "/root/.local/share/virtualenvs/app-lp47FrbD/lib/python3.7/site-packages/celery/backends/asynchronous.py", line 151, in iter_native
web_1 | for _ in self._wait_for_pending(result, no_ack=no_ack, **kwargs):
web_1 | File "/root/.local/share/virtualenvs/app-lp47FrbD/lib/python3.7/site-packages/celery/backends/asynchronous.py", line 268, in _wait_for_pending
web_1 | on_interval=on_interval):
web_1 | File "/root/.local/share/virtualenvs/app-lp47FrbD/lib/python3.7/site-packages/celery/backends/asynchronous.py", line 55, in drain_events_until
web_1 | yield self.wait_for(p, wait, timeout=interval)
web_1 | File "/root/.local/share/virtualenvs/app-lp47FrbD/lib/python3.7/site-packages/celery/backends/asynchronous.py", line 64, in wait_for
web_1 | wait(timeout=timeout)
web_1 | File "/root/.local/share/virtualenvs/app-lp47FrbD/lib/python3.7/site-packages/celery/backends/redis.py", line 161, in drain_events
web_1 | message = self._pubsub.get_message(timeout=timeout)
web_1 | File "/root/.local/share/virtualenvs/app-lp47FrbD/lib/python3.7/site-packages/redis/client.py", line 3565, in get_message
web_1 | response = self.parse_response(block=False, timeout=timeout)
web_1 | File "/root/.local/share/virtualenvs/app-lp47FrbD/lib/python3.7/site-packages/redis/client.py", line 3453, in parse_response
web_1 | response = self._execute(conn, conn.read_response)
web_1 | File "/root/.local/share/virtualenvs/app-lp47FrbD/lib/python3.7/site-packages/redis/client.py", line 3427, in _execute
web_1 | return command(*args, **kwargs)
web_1 | File "/root/.local/share/virtualenvs/app-lp47FrbD/lib/python3.7/site-packages/redis/connection.py", line 734, in read_response
web_1 | response = self._parser.read_response()
web_1 | File "/root/.local/share/virtualenvs/app-lp47FrbD/lib/python3.7/site-packages/redis/connection.py", line 324, in read_response
web_1 | (str(byte), str(response)))
web_1 | redis.exceptions.InvalidResponse: Protocol Error: , b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00*3'
Более странные ошибки:
web_1 | redis.exceptions.InvalidResponse: Protocol Error: s, b'ubscribe'
А также:
web_1 | File "/root/.local/share/virtualenvs/app-lp47FrbD/lib/python3.7/site-packages/redis/connection.py", line 350, in read_response
web_1 | response = self._buffer.read(length)
web_1 | AttributeError: 'NoneType' object has no attribute 'read'
Python 3.7, Celery 4.4.1, Redis 3.4.1.
Кто-нибудь из вас может попробовать этот патч https://github.com/celery/celery/pull/5145?
Кто-нибудь из вас может попробовать этот патч №5145?
Я бы не прочь попробовать, но отмечу, что в этом патче много разногласий, сборки с этим патчем терпят неудачу, и он простоял 17 месяцев. Похоже, что от идеи отказались.
сначала попробуйте и не прочь просмотреть PR и подготовить для него неудачный тест.
FWIW, сегодня подтвердил, что это все еще проблема с Celery 4.4.2, Kombu 4.6.8, Redis 3.4.1.
Учитывая, сколько лет этой проблеме, есть ли намек на ее причины? Я не думаю, что мой проект видел это [много] в последнее время, но, тем не менее, по крайней мере в паре проектов по-прежнему возникает эта ошибка.
@jheld Комментарии из https://github.com/celery/celery/issues/4363#issuecomment -411708951 соответствуют тому, что я видел в прошлый раз, когда ткнул в него, а именно, полученный потребитель инициализирует PubSub, который в конечном итоге распределяется между потоками и что PubSub не являются потокобезопасными. Мое обходное решение игнорировало результаты и сохраняло / просматривало результаты независимо от моего приложения Django.
Всем спасибо за отзывы. Просто чтобы прояснить несколько вещей:
ResultConsumer
- это компонент Redis Backend, который асинхронно получает результат.ResultConsumer
инициализирует экземплярPubSub
ResultConsumer
Экземпляры результаты не игнорируются.- один и тот же экземпляр
PubSub
не может использоваться одновременно из нескольких потоков- Что касается Рабочих, это изменение должно охватывать случай запуска новых вилок.
Я не знаю, можно ли выполнить соответствующие операции при запуске Django, возможно, этот обратный вызов может помочь; вызов
ResultConsumer.on_after_fork
затем создаст новые экземпляры, и проблема, скорее всего, не возникнет.
Ранее в этом выпуске кто-то упомянул, что это началось после обновления Django:
и обновил Django 1.11 (с 13 по .14 инкрементально))
Мы только что обновились с 1.11 до 2.2 и начали это видеть. Не могу представить почему, но подумал, что повторю сказанное выше.
Я только что проверил это https://github.com/andymccurdy/redis-py/issues/612#issuecomment -515019364, но не совсем уверен