Celery: Objek 'daftar' AttributeError tidak memiliki atribut 'dekode' dengan redis backend

Dibuat pada 3 Nov 2017  ·  55Komentar  ·  Sumber: celery/celery

Daftar periksa

  • [X] Saya telah menyertakan output celery -A proj report dalam masalah ini.
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://:********@************************'

versi redis-server, keduanya 2.x dan 3.x

Langkah-langkah untuk mereproduksi

Halo, saya tidak yakin apa yang dapat menyebabkan masalah dan sudah mencoba mencari solusi serupa, tetapi sejauh ini tidak berhasil. Oleh karena itu membuka edisi di sini, semoga membantu

Masalahnya dijelaskan di sana juga (bukan oleh saya): https://github.com/andymccurdy/redis-py/issues/612

Sejauh ini pengalamannya, itu terjadi dalam kedua kasus, di mana backend terlibat dan tidak (berarti hanya ketika menelepon apply_async(...) )

Pengecualian saat memanggil apply_async()
attributeerror___list__object_has_no_attribute__decode_

Pengecualian saat memanggil .get() (juga yang ini memiliki int, bukan daftar)
attributeerror___list__object_has_no_attribute__decode_

Semoga membantu

Perilaku yang diharapkan

Untuk tidak membuang kesalahan.

Perilaku sebenarnya

AttributeError: objek 'daftar' tidak memiliki atribut 'decode'

Terima kasih!

Redis Results Backend Bug Report

Semua 55 komentar

Juga, ada jejak tumpukan penuh, yang mencakup semua parameter

attributeerror___list__object_has_no_attribute__decode

Ini sepertinya kondisi balapan di kumpulan koneksi Redis dari operasi pekerja bersamaan. Jenis kumpulan pekerja mana yang Anda gunakan? Saya pikir jika Anda menggunakan kolam prefork Anda tidak akan mengalami masalah ini. Beritahu saya apa hasilnya, jika Anda mencobanya.

Hai @georgepsarakis ,

terima kasih atas tanggapan Anda, tampaknya kami menjalankan prefork

ketika memeriksa output saat memulai seledri (yang berjalan di bawah systemd) saya mendapatkan output ini:

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

beberapa info lainnya:

  • pekerja kami berada di mesin yang berbeda dari aplikasi (yang mengirimkan tugas)
  • memiliki 2+ pekerja sepanjang waktu (semua dengan pengaturan yang sama di bawah)
  • semuanya berjalan di bawah systemd ( celery multi start -A ... -E --autoscale=12,3 -Ofair + beberapa agrumen lain yang tidak begitu penting, semuanya ditentukan sebagai layanan Type=forking )

beri tahu saya jika saya dapat menambahkan hal lain, ingin membantu sebanyak mungkin untuk menyelesaikannya :)

Terima kasih!

hal lain (meskipun saya tidak yakin apakah itu bisa terkait) - terkadang kami mendapatkan pengecualian ini (pengaturan yang sama seperti di atas)

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

kami mencoba memperbarui py-redis (akan melihat, tetapi hanya yang kecil)

setiap petunjuk sangat dihargai :)

@Twista dapatkah Anda mencoba tambalan di backend Redis?

Jika Anda dapat menambahkan di sini kode berikut:

def on_after_fork(self):
    logger.info('Resetting Redis client.')
    del self.backend.client

Saya harap ini akan memaksa properti yang di-cache klien untuk menghasilkan klien Redis baru setelah setiap fork pekerja.

Beri tahu saya jika ini ada hasilnya.

Hai, maaf lama responnya.

Hanya tindak lanjut - kami baru saja menambalnya dan akan melihat apakah itu membantu. Semoga secepatnya :)

terimakasih atas bantuannya :)

beri tahu kami tanggapan Anda

Hai,

Saya mengalami masalah yang hampir sama (menggunakan Celery.send_task ) dan memiliki pertanyaan terkait:
Mengapa, ketika memanggil tugas async (sehingga hasilnya tidak diharapkan ), Celery mulai mendengarkan penerbit redis ( self._pubsub.subscribe(key) dipanggil oleh penangan sinyal celery/backends/redis.py , baris 189, di on_task_call )?

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

@georgepsarakis sayangnya tambalannya tidak membantu. :-(

di sini adalah jejak tumpukan lain:
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

Tolong beri tahu saya jika Anda ingin beberapa log atau tambalan lain diuji.

@wimby terima kasih banyak atas umpan baliknya. Bisakah Anda memberi tahu saya opsi apa yang Anda gunakan untuk memulai pekerja?

Hai @georgepsarakis
Akan menjawab yang ini alih-alih @wimby

itu seluruh perintah yang kami gunakan untuk memulai seledri

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

dihadapkan dengan masalah yang sama ketika memutuskan untuk mencoba redis broker, digulung kembali ke kelinci untuk saat ini..

Ada pembaruan tentang ini? Menjalankan ini dengan pengaturan yang mirip dengan @Twista / @wimby di mana kami memiliki pekerja cron yang berjalan di mesin terpisah dari proses yang menjadwalkan tugas kami.

seledri==4.0.2
redis==2.10.5.

Untuk memperjelas, ini terjadi pada mesin yang mengirim tugas, bukan mesin pekerja.

Saya melihat ini juga. Seledri 4.2.0, kombu 4.2.1, redis 2.10.6. Menggunakan redis untuk broker dan hasilnya. Lalu lintas redis berjalan di antara server melalui koneksi stunnel terenkripsi melalui port.

Menjalankannya dalam Django 1.11 yang berjalan pada mod_wsgi (2 proses, masing-masing 15 utas), dan itu tidak hanya terbatas pada pengecualian di atas. Saya tidak dapat menyalin-menempel jejak tumpukan penuh. Aplikasi memuat halaman web dengan sekelompok permintaan ajax (90ish per pemuatan halaman), yang masing-masing menjalankan tugas latar belakang melalui seledri. Tugas selesai dengan sukses, tetapi mendapatkan hasilnya kembali sulit.

Saat mengirimkan tugas, saya mendapatkan ConnectionError s, AttributeError s (daftar tidak memiliki enkode atribut).

Ketika mendapatkan hasilnya kembali, saya mendapatkan InvalidResponse , ResponseError , ValueError , AttributeError , TypeError , IndexError , hampir semuanya dari redis. Saya juga melihat kesalahan dekode dari kombu ketika mencoba mengurai respons json. Saya menduga lalu lintas bercampur di antara permintaan - dalam beberapa kasus saya telah melihat apa yang tampak seperti tanggapan parsial.

Saya beralih kembali untuk melakukan tugas-tugas yang lebih besar, yang setidaknya harus meminimalkan hal ini terjadi. Namun, saya telah melihat kasus di mana masalah protokol masih akan terjadi ketika bebannya minimal. Saya tidak akan terkejut jika ini disebabkan oleh masalah jaringan yang mendasarinya. Saya juga menambahkan logika coba lagi untuk mengirimkan permintaan.

@deterb jika Anda menetapkan task_ignore_result dalam konfigurasi seledri Anda, ini akan mencegah hal ini terjadi. Kecuali Anda peduli dengan hasil tugas, dalam hal ini jelas tidak akan membantu.

Perbaikan untuk menghormati ignore_result saat menjadwalkan tugas dibuat di 4.2.0, jadi Anda harus baik-baik saja.

@asgoel Intinya adalah untuk mendapatkan hasil untuk sebagian besar permintaan (tidak begitu banyak berjalan di latar belakang untuk mereka, tetapi menjalankan perhitungan berat pada server yang dibuat untuk itu alih-alih server web). Saya akan menambahkan hasil abaikan untuk yang lain dan melihat apakah itu membantu.

@deterb ini terdengar seperti masalah dengan operasi yang tidak aman untuk utas. Apakah mungkin untuk mencoba tidak menggunakan multithreading?

@georgepsarakis Saya setuju bahwa kedengarannya seperti masalah multithreading. Saya akan mencoba mengonfigurasi mod_wsgi agar berjalan dengan 15 proses, 1 utas per proses dan melihat apakah saya masih melihat perilaku itu. Saya tidak melakukan threading atau multiprocessing tambahan di luar mod_wsgi. Saya melihat perilaku serupa (meskipun dengan frekuensi yang lebih sedikit) berjalan dengan runserver Django. Satu-satunya antarmuka dengan klien redis di aplikasi web adalah melalui Celery, yaitu pengiriman tugas dan pengambilan hasilnya. redis-py mengklaim sebagai thread-safe.

Saya akan mencoba melakukan lebih banyak pengujian besok, dan melihat apakah saya dapat membuat ulang di luar Django dan tanpa proxy stunnel.

@georgepsarakis Saya tidak mendapatkan kesempatan untuk mencoba hari ini, tapi saya percaya #4670 (yaitu objek PubSub dibagikan tidak menjadi thread safe) terkait, bersama dengan #4480. Sepertinya apa yang saya lihat terpisah dari tiket asli dan masalah lain yang disebutkan. Meskipun mungkin terkait dengan masalah yang diangkat @asgoel , saya pikir tiket terpisah untuk masalah konkurensi dengan backend hasil redis akan sesuai (tetap #4480 terkait dengan backend hasil RPC). Saya dapat mulai menarik bagian yang relevan dari jejak tumpukan juga.

Hai teman-teman, saya sering menghadapi kesalahan ini.
Saya menggunakan AWS Elasticache dan aplikasi saya diterapkan di AWS Elastic Beanstalk.
Kami mengubah AWS Elasticache menjadi Redis Labs dan kami meningkatkan batas waktu AWS Elastic Load Balancer menjadi 180-an dan server Apache menjadi 180-an juga.
Kesalahan berkurang banyak.

Namun demikian mereka masih terjadi dari waktu ke waktu. Oleh karena itu saya memutuskan untuk mengubah hasil backend ke PostgreSQL dan kemudian kesalahannya hilang sepenuhnya.

Hal yang menarik: kami mulai mengalami masalah ini setelah menerapkan rilis baru yang

  • beralih dari python 2.7 ke python 3.6
    (dan memperbarui Django 1.11 (tambahan. 13 ke .14))

Jadi, menarik untuk mengetahui ini mungkin sesuatu yang bergantung pada versi python. Berharap ini dapat membantu melacak masalah.

(Selebihnya, kami menggunakan seledri 4.2.1 dan redis 2.10.6)

Juga, tugas bermasalah diluncurkan oleh Celery beat, dan kami memiliki masalah yang sama dalam tugas yang diluncurkan oleh tugas lain

Terima kasih atas umpan balik semua orang. Hanya untuk memperjelas beberapa hal:

  • ResultConsumer adalah komponen Redis Backend yang secara asinkron mengambil hasilnya
  • ResultConsumer menginisialisasi instance PubSub
  • Instance ResultConsumer dapat dibuat baik pada pekerja (alur kerja Kanvas) atau saat Tugas diantrekan dan hasilnya tidak diabaikan
  • instance PubSub tidak dapat digunakan secara bersamaan dari beberapa utas
  • sejauh menyangkut Pekerja, perubahan ini harus mencakup kasus memulai garpu baru

Saya tidak mengetahui apakah operasi yang sesuai dapat dilakukan pada startup Django, mungkin callback ini dapat membantu; memanggil ResultConsumer.on_after_fork kemudian akan membuat instance baru dan masalah kemungkinan besar tidak akan terjadi.

Hmm.

Dalam kasus saya, saya telah mencoba mereproduksi masalah dengan meningkatkan frekuensi pada tugas di Celery Beat. Saat ini saya memiliki sesuatu seperti 1 kegagalan setiap 15 tugas, tetapi kegagalan terjadi pada pekerja, bukan di Beat, dan pekerja saya memiliki concurrency dari 1 (Saya memiliki beberapa proses independen, tetapi semuanya "tunggal" ). htop memberi tahu saya bahwa ada proses utama dengan satu utas anak. Apakah itu konsisten dengan hipotesis Anda?

Jika saya memahami dengan benar jawaban Anda (saya membaca ulang, hanya untuk memastikan), paragraf:

Saya tidak mengetahui apakah operasi yang sesuai dapat dilakukan pada startup Django, mungkin panggilan balik ini dapat membantu; memanggil ResultConsumer.on_after_fork kemudian akan membuat instance baru dan masalah kemungkinan besar tidak akan terjadi.

ini akan terkait dengan kasus di mana kami akan memiliki kesalahan saat menerbitkan tugas dari proses web kami (gunicorn/uwsgi/...), kan?

sejauh menyangkut Pekerja, perubahan ini harus mencakup kasus memulai garpu baru

Dalam kasus kami, kesalahan tersebut berasal dari pekerja secara eksklusif dengan Seledri 4.2.1. Jika saya memahami pernyataan Anda dengan benar, maka, mungkin masih ada masalah di sana ...

Di Celery 4.2.0, saya dapat mengatasi masalah ini dengan melakukan hal berikut:

  • Patch celery.backends.redis.ResultConsumer untuk menyimpan _pubsub dan berlangganan di thread.local atau untuk menambahkan thread.local ke daftar kelas yang diwarisi.
  • Setelah memanggil tugas dan memproses hasilnya, panggil:
    result.backend.result_consumer.cancel_for(result.task_id)
    result.backend.result_consumer.stop()

Saya belum menemukan cara untuk menghapus panggilan untuk berhenti; jika saya menghapusnya koneksi mulai bocor (perhatikan saya menjalankan di lingkungan mod_wsgi yang terhubung ke redis over stunnel; akhirnya mendapatkan masalah koneksi setelah beberapa ratus permintaan). Setelah beberapa saat saya berhenti bisa terhubung melalui terowongan. Saya juga belum menemukan cara untuk terhubung ke manajemen utas mod_wsgi.

Kasus uji yang saya gunakan memiliki halaman web yang membuat ~1500 Ajax meminta 5 sekaligus berjalan seluruhnya dalam perintah run_server Django. Tanpa batas kecepatan, kondisi balapan pada objek pubsub backend hasil terjadi terus-menerus. Mereka terjadi secara teratur dengan tingkat yang membatasi juga.

Saya menduga menggunakan utas ResultConsumers lokal untuk redis dapat membantu dengan masalah pekerja Greenlet juga, meskipun saya tidak yakin bagaimana cara menutup pubsubs dengan andal.

Mengalihkan RedisBackend ke menggunakan polling untuk aplikasi web berfungsi tanpa perubahan khusus aplikasi. Saya menukar mixin backend async dengan base.SyncBackendMixin dan mengomentari baris yang merujuk pada konsumen hasil. Sampai solusi yang lebih bersih diperbaiki, saya mungkin akan mengganti aplikasi web untuk menggunakan versi SyncBackend dari RedisBackend dan membiarkan para pekerja menggunakan yang normal (yang tampaknya tidak memiliki masalah ini.

Mengingat saya menggunakan Django, solusi saya adalah menggunakan Django-celery-results. Ini lebih sejalan dengan keputusan desain lain untuk menggunakan redis lebih sedikit, dan kami sebenarnya tidak menyimpan banyak. Kami belum memasukkannya ke dalam produksi.

Hasilnya cukup berat dan transitif saya tidak ingin memasukkannya ke dalam database. Sebagai opsi yang lebih konservatif, saya mungkin langsung menyimpan hasilnya daripada membiarkan Seledri melakukannya sampai ini diperbaiki (saya sudah menggunakan Redis untuk beberapa caching).

Saya mengalami masalah serupa dengan ini. Saya harap informasi tambahan yang saya berikan dapat menjelaskan lebih banyak masalah.

Saya memiliki satu pekerja seledri (konkurensi=1) dan server yang mendorong tugas ke antrian dan mengambil hasilnya. Saya menggunakan rabbitmq sebagai broker dan redis untuk backend hasil.

Saya menggunakan seledri 4.2.1 & hireis 0.2.0 (masalahnya terjadi tanpa hire juga).

Memanggil layanan pada kecepatan yang lebih lambat berfungsi sebagaimana dimaksud, tetapi begitu saya mengirim ~100 permintaan bersamaan, saya mulai melihat masalah yang telah dijelaskan dalam masalah ini. Saya memiliki beberapa pengecualian berbeda, yang saya yakin semuanya terkait.

  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'

Dan

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

Dan

  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'

Dari sudut pandang pekerja seledri, semua tugas ini berhasil dan tidak ada pengecualian yang dikirim kembali. Saya menerima pengecualian ini dari proses Python yang mencoba mengambil hasil tugas.

Apakah ada pembaruan tentang masalah ini, atau kemungkinan solusi yang harus saya coba tidak tercantum dalam masalah ini?

Apakah Anda menjalankan dengan banyak utas? Jika demikian, Anda agak cocok dengan pengaturan Django yang saya sebutkan sebelumnya. Jika tidak, itu mungkin sesuatu yang lain.

Solusi saya saat ini adalah tidak pernah benar-benar menggunakan objek hasil yang kembali dan sebaliknya mendorong dan menarik hasilnya langsung ke/dari struktur data Redis. Karena permintaan web memblokir hasil, saya biasanya akan memberikan "daftar" hasil untuk tugas untuk memasukkan tanggapan dan memasukkan daftar itu ke dalam permintaan web. Saya akan menggunakan blpop redis untuk mendapatkan hasilnya kembali sampai saya memiliki semuanya atau waktu habis pada yang terakhir. Saya menggunakan tanda abaikan hasil saat mengirimkan tugas untuk mencegah saluran pendengar dibuat, dan masih akan menyimpan tugas asli untuk mencatat id dan berpotensi mendapatkan status kembali nanti.

Saya tidak merasa nyaman berjalan dengan patch yang saya sebutkan sebelumnya; Saya membutuhkan sesuatu yang saya tahu tidak akan memiliki potensi efek samping.

@deterb Terima kasih atas tanggapannya.

Saya menjalankan dengan banyak utas, jadi saya yakin ini sangat mirip dengan apa yang Anda lihat. Saya berpotensi melakukan sesuatu yang serupa dengan utas tambahan yang pada dasarnya polling redis untuk hasil, tetapi saya benar-benar ingin membahasnya juga.

Saya melanjutkan dan mengumpulkan beberapa wadah buruh pelabuhan untuk mereproduksi bug dengan pengaturan yang cukup sederhana
https://github.com/sihrc/celery_repro. Saya akan terus memeriksanya sendiri melalui repo ini, tetapi sejauh ini saya tidak menemukan apa pun. Ini akan membawa saya sedikit untuk mendapatkan kecepatan dengan seluk-beluk seledri dan redis.

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

Saya telah melakukan debugging sejak dan saya menemukan bahwa segera setelah utas yang telah digunakan sebelumnya digunakan lagi untuk mengambil hasil, kami mulai mendapatkan kesalahan. Saya menguji ini dengan membuat kumpulan utas 100 dan menjalankan 101 tugas secara bersamaan.

Bagi saya itu telah bermuara pada keamanan utas dari ResultConsumer dan mungkin klien PubSub redis. Solusi saya saat ini menggunakan ResultConsumer terpisah per konteks lokal utas.

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",
    ...
)

Sayangnya, saya tidak bisa menggali cukup untuk mencari tahu mengapa ini menjadi masalah untuk memulai, tetapi mudah-mudahan, solusi ini berguna untuk orang lain.

Seledri cukup agresif dalam mendaftarkan saluran di antarmuka PubSub, yang menurut dokumentasi redis-py

Solusi yang Anda posting memiliki tema yang sama dengan yang saya lakukan, meskipun terlihat lebih bersih.

@sihrc @deterb Saya pikir Anda berada di jalur yang benar, ini dapat dikaitkan dengan klien PubSub yang tidak aman untuk thread. @sihrc menggunakan utas lokal untuk tujuan ini tampaknya benar bagi saya, apakah Anda mungkin mempertimbangkan/mencoba mentransfer logika ke ResultConsumer dan contoh klien PubSub ? Either way jika Anda bisa memberikan PR dengan solusi ini akan sangat bagus!

@georgepsarakis Saya telah mencoba untuk memindahkan logika ke ResultConsumer dan melemparkannya ke PR. Penafian total: Saya tidak yakin apakah saya punya waktu untuk secara aktif mendorong PR ini tepat waktu, tetapi saya akan melakukan yang terbaik.

@sihrc terima kasih! Itu sangat bisa dimengerti, beri tahu kami jika Anda tidak dapat mendedikasikan lebih banyak waktu kapan pun.

Sepertinya PR untuk solusinya tidak aktif. Apakah ada niat untuk memperbaiki masalah ini?

Mungkin setelah rilis.

@thedrow Terima kasih atas tanggapan cepatnya. Apakah Anda mengatakan setelah 4.3 dirilis (semoga bukan 5.0), kontributor akan mengalihkan perhatian mereka ke bug ini?

Ya.

Kami juga telah melihat ini pada versi 4.2 baru-baru ini dalam sebulan terakhir.

@thedrow apakah Anda memiliki perkiraan kasar tentang timeline untuk rilis 4.3?

4.3 dirilis kembali pada bulan Maret. juga, 4.4rc2 ada di pypi dan 4.4 akan dirilis minggu ini.

@auvipy oh bagus, terima kasih. Apakah masalah ini saat ini sedang ditangani?

tidak yakin tetapi direncanakan untuk 4,5.

FWIW, bahkan dengan potongan kode di atas , kami masih melihat Kesalahan Protokol berkala, meskipun lebih jarang:

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'

Respons semua-nol adalah yang paling umum, meskipun saya baru saja melihat ProtocolError: 1, b'575932362]' berlalu.

Saya tidak mengabaikan kemungkinan bahwa ini juga bisa menjadi kesalahan di Redis dan tidak ada hubungannya dengan Seledri sekalipun. Agak sulit untuk mengatakannya.

Ini menggunakan Seledri 4.3.0, Kombu 4.6.6, dan Redis 3.3.11

Saya mulai sering mendapatkan kesalahan ini setelah menerapkan panggilan async. Aplikasi labu memanggil seledri. Ini berfungsi beberapa kali dan kemudian yang lain saya mendapatkan hasil 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'

Lebih banyak kesalahan aneh:

web_1 | redis.exceptions.InvalidResponse: Protocol Error: s, b'ubscribe'

Dan:

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, Seledri 4.4.1, Redis 3.4.1.

adakah dari Anda yang bisa mencoba tambalan ini https://github.com/celery/celery/pull/5145?

adakah dari kalian yang bisa mencoba patch #5145 ini?

Saya tidak keberatan mencobanya, tetapi saya perhatikan ada banyak pertentangan tentang pendekatan di tambalan itu, build dengan tambalan itu gagal, dan sudah ada di sana selama 17 bulan. Sepertinya ide itu telah ditinggalkan.

cobalah dulu dan jangan 'keberatan meninjau PR dan menyiapkan tes yang gagal untuk itu.

FWIW, dikonfirmasi hari ini bahwa ini masih menjadi masalah dengan Seledri 4.4.2, Kombu 4.6.8, Redis 3.4.1.

Mempertimbangkan berapa lama masalah ini, apakah ada petunjuk tentang apa penyebabnya? Saya tidak berpikir proyek saya telah melihatnya [banyak] baru-baru ini, tetapi bagaimanapun, setidaknya beberapa proyek terus melihat bug ini terjadi.

@jheld Komentar dari https://github.com/celery/celery/issues/4363#issuecomment -411708951 sesuai dengan apa yang saya lihat terakhir kali saya menyodoknya - yaitu hasil konsumen menginisialisasi PubSub yang akhirnya dibagikan di seluruh utas dan bahwa PubSubs tidak aman untuk utas. Solusi saya mengabaikan hasil dan menyimpan/menonton hasil secara independen dari aplikasi Django saya.

Terima kasih atas umpan balik semua orang. Hanya untuk memperjelas beberapa hal:

  • ResultConsumer adalah komponen Redis Backend yang secara asinkron mengambil hasilnya
  • ResultConsumer menginisialisasi instance PubSub
  • Instance ResultConsumer dapat dibuat baik pada pekerja (alur kerja Kanvas) atau saat Tugas diantrekan dan hasilnya tidak diabaikan
  • instance PubSub tidak dapat digunakan secara bersamaan dari beberapa utas
  • sejauh menyangkut Pekerja, perubahan ini harus mencakup kasus memulai garpu baru

Saya tidak mengetahui apakah operasi yang sesuai dapat dilakukan pada startup Django, mungkin callback ini dapat membantu; memanggil ResultConsumer.on_after_fork kemudian akan membuat instance baru dan masalah kemungkinan besar tidak akan terjadi.

Sebelumnya dalam masalah ini, seseorang menyebutkan ini mulai terjadi setelah mereka memutakhirkan Django:

dan memperbarui Django 1.11 (tambahan. 13 ke .14))

Kami baru saja memutakhirkan dari 1,11 ke 2.2, dan kami mulai melihatnya. Saya tidak dapat membayangkan mengapa, tetapi saya pikir saya akan mengulangi hal di atas.

Saya baru saja memeriksa ini https://github.com/andymccurdy/redis-py/issues/612#issuecomment -515019364 tetapi tidak sepenuhnya yakin

Apakah halaman ini membantu?
0 / 5 - 0 peringkat