Celery: كائن "قائمة" AttributeError ليس له سمة "فك تشفير" مع خلفية redis

تم إنشاؤها على ٣ نوفمبر ٢٠١٧  ·  55تعليقات  ·  مصدر: celery/celery

قائمة تدقيق

  • [X] لقد قمت بتضمين ناتج 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 ، كلاهما 2.x و 3.x

خطوات التكاثر

مرحبًا ، لست متأكدًا مما يمكن أن يسبب المشاكل وحاولت بالفعل إيجاد حل مماثل ، لكن لم يحالفني الحظ حتى الآن. لذلك فتح القضية هنا ، آمل أن يساعد

تم وصف المشكلة هناك أيضًا (ليس من قبلي): https://github.com/andymccurdy/redis-py/issues/612

حتى الآن ، فإن التجربة تحدث في كلتا الحالتين ، حيث تكون الخلفية متورطة وغير متضمنة (يعني فقط عند استدعاء apply_async(...) )

استثناء عند استدعاء apply_async()
attributeerror___list__object_has_no_attribute__decode_

استثناء عند استدعاء .get() (يحتوي هذا أيضًا على عدد صحيح ، بدلاً من القائمة)
attributeerror___list__object_has_no_attribute__decode_

آمل أن يساعد

سلوك متوقع

لعدم رمي الخطأ.

السلوك الفعلي

AttributeError: الكائن "قائمة" ليس له سمة "فك تشفير"

شكرا!

Redis Results Backend Bug Report

ال 55 كومينتر

أيضًا ، هناك تتبع مكدس كامل ، والذي يتضمن جميع المعلمات

attributeerror___list__object_has_no_attribute__decode

يبدو هذا وكأنه حالة سباق في تجمع اتصالات Redis من عمليات العمال المتزامنة. ما نوع تجمع العمال الذي تستخدمه؟ أعتقد أنه إذا كنت تستخدم تجمع بريفورك فلن تواجه هذه المشكلة. اسمحوا لي أن أعرف ما هي النتيجة ، إذا حاولت ذلك.

يا 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

بعض المعلومات الأخرى:

  • عمالنا على أجهزة مختلفة ثم التطبيق (الذي يرسل المهام)
  • لديها 2+ عاملين في كل وقت (كل مع نفس الإعدادات أدناه)
  • كل منهم يعمل في إطار systemd ( 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

آمل أن يؤدي ذلك إلى إجبار خاصية ذاكرة التخزين المؤقت للعميل على إنشاء عميل Redis جديد بعد كل مفترق عامل.

اسمحوا لي أن أعرف إذا كان هذا له أي نتيجة.

مرحبًا ، آسف للاستجابة الطويلة.

مجرد متابعة - قمنا فقط بتصحيحها وسنرى ما إذا كانت تساعد. آمل قريبا :)

شكرا للمساعدة :)

يتيح لنا معرفة ملاحظاتك

أهلا،

أواجه نفس المشكلة (باستخدام Celery.send_task ) ولدي سؤال متعلق بذلك:
لماذا، عندما تدعو مهمة المتزامن (بحيث لا يتوقع النتيجة)، الكرفس يبدأ في الاستماع ناشر رديس ( self._pubsub.subscribe(key) دعا بواسطة معالج إشارة celery/backends/redis.py ، خط 189، في on_task_call

https://github.com/celery/celery/blob/2547666a1ea13b27bc13ef296ae43a163ecd4ab3/celery/backends/redis.py#L197

georgepsarakis للأسف لم يساعد التصحيح. :-(

هنا تتبع مكدس آخر:
screen shot 2018-03-12 at 10 56 04
screen shot 2018-03-12 at 10 56 26
screen shot 2018-03-12 at 10 56 45
screen shot 2018-03-12 at 10 57 06

يرجى إعلامي إذا كنت تريد اختبار بعض السجلات أو تصحيحات أخرى.

wimby شكرًا جزيلاً على التعليقات. هل يمكنك إخباري ما هي الخيارات التي تستخدمها لبدء العامل؟

ياgeorgepsarakis
سأجيب على هذا السؤال بدلاً من @ wimby

هذا هو الأمر الكامل الذي نستخدمه لبدء الكرفس

celery multi start worker -A ... --pidfile=... --logfile=... --loglevel=... -E --time-limit=300 --autoscale=10,3 --queues=... -Ofair

واجهت نفس المشكلة عندما قررت تجربة وسيط redis ، التراجع إلى Rabbit في الوقت الحالي ..

أي تحديث على هذا؟ تشغيل هذا مع إعداد مشابه لـ 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 (عمليتان ، 15 مؤشر ترابط لكل منهما) ، ولا يقتصر الأمر على الاستثناء أعلاه فقط. لا يمكنني نسخ آثار المكدس الكاملة ولصقها. يقوم التطبيق بتحميل صفحة ويب بها مجموعة من طلبات ajax (90ish لكل تحميل صفحة) ، كل منها يدير مهمة خلفية عبر الكرفس. اكتملت المهام بنجاح ، ولكن استعادة النتائج أمر صعب.

عند إرسال المهام ، حصلت على ConnectionError s ، AttributeError s (القائمة لا تحتوي على ترميز سمة).

عند استعادة النتائج ، حصلت على InvalidResponse ، ResponseError ، ValueError ، AttributeError ، TypeError ، IndexError ، كلها تقريبًا من redis. لقد رأيت أيضًا أخطاء في فك التشفير من kombu عند محاولة تحليل استجابات json. أظن أن حركة المرور تختلط بين الطلبات - في بعض الحالات رأيت ما يشبه الردود الجزئية.

أنا أعود إلى القيام بمهام أكبر ، والتي من شأنها على الأقل تقليل حدوث ذلك. ومع ذلك ، فقد رأيت حالات لا تزال تحدث فيها مشكلات البروتوكول عندما يكون للأشياء حد أدنى من الحمل. لن أتفاجأ إذا كان هذا بسبب مشاكل الشبكات الأساسية. أنا أعمل منطقًا إضافيًا لإعادة المحاولة أيضًا لتقديم الطلبات.

deterb إذا قمت بتعيين task_ignore_result في تكوين الكرفس ، فيجب أن يمنع ذلك من الحدوث. ما لم تكن مهتمًا بنتائج المهام ، فمن الواضح أنها لن تساعد في هذه الحالة.

الإصلاح الذي يحترم ignore_result عند جدولة المهام تم إجراؤه في 4.2.0 ، لذلك يجب أن تكون جيدًا.

asgoel بيت القصيد هو الحصول على نتائج الجزء الأكبر من الطلبات (لا يتم تشغيلها كثيرًا في الخلفية بالنسبة لها ، ولكنها تجري عمليات حسابية ثقيلة على خادم مصمم لها بدلاً من خادم الويب). سأضيف نتيجة التجاهل للآخرين على الرغم من ذلك وأرى ما إذا كان ذلك يساعد.

deterb تبدو هذه مشكلة في العمليات غير الآمنة

georgepsarakis أوافق على أنها تبدو مشكلة تعدد مؤشرات الترابط. سأحاول تكوين mod_wsgi للتشغيل مع 15 عملية ، مؤشر ترابط واحد لكل عملية ومعرفة ما إذا كنت ما زلت أرى هذا السلوك. لا أقوم بأي خيوط إضافية أو معالجة متعددة خارج 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 2.7 إلى python 3.6
    (وتحديث Django 1.11 (تزايدي. 13 إلى .14))

لذا ، من المثير للاهتمام معرفة أن هذا قد يكون شيئًا يعتمد على إصدار بيثون. على أمل أنه قد يساعد في تتبع المشكلة.

(بالنسبة للباقي ، نستخدم الكرفس 4.2.1 و 2.10.6)

أيضًا ، تم إطلاق المهمة الإشكالية بواسطة Celery Beat ، وواجهنا نفس المشكلة في مهمة تم إطلاقها بواسطة مهمة أخرى

شكرا للجميع على ردود الفعل. فقط لتوضيح بعض الأشياء:

  • ResultConsumer هو مكون Redis Backend الذي يسترد النتيجة بشكل غير متزامن
  • ResultConsumer بتهيئة مثيل PubSub
  • يمكن إنشاء مثيلات ResultConsumer إما على عامل (مهام سير عمل Canvas) أو عند وضع مهمة في قائمة الانتظار ولا يتم تجاهل النتائج
  • لا يمكن استخدام مثيل PubSub نفسه في نفس الوقت من سلاسل رسائل متعددة
  • بالنسبة للعمال ، يجب أن يشمل هذا التغيير حالة بدء مفترقات جديدة

لا أعرف ما إذا كان بالإمكان إجراء العمليات المقابلة عند بدء تشغيل Django ، فربما يساعد رد الاتصال هذا ؛ سيؤدي استدعاء ResultConsumer.on_after_fork بعد ذلك إلى إنشاء مثيلات جديدة ولن تحدث المشكلة على الأرجح.

همم.

في حالتي ، حاولت إعادة إظهار المشكلة عن طريق زيادة التكرار في مهمة في Celery Beat. لدي حاليًا شيء مثل فشل واحد كل 15 مهمة ، ولكن الفشل يحدث في العامل ، وليس في Beat ، ولدى عاملي concurrency 1 (لدي عدة عمليات مستقلة ، لكنها كلها "فردية" ). يخبرني htop أن هناك عملية رئيسية مع سلسلة فرعية واحدة. هل سيكون ذلك متسقًا مع فرضيتك؟

إذا فهمت إجابتك بشكل صحيح (أنا أعيد قراءتها ، فقط للتأكد) ، الفقرة:

لا أعرف ما إذا كان بالإمكان إجراء العمليات المقابلة عند بدء تشغيل Django ، فربما يساعد رد الاتصال هذا ؛ سيؤدي استدعاء ResultConsumer.on_after_fork بعد ذلك إلى إنشاء مثيلات جديدة ومن المحتمل ألا تحدث المشكلة.

قد يكون هذا مرتبطًا بحالة سيكون لدينا فيها أخطاء عند نشر المهام من عمليات الويب الخاصة بنا (gunicorn / uwsgi / ...) ، أليس كذلك؟

بالنسبة للعمال ، يجب أن يشمل هذا التغيير حالة بدء مفترقات جديدة

في حالتنا ، تأتي هذه الأخطاء من العاملين حصريًا مع الكرفس 4.2.1. إذا فهمت بيانك بشكل صحيح ، فربما لا تزال هناك مشكلة ...

في إصدار الكرفس 4.2.0 ، تمكنت من التغلب على هذه المشكلة عن طريق القيام بما يلي:

  • قم بتصحيح celery.backends.redis.ResultConumer إما لتخزين _pubsub والاشتراك في thread.local أو لإضافة thread.local إلى قائمة الفئات الموروثة.
  • بعد استدعاء المهمة ومعالجة النتائج ، اتصل بـ:
    result.backend.result_consumer.cancel_for(result.task_id)
    result.backend.result_consumer.stop()

لم أحسب كيفية إزالة المكالمة للتوقف ؛ إذا قمت بإزالته ، تبدأ الاتصالات في التسريب (لاحظ أنني أعمل في بيئة mod_wsgi متصلة بـ redis عبر stunnel ؛ ينتهي الأمر بالحصول على مشكلات في الاتصال بعد بضع مئات من الطلبات). بعد فترة توقفت عن القدرة على الاتصال عبر النفق. أنا لم أحسب ربط إدارة موضوع mod_wsgi أيضا.

حالة الاختبار التي أستخدمها بها صفحة ويب تجعل 1500 Ajax تطلب 5 طلبات في وقت واحد تعمل بالكامل في أمر run_server من Django. بدون تحديد المعدل ، تحدث ظروف السباق على كائنات pubsub الخلفية الناتجة باستمرار. تحدث بانتظام مع تحديد المعدل أيضًا.

أظن أن استخدام ResultConsumers for redis للخيط المحلي يمكن أن يساعد في حل مشكلات عمال Greenlet أيضًا ، على الرغم من أنني لست متأكدًا من كيفية قيام المرء بإغلاق الحانات بشكل موثوق.

يعمل تبديل RedisBackend لاستخدام الاقتراع لتطبيق الويب بدون تغييرات خاصة بالتطبيق. لقد قمت بتبديل مزيج الواجهة الخلفية غير المتزامن مع base.SyncBackendMixin وعلقت على الأسطر التي تشير إلى المستهلك الناتج. حتى يتم إصلاح حل أكثر نظافة ، من المحتمل أن أقوم بتبديل تطبيق الويب لاستخدام إصدار SyncBackend من RedisBackend والسماح للعمال باستخدام الحل العادي (الذي يبدو أنه لا يعاني من هذه المشكلة.

نظرًا لأنني أستخدم Django ، كان الحل البديل الخاص بي هو استخدام نتائج django-celery. يتماشى نوعًا ما مع قرارات التصميم الأخرى لاستخدام redis بشكل أقل ، ونحن في الحقيقة لا نخزن الكثير على أي حال. لم نضع ذلك في الإنتاج بعد.

النتائج ثقيلة ومتعددة بما يكفي ولا أريد وضعها في قاعدة البيانات. كخيار أكثر تحفظًا ، قد أقوم بتخزين النتائج مباشرةً بدلاً من ترك الكرفس يفعل ذلك حتى يتم إصلاح ذلك (أنا أستخدم Redis بالفعل لبعض التخزين المؤقت).

أواجه مشكلة مماثلة لهذا. آمل أن تلقي المعلومات الإضافية التي أقدمها مزيدًا من الضوء على المشكلة.

لدي عامل كرفس واحد (التزامن = 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. نظرًا لأن طلب الويب يحظر النتائج ، فسأقوم عمومًا بتمرير "قائمة" نتيجة للمهمة لوضع الردود فيها والدفع إلى تلك القائمة في طلب الويب. سأستخدم blpop الخاص بـ redis للحصول على النتائج مرة أخرى حتى أحصل عليها جميعًا أو تنتهي المهلة في آخر مرة. أستخدم علامة تجاهل النتائج عند إرسال المهمة لمنع إنشاء قناة الاستماع ، وسأحتفظ بالمهام الأصلية لتسجيل المعرفات وربما استعادة الحالة لاحقًا.

لم أشعر بالراحة في الجري مع الرقعة التي ذكرتها سابقًا ؛ كنت بحاجة إلى شيء أعلم أنه لن يكون له آثار جانبية محتملة.

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 وربما عميل PubSub من redis. يستخدم الحل البديل الحالي 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",
    ...
)

لسوء الحظ ، لم أتمكن من البحث بشكل كافٍ لمعرفة سبب كون هذه مشكلة في البداية ، ولكن آمل أن يكون هذا الحل مفيدًا لشخص آخر.

يعتبر الكرفس عدوانيًا إلى حد ما فيما يتعلق بتسجيل القنوات في واجهة PubSub ، والتي تنص على أن وثائق redis-py

الحل الذي نشرته له نفس المظهر الذي قمت به ، على الرغم من أنه يبدو أنظف.

sihrcdeterb أعتقد أنك على الطريق الصحيح، وهذا يمكن أن يعزى إلى العميل PubSub في لا يجري الخيط آمنة. يبدو أن استخدام ResultConsumer و PubSub ؟ في كلتا الحالتين ، إذا كان بإمكانك فقط توفير العلاقات العامة مع هذا الحل ، فسيكون ذلك رائعًا!

georgepsarakis لقد حاولت نقل المنطق إلى

sihrc شكرا لك! هذا مفهوم تمامًا ، فقط أخبرنا إذا لم يكن بإمكانك تخصيص المزيد من الوقت في أي وقت للأمام.

يبدو أن العلاقات العامة للحل غير نشطة. هل هناك نية لإصلاح هذه المشكلة؟

ربما بعد الإصدار.

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 ولا علاقة له بالكرفس. من الصعب نوعًا ما معرفة ذلك.

يستخدم هذا الكرفس 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'

بايثون 3.7 ، كرفس 4.4.1 ، ريديس 3.4.1.

هل يمكن لأي منكم تجربة هذا التصحيح https://github.com/celery/celery/pull/5145؟

هل يمكن لأي منكم تجربة هذا التصحيح رقم 5145؟

لا أمانع في تجربته ، لكنني لاحظت أن هناك قدرًا كبيرًا من الخلاف حول النهج في ذلك التصحيح ، فالبناء مع هذا التصحيح يفشل ، وهو موجود منذ 17 شهرًا. يبدو أنه تم التخلي عن الفكرة.

جربها أولاً ولا تمانع في مراجعة العلاقات العامة وإعداد اختبار فاشل لها.

FWIW ، أكد اليوم أن هذا لا يزال يمثل مشكلة مع Celery 4.4.2 و Kombu 4.6.8 و Redis 3.4.1.

بالنظر إلى عمر هذه المشكلة ، هل هناك أي تلميح لما يسببها؟ لا أعتقد أن مشروعي قد شهد ذلك [كثيرًا] مؤخرًا ، ولكن مع ذلك ، ما زال هناك مشروعان على الأقل يشهدان حدوث هذا الخطأ.

تعليقات jheld من https://github.com/celery/celery/issues/4363#issuecomment -411708951 تتلاءم مع ما رأيته في المرة الأخيرة التي طعنت فيها - أي أن المستهلك الناتج يقوم بتهيئة PubSub والذي ينتهي بالمشاركة عبر سلاسل الرسائل و أن PubSubs ليست آمنة الخيط. كان الحل الذي أجريته هو تجاهل النتائج وحفظ / مشاهدة النتائج بشكل مستقل عن تطبيق Django.

شكرا للجميع على ردود الفعل. فقط لتوضيح بعض الأشياء:

  • ResultConsumer هو مكون Redis Backend الذي يسترد النتيجة بشكل غير متزامن
  • ResultConsumer بتهيئة مثيل PubSub
  • يمكن إنشاء مثيلات ResultConsumer إما على عامل (مهام سير عمل Canvas) أو عند وضع مهمة في قائمة الانتظار ولا يتم تجاهل النتائج
  • لا يمكن استخدام مثيل 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 ولكني لست متأكدًا تمامًا على الرغم من ذلك

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات