Celery: عامل الكرفس يستخدم 100٪ CPU حول epoll w / prefork + SQS ولكنه لا يزال يستهلك المهام

تم إنشاؤها على ٢٢ يناير ٢٠١٩  ·  45تعليقات  ·  مصدر: celery/celery

البيئة والإعدادات

نسخة الكرفس :

تقرير :

software -> celery:4.2.0 (windowlicker) kombu:4.2.2-post1 py:3.6.6
            billiard:3.5.0.5 sqs:N/A
platform -> system:Linux arch:64bit, ELF
            kernel version:3.13.0-139-generic imp:CPython
loader   -> celery.loaders.app.AppLoader
settings -> transport:sqs results:disabled

broker_url: 'sqs://localhost//'
include: [...]
worker_hijack_root_logger: False
task_serializer: 'json'
result_expires: 3600
accept_content: ['json']
result_serializer: 'json'
timezone: 'Europe/Berlin'
enable_utc: True
broker_transport_options: {
    'polling_interval': 1,
    'region': 'eu-west-1',
    'visibility_timeout': 10860}
task_ignore_result: True
task_acks_late: True
worker_prefetch_multiplier: 1
worker_max_tasks_per_child: 10
worker_pool: 'celery.concurrency.prefork:TaskPool'
task_time_limit: 10800
worker_enable_remote_control: False
worker_send_task_events: False
task_default_queue: 'celery'

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

التبعيات المطلوبة

  • الحد الأدنى من إصدار Python : 3.6
  • الحد الأدنى من إصدار الوسيط : N / A أو Unknown
  • الحد الأدنى من إصدار الواجهة الخلفية للنتيجة : N / A أو غير معروف
  • الحد الأدنى من نظام التشغيل و / أو إصدار Kernel : N / A أو Unknown

حالة اختبار قابلة للتكرار بالحد الأدنى

  • تحرير kombu/asynchronous/hub.py لإلغاء تعليق كشوفات الطباعة في create_loop
  • أطلق 500 مهمة قصيرة جدًا للتشغيل (لذا يجب أن تخرج عملية العامل الرئيسية وبدء عمال جدد بسبب worker_max_tasks_per_child: 10 )

سلوك متوقع

يجب أن تستقر عملية العامل الرئيسية على ~ 1٪ من استخدام وحدة المعالجة المركزية ، بعد تشغيل 500 مهمة. يجب عدم تشغيل كشوفات الطباعة في كثير من الأحيان بسبب استدعاء sleep في نهاية طريقة create_loop .

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

يدق العامل الرئيسي وحدة المعالجة المركزية بنسبة ثابتة 100٪ ويتم غمر ناتج وحدة التحكم (بسبب بيان الطباعة غير المعلق) بالكثير (تقريبًا كل ميكروثانية) من هؤلاء (لا يتوقف أبدًا):

WARNING:celery.redirected: [[[HUB]]]: (31)_event_process_exit(<Hub<strong i="12">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-32, started daemon)>)->R!, (20)on_result_readable(20)->R!, (34)_event_process_exit(<Hub<strong i="13">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-33, started daemon)>)->R!, (16)on_result_readable(16)->R!, (42)_event_process_exit(<Hub<strong i="14">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-34, started daemon)>)->R!, (24)on_result_readable(24)->R!, (53)_event_process_exit(<Hub<strong i="15">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-35, started daemon)>)->R!, (28)on_result_readable(28)->R!, (38)_event_process_exit(<Hub<strong i="16">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-36, started daemon)>)->R!, (12)on_result_readable(12)->R!, (45)on_readable(45)->R!, (45)on_writable(45)->W (2019-01-22 17:09:04,502; /home/ubuntu/.virtualenvs/unchained-worker3.6/src/celery/celery/utils/log.py:235)
WARNING:celery.redirected: [EVENTS]: (GONE)(48)->R, (GONE)(46)->R, (GONE)(44)->R! (2019-01-22 17:09:04,503; /home/ubuntu/.virtualenvs/unchained-worker3.6/src/celery/celery/utils/log.py:235)
WARNING:celery.redirected: [[[HUB]]]: (31)_event_process_exit(<Hub<strong i="17">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-32, started daemon)>)->R!, (20)on_result_readable(20)->R!, (34)_event_process_exit(<Hub<strong i="18">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-33, started daemon)>)->R!, (16)on_result_readable(16)->R!, (42)_event_process_exit(<Hub<strong i="19">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-34, started daemon)>)->R!, (24)on_result_readable(24)->R!, (53)_event_process_exit(<Hub<strong i="20">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-35, started daemon)>)->R!, (28)on_result_readable(28)->R!, (38)_event_process_exit(<Hub<strong i="21">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-36, started daemon)>)->R!, (12)on_result_readable(12)->R!, (45)on_readable(45)->R!, (45)on_writable(45)->W (2019-01-22 17:09:04,505; /home/ubuntu/.virtualenvs/unchained-worker3.6/src/celery/celery/utils/log.py:235)
WARNING:celery.redirected: [EVENTS]: (GONE)(48)->R, (GONE)(46)->R, (GONE)(44)->R! (2019-01-22 17:09:04,506; /home/ubuntu/.virtualenvs/unchained-worker3.6/src/celery/celery/utils/log.py:235)
WARNING:celery.redirected: [[[HUB]]]: (31)_event_process_exit(<Hub<strong i="22">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-32, started daemon)>)->R!, (20)on_result_readable(20)->R!, (34)_event_process_exit(<Hub<strong i="23">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-33, started daemon)>)->R!, (16)on_result_readable(16)->R!, (42)_event_process_exit(<Hub<strong i="24">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-34, started daemon)>)->R!, (24)on_result_readable(24)->R!, (53)_event_process_exit(<Hub<strong i="25">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-35, started daemon)>)->R!, (28)on_result_readable(28)->R!, (38)_event_process_exit(<Hub<strong i="26">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-36, started daemon)>)->R!, (12)on_result_readable(12)->R!, (45)on_readable(45)->R!, (45)on_writable(45)->W (2019-01-22 17:09:04,507; /home/ubuntu/.virtualenvs/unchained-worker3.6/src/celery/celery/utils/log.py:235)
WARNING:celery.redirected: [EVENTS]: (GONE)(48)->R, (GONE)(46)->R, (GONE)(44)->R! (2019-01-22 17:09:04,508; /home/ubuntu/.virtualenvs/unchained-worker3.6/src/celery/celery/utils/log.py:235)
WARNING:celery.redirected: [[[HUB]]]: (31)_event_process_exit(<Hub<strong i="27">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-32, started daemon)>)->R!, (20)on_result_readable(20)->R!, (34)_event_process_exit(<Hub<strong i="28">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-33, started daemon)>)->R!, (16)on_result_readable(16)->R!, (42)_event_process_exit(<Hub<strong i="29">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-34, started daemon)>)->R!, (24)on_result_readable(24)->R!, (53)_event_process_exit(<Hub<strong i="30">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-35, started daemon)>)->R!, (28)on_result_readable(28)->R!, (38)_event_process_exit(<Hub<strong i="31">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-36, started daemon)>)->R!, (12)on_result_readable(12)->R!, (45)on_readable(45)->R!, (45)on_writable(45)->W (2019-01-22 17:09:04,509; /home/ubuntu/.virtualenvs/unchained-worker3.6/src/celery/celery/utils/log.py:235)
WARNING:celery.redirected: [EVENTS]: (GONE)(48)->R, (GONE)(46)->R, (GONE)(44)->R! (2019-01-22 17:09:04,510; /home/ubuntu/.virtualenvs/unchained-worker3.6/src/celery/celery/utils/log.py:235)
WARNING:celery.redirected: [[[HUB]]]: (31)_event_process_exit(<Hub<strong i="32">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-32, started daemon)>)->R!, (20)on_result_readable(20)->R!, (34)_event_process_exit(<Hub<strong i="33">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-33, started daemon)>)->R!, (16)on_result_readable(16)->R!, (42)_event_process_exit(<Hub<strong i="34">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-34, started daemon)>)->R!, (24)on_result_readable(24)->R!, (53)_event_process_exit(<Hub<strong i="35">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-35, started daemon)>)->R!, (28)on_result_readable(28)->R!, (38)_event_process_exit(<Hub<strong i="36">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-36, started daemon)>)->R!, (12)on_result_readable(12)->R!, (45)on_readable(45)->R!, (45)on_writable(45)->W (2019-01-22 17:09:04,511; /home/ubuntu/.virtualenvs/unchained-worker3.6/src/celery/celery/utils/log.py:235)
WARNING:celery.redirected: [EVENTS]: (GONE)(48)->R, (GONE)(46)->R, (GONE)(44)->R! (2019-01-22 17:09:04,513; /home/ubuntu/.virtualenvs/unchained-worker3.6/src/celery/celery/utils/log.py:235)

أيضًا ، فإن strace مليء بما يلي:

epoll_ctl(9, EPOLL_CTL_DEL, 43, {EPOLLRDNORM|EPOLLRDBAND|EPOLLWRNORM|EPOLLERR|0x36f80000, {u32=32711, u64=4295000007}}) = -1 ENOENT (No such file or directory)
epoll_ctl(9, EPOLL_CTL_DEL, 46, {EPOLLERR|0x36ca1800, {u32=32711, u64=4295000007}}) = -1 EBADF (Bad file descriptor)
epoll_ctl(9, EPOLL_CTL_DEL, 44, {EPOLLERR|0x36ca1800, {u32=32711, u64=4295000007}}) = -1 EBADF (Bad file descriptor)
epoll_ctl(9, EPOLL_CTL_DEL, 19, {EPOLLRDNORM|EPOLLRDBAND|EPOLLWRBAND|EPOLLERR|EPOLLRDHUP|EPOLLONESHOT|0x1a0fd820, {u32=32711, u64=4295000007}}) = -1 ENOENT (No such file or directory)
epoll_ctl(9, EPOLL_CTL_DEL, 23, {EPOLLRDNORM|EPOLLRDBAND|EPOLLWRBAND|EPOLLERR|EPOLLRDHUP|EPOLLONESHOT|0x1a0fd820, {u32=32711, u64=4295000007}}) = -1 ENOENT (No such file or directory)
epoll_ctl(9, EPOLL_CTL_DEL, 27, {EPOLLRDNORM|EPOLLRDBAND|EPOLLWRBAND|EPOLLERR|EPOLLRDHUP|EPOLLONESHOT|0x1a0fd820, {u32=32711, u64=4295000007}}) = -1 ENOENT (No such file or directory)
epoll_ctl(9, EPOLL_CTL_DEL, 11, {EPOLLRDNORM|EPOLLRDBAND|EPOLLWRBAND|EPOLLERR|EPOLLRDHUP|EPOLLONESHOT|0x1a0fd820, {u32=32711, u64=4295000007}}) = -1 ENOENT (No such file or directory)
epoll_ctl(9, EPOLL_CTL_DEL, 15, {EPOLLRDNORM|EPOLLRDBAND|EPOLLWRBAND|EPOLLERR|EPOLLRDHUP|EPOLLONESHOT|0x1a0fd820, {u32=32711, u64=4295000007}}) = -1 ENOENT (No such file or directory)

كانت هناك مشكلات مماثلة حولها ، لكنها في معظم الأحيان كانت مرتبطة بـ redis . أيضًا ما يختلف عن المشكلات السابقة ، هو أن العامل لا يزال يوزع / يستهلك مهامًا جديدة. لذلك ، يعمل كل شيء بشكل أساسي ، لكن وحدة المعالجة المركزية لا تتباطأ ، ولكنها تطرق الحلقة بدلاً من ذلك. يمكنني إعادة إنتاج هذا باستمرار مع عامل SQS (التزامن 5 راجع للشغل) ، لذا يرجى إعلامي ، ما هي المعلومات الأخرى التي يمكنني جمعها لتعقب هذه المشكلة. شكرا!

Amazon SQS Broker Prefork Workers Pool Bug Report Critical

التعليق الأكثر فائدة

لذلك ، يتم استدعاء on_inqueue_close بنجاح ، ولكن في كل مرة ، مرة واحدة على الأقل بعد ذلك ، يتم استدعاء on_process_alive والخط https://github.com/celery/celery/blob/e7ae4290ef044de4ead45314d8fe2b190e497322/celery /concurrency/asynpool.py#L1083 يضيف 8 هناك.

ال 45 كومينتر

مرحبا مجددا. لا يبدو أن التحديثات الأخيرة تساعد. حاليا نحن في

billiard==3.6.0.0
celery==4.3.0
kombu==4.5.0
django-celery-beat==1.4.0

المشكلة برمتها تشبه إلى حد بعيد https://github.com/celery/celery/issues/1845 فقط أننا نستخدم SQS كوسيط. يبدو أن المشكلة ناتجة عن worker_max_tasks_per_child ، لأن العملية الرئيسية تذهب إلى وحدة المعالجة المركزية بنسبة 100٪ بمجرد أن تضطر إلى إعادة تشغيل أبنائها بسبب حد المهمة هذا.

لقد أجريت بعض التحقيقات الإضافية مع python gdb . إليك تعقب بيثون من خلال py-bt :

Traceback (most recent call first):
  <built-in method unregister of select.epoll object at remote 0x7fadac9b8600>
  File "$ENV/lib/python3.6/site-packages/kombu/utils/eventio.py", line 75, in unregister
    self._epoll.unregister(fd)
  File "$ENV/lib/python3.6/site-packages/kombu/asynchronous/hub.py", line 243, in _unregister
    self.poller.unregister(fd)
  File "$ENV/lib/python3.6/site-packages/kombu/asynchronous/hub.py", line 160, in _remove_from_loop
    self._unregister(fd)
  File "$ENV/lib/python3.6/site-packages/kombu/asynchronous/hub.py", line 181, in remove
    self._remove_from_loop(fd)
  File "$ENV/lib/python3.6/site-packages/celery/concurrency/asynpool.py", line 721, in <listcomp>
    [hub_remove(fd) for fd in diff(active_writes)]
  File "$ENV/lib/python3.6/site-packages/celery/concurrency/asynpool.py", line 721, in on_poll_start
    [hub_remove(fd) for fd in diff(active_writes)]
  File "$ENV/lib/python3.6/site-packages/kombu/asynchronous/hub.py", line 295, in create_loop
    tick_callback()
  <built-in method next of module object at remote 0x7fadcaf37638>
  File "$ENV/lib/python3.6/site-packages/celery/worker/loops.py", line 91, in asynloop
    next(loop)
  File "$ENV/lib/python3.6/site-packages/celery/worker/consumer/consumer.py", line 596, in start
    c.loop(*c.loop_args())
  File "$ENV/lib/python3.6/site-packages/celery/bootsteps.py", line 119, in start
    step.start(parent)
  File "$ENV/lib/python3.6/site-packages/celery/worker/consumer/consumer.py", line 318, in start
    blueprint.start(self)
  File "$ENV/lib/python3.6/site-packages/celery/bootsteps.py", line 369, in start
    return self.obj.start()
  File "$ENV/lib/python3.6/site-packages/celery/bootsteps.py", line 119, in start
    step.start(parent)
  File "$ENV/lib/python3.6/site-packages/celery/worker/worker.py", line 205, in start
    self.blueprint.start(self)
  File "$ENV/lib/python3.6/site-packages/celery/bin/worker.py", line 258, in run
    worker.start()
  File "$ENV/lib/python3.6/site-packages/celery/bin/base.py", line 252, in __call__
    ret = self.run(*args, **kwargs)
  File "$ENV/lib/python3.6/site-packages/celery/bin/worker.py", line 223, in run_from_argv
    return self(*args, **options)
  File "$ENV/lib/python3.6/site-packages/celery/bin/celery.py", line 420, in execute
    ).run_from_argv(self.prog_name, argv[1:], command=argv[0])
  File "$ENV/lib/python3.6/site-packages/celery/bin/celery.py", line 488, in handle_argv
    return self.execute(command, argv)
  File "$ENV/lib/python3.6/site-packages/celery/bin/base.py", line 298, in execute_from_commandline
    return self.handle_argv(self.prog_name, argv[1:])
  File "$ENV/lib/python3.6/site-packages/celery/bin/celery.py", line 496, in execute_from_commandline
    super(CeleryCommand, self).execute_from_commandline(argv)))
  File "$ENV/lib/python3.6/site-packages/celery/bin/celery.py", line 322, in main
    cmd.execute_from_commandline(argv)
  File "$ENV/lib/python3.6/site-packages/celery/__main__.py", line 16, in main
    _main()
  File "$ENV/lib/python3.6/site-packages/celery/__main__.py", line 20, in <module>
    main()
  <built-in method exec of module object at remote 0x7fadcaf37638>
  File "/usr/lib/python3.6/runpy.py", line 85, in _run_code
    exec(code, run_globals)
  File "/usr/lib/python3.6/runpy.py", line 193, in _run_module_as_main
    "__main__", mod_spec)

فشل في تنفيذ self._epoll.unregister(fd) الذي يرتبط بشكل جيد بمخرجات strace قمت بنشرها في الأصل. تخطو لفترة من الوقت انتهى بي المطاف في

 241        def _unregister(self, fd):
 242            try:
 243                self.poller.unregister(fd)
 244            except (AttributeError, KeyError, OSError):
>245                pass
 246    
 247        def close(self, *args):
 248            [self._unregister(fd) for fd in self.readers]
 249            self.readers.clear()
 250            [self._unregister(fd) for fd in self.writers]

لذا من الواضح أن unregister يثير استثناءً.

FDs هناك مثل 27 ، 11 ، 15 كلها أنابيب وفقًا لـ /proc/$PID/fd . هناك يمكنني الحصول على الأنابيب FDs ووفقًا لـ lsof | grep $PIPE_FD فهي عبارة عن أنابيب بين العملية الرئيسية والعمليات الفرعية:

15614 هي العملية الرئيسية و 23179 سبيل المثال عملية فرعية:

python3.6 15614              ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 15614              ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18344              ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18344              ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18344 18346        ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18344 18346        ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18368              ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18368              ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18368 18406        ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18368 18406        ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18369              ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18369              ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18369 18382        ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18369 18382        ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18370              ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18370              ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18370 18389        ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18370 18389        ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18371              ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18371              ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18371 18391        ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18371 18391        ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18372              ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18372              ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18372 18390        ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18372 18390        ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18373              ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18373              ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18373 18393        ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18373 18393        ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18375              ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18375              ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18375 18405        ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18375 18405        ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18376              ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18376              ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18376 18395        ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18376 18395        ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18378              ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18378              ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18378 18403        ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18378 18403        ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18380              ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18380 18400        ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18383              ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18383              ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18383 18404        ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18383 18404        ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18384              ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18384              ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18384 18424        ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18384 18424        ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18386              ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18386              ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18386 18420        ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18386 18420        ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18387              ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18387              ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18387 18423        ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18387 18423        ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18396              ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18396              ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18396 18426        ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18396 18426        ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18397              ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18397              ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18397 18422        ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18397 18422        ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18399              ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18399              ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 18399 18425        ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 18399 18425        ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 23122              ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 23122              ubuntu   40w     FIFO                0,8      0t0   49209822 pipe
python3.6 23179              ubuntu   39r     FIFO                0,8      0t0   49209822 pipe
python3.6 23179              ubuntu   40w     FIFO                0,8      0t0   49209822 pipe

بدون معرفة الكود kombu / billiard يبدو لي أنه تم إعادة تشغيل العمليات الفرعية بنجاح ولكن بطريقة ما العملية الرئيسية لا تدرك ذلك وتستمر في محاولة التخلص من العمليات القديمة ، التي لم تعد موجودة.

أنت الآن توصلنا إلى نتيجة يمكننا استخدامها لتصحيح هذا الأمر بشكل أكبر.
هل تمانع في تغيير الكود بحيث يظهر الاستثناء؟

إذا كان الاستثناء يشكو من واصف ملف غير موجود ، فربما يمكننا ببساطة إزالته من قائمة القراء / الكتاب.

لذلك ربما لا ينبغي تجاوز هذا الاستثناء وتجاهله بصمت.
سأجعل الكرفس يصدر سجلًا لهذه الإخفاقات.

شكرا لدعمك.

في kombu eventio.py اضطررت إلى تعديل _epoll لعدم ابتلاع الاستثناءات (لذلك أعلاه ربما لم أصادف استثناءً. ربما اعتقد مصحح أخطاء جنوم أنه كان في ذلك المسار). باستخدام هذا يمكنني إظهار جميع الأخطاء غير المسجلة المتكررة:

    def unregister(self, fd):
        try:
            self._epoll.unregister(fd)
        except (socket.error, ValueError, KeyError, TypeError):
            raise
        except (IOError, OSError) as exc:
            raise
            if getattr(exc, 'errno', None) not in (errno.ENOENT, errno.EPERM):
                raise

بحيث يمكنني القيام بذلك في hub.py

    def _unregister(self, fd):
        try:
            self.poller.unregister(fd)
        except (AttributeError, KeyError, OSError):
            logger.exception("pls debug me, fd = %s", fd, extra={'fd_debug': fd})

مع العديد من الأرقام fd تتكرر آلاف المرات في غضون ثوانٍ.

على سبيل المثال ، كانت الرسالة pls debug me, fd = 56 مع traceback

Traceback (most recent call last):
  File ".../kombu/asynchronous/hub.py", line 243, in _unregister
    self.poller.unregister(fd)
  File ".../kombu/utils/eventio.py", line 78, in unregister
    self._epoll.unregister(fd)
FileNotFoundError: [Errno 2] No such file or directory

تتجاهل الوحدة eventio هذه الاستثناءات على وجه التحديد ولا ترفعها.

يمكننا إما:

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

هل يمكنك التحقق مما إذا كان # 5499 يساعد بأي شكل من الأشكال؟

سأجربها.

للأسف لا يساعد. اعتمادًا على الإعداد ( -Ofair مقابل -Odefault ) لا يزال العامل يشعر بالجنون عند هذه الأسطر:

https://github.com/celery/celery/blob/master/celery/concurrency/asynpool.py#L735
https://github.com/celery/celery/blob/master/celery/concurrency/asynpool.py#L742

تعديل:
وفي السجلات لا أجد أي تكرارات لـ Encountered OSError while trying .

لا افهم لماذا.
إذا كنا نتصل بـ hub.remove فإننا نتصل في النهاية
hub._discard يجب أن يزيل واصف الملف من كل من القراء والكتاب.
إذا أضفت نقطة توقف هناك ، فهل ترى إزالة fd؟
هل تمت إضافته مرة أخرى في مكان ما؟

لذلك بالنسبة للحالة -Ofair ، قمت بتعديل on_poll_start كما يلي:

            def on_poll_start():
                if outbound and len(busy_workers) < len(all_inqueues):
                    #  print('ALL: %r ACTIVE: %r' % (len(all_inqueues),
                    #                                len(active_writes)))
                    inactive = diff(active_writes)
                    [hub_add(fd, None, WRITE | ERR, consolidate=True)
                     for fd in inactive]
                else:
                    for fd in diff(active_writes):
                        aw = list(self._active_writes)
                        ai = list(self._all_inqueues)
                        result = hub_remove(fd)
                        extra = {
                            'fd': fd,
                            'result': repr(result),
                            'active_writes': aw,
                            'all_inqueues': ai,
                        }
                        logger.warn('removed fd %r result %r aw %r ai %r', fd, result, aw, ai, extra=extra)

أسجل self._active_writes و self._all_inqueues ، لأن diff(active_writes) يبدو أنه يعادل self._all_inqueues - self._active_writes .

ثم بدأت العامل بـ --concurrency 1 و worker_max_tasks_per_child=10 و delay ed بعض المهام ، لذلك يجب أن يتم تعيين إعادة التشغيل بسرعة.

حتى قبل إعادة التشغيل ، كانت سجلاتي مليئة بهذا

removed fd 8 result None aw [] ai [8]
removed fd 8 result None aw [] ai [8]
removed fd 8 result None aw [] ai [8]

التردد ووحدة المعالجة المركزية ليست عالية بعد . ولكن بمجرد حدوث إعادة التشغيل وعدم وصول المزيد من المهام ، يصبح هذا الأمر مجنونًا إلى 7000 سجل لكل 5 ثوانٍ. من البداية إلى النهاية ، لا يتغير fd 8 أبدًا. بالنسبة لي ، يبدو أن AsynPool._all_inqueues لم يتم تنظيفه بشكل صحيح. تصحيح الأخطاء on_inqueue_close الآن.

لذلك ، يتم استدعاء on_inqueue_close بنجاح ، ولكن في كل مرة ، مرة واحدة على الأقل بعد ذلك ، يتم استدعاء on_process_alive والخط https://github.com/celery/celery/blob/e7ae4290ef044de4ead45314d8fe2b190e497322/celery /concurrency/asynpool.py#L1083 يضيف 8 هناك.

إذن لا نزيل العملية من البركة بعد أن يعيد العامل تدوير نفسه؟

عندما نبدأ عملية نلحق العامل بالمجمع:

https://github.com/celery/billiard/blob/265f119ec8b2944e36e3b0810578b5d615e6f987/billiard/pool.py#L1102 -L1123

لا أرى أي شخص يتصل بـ _join_exited_workers() والذي يجب أن ينظفهم من البركة.

هل يمكن أن تكون هذه هي المشكلة؟
هل يمكنك التحقق من فضلك؟

لست متأكدًا بعد الآن ، إذا كان هذا هو الجاني العام. أعني ، بعد كل شيء ، يجب أن تكون بعض التعليمات البرمجية مسؤولة عن عملية التنظيف ليتم استدعاؤها بشكل متكرر. لذلك قمت بتحريك traceback لوضع بعض عمليات تسجيل الدخول إلى حلقة المحور في https://github.com/celery/kombu/blob/master/kombu/asynchronous/hub.py#L301 :

…
poll_timeout = fire_timers(propagate=propagate) if scheduled else 1
logger.info('new loop run with poll_timeout %r', poll_timeout)
if readers or writers:
    …

دعني أوضح لك كيف أحلل رسالة السجل هذه الآن باستخدام kibana:

Screenshot_20190521_102310

  1. أقوم بتشغيل 20 مهمة لأقصى 10 مهام لكل طفل
  2. يعمل العامل على أول 10 مهام
  3. يقوم العامل بإعادة التشغيل بفترة قصيرة تبلغ 100٪ من وحدة المعالجة المركزية والكثير من عمليات التشغيل الحلقية
  4. يعمل العامل على آخر 10 مهام
  5. تمت إعادة تشغيل العامل مرة أخرى بسبب الحد الأقصى من المهام لكل طفل. الحلقة تصبح مجنونة إلى الأبد
  6. إعادة تشغيل العامل يدويًا. لا تصل أي مهام جديدة وتعمل الحلقة بمعدل معقول

بالنسبة لي ، يبدو أن هناك شيئًا ما يقطع الدائرة وهذا هو سبب انتقال وحدة المعالجة المركزية إلى 100 ٪. ومع ذلك ، ليس لدي أي فكرة عن كيفية تصحيح هذا الأمر بشكل فعال. هناك العديد من عبارات except و continue بداخلها.

هل يمكنك التحقق من فضلك؟

لست متأكدا ، فهمت. هل تريد مني التحقق ، إذا تم استدعاء _join_exited_workers ؟

لقد قمت بتحليل الحلقة بالكثير من عبارات السجل ، ولكن كل ما يمكنني قوله ، هو أنها كانت في معظم الأحيان تصل إلى https://github.com/celery/kombu/blob/master/kombu/asynchronous/hub.py#L362 ، وهو أمر طبيعي أعتقد. لذلك هناك طريقة فقط للعديد من الأحداث التي لا يبدو أنها تختفي. تمامًا كما في رسالتي الأصلية:

WARNING:celery.redirected: [[[HUB]]]: (31)_event_process_exit(<Hub<strong i="7">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-32, started daemon)>)->R!, (20)on_result_readable(20)->R!, (34)_event_process_exit(<Hub<strong i="8">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-33, started daemon)>)->R!, (16)on_result_readable(16)->R!, (42)_event_process_exit(<Hub<strong i="9">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-34, started daemon)>)->R!, (24)on_result_readable(24)->R!, (53)_event_process_exit(<Hub<strong i="10">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-35, started daemon)>)->R!, (28)on_result_readable(28)->R!, (38)_event_process_exit(<Hub<strong i="11">@0x7fa987a1dc18</strong>: R:11 W:1>, <ForkProcess(ForkPoolWorker-36, started daemon)>)->R!, (12)on_result_readable(12)->R!, (45)on_readable(45)->R!, (45)on_writable(45)->W (2019-01-22 17:09:04,502; /home/ubuntu/.virtualenvs/unchained-worker3.6/src/celery/celery/utils/log.py:235)
WARNING:celery.redirected: [EVENTS]: (GONE)(48)->R, (GONE)(46)->R, (GONE)(44)->R! (2019-01-22 17:09:04,503; /home/ubuntu/.virtualenvs/unchained-worker3.6/src/celery/celery/utils/log.py:235)

هل يمكنك التحقق من فضلك؟

لست متأكدا ، فهمت. هل تريد مني التحقق ، إذا تم استدعاء _join_exited_workers ؟

نعم.
ربما تضيفه إلى الجزء العلوي من الحلقة أيضًا ...

هل من الممكن أن يحتوي رمز الكرفس بالفعل على تعليق يصف هذا يمكن أن يحدث؟

https://github.com/celery/celery/pull/5499/files#diff -c80fd9891efbbe68275a133d83cd22a4L456

    def _track_child_process(self, proc, hub):
        try:
            fd = proc._sentinel_poll
        except AttributeError:
            # we need to duplicate the fd here to carefully
            # control when the fd is removed from the process table,
            # as once the original fd is closed we cannot unregister
            # the fd from epoll(7) anymore, causing a 100% CPU poll loop.
            fd = proc._sentinel_poll = os.dup(proc._popen.sentinel)
        hub.add_reader(fd, self._event_process_exit, hub, proc)

هل يمكنك مشاركة الأمر الذي تقوم بتشغيله tuky لإطلاق عامل الكرفس لتقرير الخطأ هذا؟ هل من الممكن أنك تستخدم دقات القلب وليس لديك - بدون اختلاط - بدون ثرثرة؟

كان هذا التزامًا أصليًا مرة أخرى عندما كان Ask يحاول حل هذه المشكلة لـ Celery 3 وأرجعها إلى epoll https://github.com/celery/celery/commit/ca57e722b25f8fca817084ec7562be3698c7ee02
وذكر في الالتزام أن:

1) تعرض epoll_wait دائمًا حالة خطأ لأنبوب Popen fd.
2) كان العامل يحاول إلغاء تسجيل هذا fd من epoll ، لكن
3) epoll.unregister رفض القيام بذلك مما أعطى خطأ IOError (EnOENT)
خطأ.

لذلك اتضح أن هذا أمر غريب ، والحل هو تكرار الأنبوب fd
حتى نتمكن من التحكم بدقة في وقت إزالته من العملية
جدول واصف الملف.

هل يمكنك مشاركة الأمر الذي تقوم بتشغيله tuky لإطلاق عامل الكرفس لتقرير الخطأ هذا؟

شكرا لك على التحقيقات الخاصة بك. هنا تذهب @ ماتيوس

python3.6 -m celery worker --loglevel=INFO --app=foo.worker -Ofair --concurrency=1 --logfile=/var/log/celery/foo.log --pidfile=/var/run/celery/foo.pid --hostname=foo<strong i="9">@bar</strong>

هل يمكنك أن تعرف من ذلك ، ما إذا كنت أستخدم دقات القلب؟ على الأقل ، لا أستخدم الخيار --without-heartbeat . هل يجب أن أحاول الجري بكل الخيارات الثلاثة --without-gossip --without-mingle --without-heartbeat ؟

tuky سيكون الأمر يستحق المحاولة من أجل العلم ، على الرغم من أنني أشك الآن بعد قراءة المزيد من التعليمات البرمجية الليلة الماضية أن المشكلة متأصلة في دعم SQS و / أو كيفية استخدامه فقط. لقد بدأت العمل على تغيير رمز مشابه لعلاقاتي العامة الأخرى التي تم دمجها وربطها هنا سابقًا والتي ستحاول أيضًا قبول نقاط واصف الملفات الأخرى في مجموعة Async - على أمل قضاء المزيد من الوقت عليها في وقت لاحق اليوم ولكنها ستفعل يستغرق وقتًا لفهم كل شيء حيث يوجد الكثير. هل هناك فرصة للعمل على اختبار وحدة نمط TDD الذي يعيد إنتاج مسألة ENOENT بطريقة ما؟

لاحظ أيضًا (منذ أن طلبت): تجربتي مع دقات القلب موجودة على وسيط RabbitMQ AMQP حيث أعتقد أن الوسيط مُعد لإرسال ضربات الحرارة وفي هذه الحالة يجب أن يستجيب العامل خلال فترة النبضات أو ينقطع الاتصال - - نحن نعمل بهذه الطريقة وباستخدام الأعلام - بدون ثرثرة - بدون اختلاط لأن تلك البروتوكولات كانت ثرثرة للغاية مما تسبب في ظهور بعض أخطاء الأداء مرة أخرى على Celery 3.

حاولت الجري --without-gossip --without-mingle --without-heartbeat ، لكن دون جدوى. thedrow لقد تحققت أخيرًا من أن _join_exited_workers يتم الاتصال به بالفعل من وقت لآخر.

سأحاول التوصل إلى اختبار مشابه لاختبار https://github.com/celery/celery/pull/5499/files ، ولكن من المحتمل أن أفشل ، لأنني أعرف القليل جدًا من التفاصيل: crossed_fingers:

tuky حسنًا ، لقد سأمنحك فرصة لكتابة اختبار الوحدة هذا والتركيز أكثر على تغيير الكود وإعادة البناء لأنه بدا مشابهًا بشكل غريب للعلاقات العامة الأخرى التي تم ربطها. لذلك لدي الآن هذه العلاقات العامة الجديدة خصيصًا من أجل هذه التذكرة - ربما يمكنك تجربة هذه التغييرات لمعرفة ما إذا كان ذلك يحل الخطأ أم لا ويمكننا مواصلة العمل نحو الاختبارات. أخطط لعدم كتابة اختبارات الوحدة في نهاية هذا الأسبوع لأن هذا كان أعلى مما اعتقدت أنني سأحصل عليه حتى: https://github.com/celery/celery/pull/5604/files

هو موضع تقدير كبير الجهد. ومع ذلك ، فإنه لا يساعد في حالتنا. ليس هناك ورود لـ Encountered OSError when accessing fd في سجلاتي. فقط عندما أقوم بإلغاء تنشيط ابتلاع الاستثناء في https://github.com/celery/kombu/blob/master/kombu/asynchronous/hub.py#L245 و https://github.com/celery/kombu/blob/master/ kombu / utils / eventio.py # L79 ، أحصل على هذا مباشرة بعد بدء العامل:

Traceback (most recent call last):
  File "venv/src/celery/celery/concurrency/asynpool.py", line 240, in iterate_file_descriptors_safely
    hub_method(fd, *hub_args, **hub_kwargs)
  File "venv/lib/python3.6/site-packages/kombu/asynchronous/hub.py", line 181, in remove
    self._remove_from_loop(fd)
  File "venv/lib/python3.6/site-packages/kombu/asynchronous/hub.py", line 160, in _remove_from_loop
    self._unregister(fd)
  File "venv/lib/python3.6/site-packages/kombu/asynchronous/hub.py", line 243, in _unregister
    self.poller.unregister(fd)
  File "venv/lib/python3.6/site-packages/kombu/utils/eventio.py", line 75, in unregister
    self._epoll.unregister(fd)
FileNotFoundError: [Errno 2] No such file or directory

يعمل هذا على إصلاح حلقة وحدة المعالجة المركزية بنسبة 100٪ ، ولكن يمكن استهلاك حلقة واحدة فقط ثم تكون ميتة.

tuky يبدو أن kombu يتتبع أيضًا نفس ملفات FD في المحور غير المتزامن ، وقد لا تتمكن طريقة envtio _epoll هناك من التقاط جميع الاستثناءات ذات الصلة. قد أحاول إجراء تغيير مشابه إلى حد ما في kombu لجعل هذا الرمز أكثر أمانًا أو على الأقل تسجيل التحذيرات بدلاً من مجرد المرور. بالنسبة لحالتك ، على الرغم من أنك تقول إنها استهلكت 1 ثم ماتت - حسنًا ، الخطأ هو FileNotFoundError والاتصال هو واصف الملف ، لذلك أغلق شيء ما أو أزال واصف ملف مؤقت بعد الاتصال الأول. هل هذا FD هو الاتصال بـ SQS؟ هل هناك طريقة لتحديد الاتصالات المستمرة مع خدمة AWS هذه؟

من الغريب أنك تحصل على خطأ FileNotFoundError مرفوعًا من ذلك iterate_file_descriptors_safely عندما يكون في الواقع أحد الاستثناءين اللذين يحاولان التقاطهما. إذا تم اكتشافه ، فسيتم طباعة "خطأ OSE واجه عند الوصول إلى fd" والذي قلته ليس موجودًا ولكن هناك تتبع FileNotFoundError

من الغريب أنك تحصل على خطأ FileNotFoundError مرفوعًا من ذلك iterate_file_descriptors_safely عندما يكون في الواقع أحد الاستثناءين اللذين يحاولان التقاطهما. إذا كان قد تم اكتشافه ، فسيتم طباعة "خطأ OSE مصادف عند الوصول إلى fd"

أنا آسف ، لقد أربكتك. لقد تم إرفاق رسالة التتبع التي قمت بنشرها بالرسالة Encountered OSError when accessing fd . لقد قمت بنسخ traceback من kibana والرسالة هناك حقل مفصول عن traceback.

هل هذا FD هو الاتصال بـ SQS؟

لا أعرف ، كيف يمكنني اكتشاف هذا / تصحيحه. هل لديك أمر يمكنني تشغيله على نظام التشغيل الخاص بي أو في غلاف بيثون أو شيء من هذا القبيل؟

هل هناك طريقة لتحديد الاتصالات المستمرة مع خدمة AWS هذه؟

بالنظر إلى https://boto3.amazonaws.com/v1/documentation/api/latest/guide/configuration.html#configuration ، يوجد بالفعل خيار tcp_keepalive . سوف احاول ان اجرب هذا.

هل هذا FD هو الاتصال بـ SQS؟

لا أعرف ، كيف يمكنني اكتشاف هذا / تصحيحه. هل لديك أمر يمكنني تشغيله على نظام التشغيل الخاص بي أو في غلاف بيثون أو شيء من هذا القبيل؟

عبر

ls -l /proc/4460/fd/

انا حصلت

8 -> pipe:[163974]

ثم مع

lsof | grep 163974

انا حصلت

python3.6  4460             ubuntu    7r     FIFO   0,10      0t0 163974 pipe
python3.6  4460             ubuntu    8w     FIFO   0,10      0t0 163974 pipe
python3.6  4530             ubuntu    7r     FIFO   0,10      0t0 163974 pipe

حيث 4460 هو معرف العملية الأصل و 4530 هو الطفل. لذا فإن fd هو الأنبوب من عملية الوالدين إلى الطفل. أتساءل ، لماذا يتصل الوالد بـ unregister مباشرة في البداية ، عندما لم تكن هناك مهام تتم معالجتها بعد ، على الإطلاق: مرتبك:

قراءة https://idea.popcount.org/2017-02-20-epoll-is-fundamentally-broken-12/ لقد تحققت قليلاً وتوصلت إلى أن نواة لينكس المستخدمة قد تكون قديمة جدًا. كان لدينا 4.4 التي لا توفر حتى الآن ميزة EPOLLEXCLUSIVE . لكن لا يتم استخدامه بشكل افتراضي على أي حال. لقد قمت بترقية النواة إلى 4.5 ونشّطت العلامة ، عندما يتم استدعاء _epoll.register ، لكن ذلك لم ينجح. في الوقت الحالي ، نستخدم poll بدلاً من epoll عن طريق القرصنة

import select
del select.epoll

مع poll لا يمكنني إعادة إنتاج مشكلة وحدة المعالجة المركزية بنسبة 100٪ على الإطلاق. مع SQS كخدمة قائمة انتظار ، يبدو أيضًا أنه تحسين غير ضروري لاستخدام epoll على poll . هل تفكر في إضافة إعداد يجعل _get_poller يفضل poll على epoll ؟

هذا مقلق لأن جميع تطبيقات حلقة الأحداث بما في ذلك استخدام gevent epoll.
jamadden في هذه المرحلة ، أود أن
هل هناك خطأ في gevent يمكن أن يسبب هذا؟

قراءة https://idea.popcount.org/2017-02-20-epoll-is-fundamentally-broken-12/ لقد تحققت قليلاً وتوصلت إلى أن نواة لينكس المستخدمة قد تكون قديمة جدًا. كان لدينا 4.4 التي لا توفر حتى الآن ميزة EPOLLEXCLUSIVE . لكن لا يتم استخدامه بشكل افتراضي على أي حال. لقد قمت بترقية النواة إلى 4.5 ونشّطت العلامة ، عندما يتم استدعاء _epoll.register ، لكن ذلك لم ينجح. في الوقت الحالي ، نستخدم poll بدلاً من epoll عن طريق القرصنة

import select
del select.epoll

مع poll لا يمكنني إعادة إنتاج مشكلة وحدة المعالجة المركزية بنسبة 100٪ على الإطلاق. مع SQS كخدمة قائمة انتظار ، يبدو أيضًا أنه تحسين غير ضروري لاستخدام epoll على poll . هل تفكر في إضافة إعداد يجعل _get_poller يفضل poll على epoll ؟

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

tuky أشعر أننا وصلنا حقًا إلى مكان ما مع هذا وقد عدت إلى هذه المحادثة بأكملها مرة أخرى اليوم وأدركت أن ما قلته في 20 مايو أصبح الآن أكثر وضوحًا. لذا فإن الأنابيب FD بين عملية الكرفس الرئيسية والأطفال تنكسر في هذا التكوين عند الوصول إلى الحد الأقصى من المهام لكل طفل. يقوم بمسح واصف الملف من القائمة ولكن يتم إنشاء العامل الجديد بنفس واصف الملف الذي تمت إضافته مرة أخرى إلى القائمة. كنت أحاول قراءة رمز البلياردو أكثر لمعرفة كيفية تنفيذ ميزة maxtasks.

لذلك ، يتم استدعاء on_inqueue_close بنجاح ، ولكن في كل مرة ، مرة واحدة على الأقل بعد ذلك ، يتم استدعاء on_process_alive والخط

https://github.com/celery/celery/blob/e7ae4290ef044de4ead45314d8fe2b190e497322/celery/concurrency/asynpool.py#L1083

يضيف 8 هناك.

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

هذا المكان في Billiard هو المكان الذي يبدو أنه ينشئ عملية عاملة باستخدام FDs السابقة المعروفة في _create_worker_process: https://github.com/celery/billiard/blob/master/billiard/pool.py#L1143

لقد وجدت هذا أيضًا في Billiard حيث يتم تنفيذ maxtasks: https://github.com/celery/billiard/blob/master/billiard/pool.py#L389

أنا أشكك في ترتيب الطرق التي تم استدعاؤها في طريقة Worker __call__: https://github.com/celery/billiard/blob/master/billiard/pool.py#L288
أولاً يطلق على self._make_child_methods () باستخدام self.inq و self.synq قبل استدعاء self.after_fork () والذي قد يقترب من كتّاب self.inq و self.synq ... لا يقول ذلك سيمنعه من الإضافة FD القديم مرة أخرى ، لكنه يجعلني خدش رأسي ، على الرغم من المضاربة تمامًا.

ثم نظرت إلى ما ذكره thedrow سابقًا في السؤال حول _join_exited_workers يتم استدعاءه ووجدت أنه يتم استدعاؤه من _maintain_pool في لعبة البلياردو التي تسمى نقطتين ، أحدهما في طريقة عامل معالج الجسم. يتم استدعاؤه أثناء تشغيل العملية _state مع سكون دوري مدته 0.8 ثانية ، لكني أشك في أن _ الحالة قد تغيرت عندما أعاد العامل حالة EX_RECYCLE من __call__. بالإضافة إلى ذلك ، فإن المكان الآخر الوحيد الذي يطلق عليه هو الطريقة العامة keep_pool (والتي يتم استدعاؤها من الكرفس AsyncPool في _event_process_exit) ولكن إذا كنت محقًا بشأن متغير الحالة في ذلك الوقت ، فلن يتم استدعاء _maintain_pool في الواقع لأن keep_pool يتحقق إذا كان self. ._worker_handler._state == RUN and self._state == RUN قبل القيام بذلك ، لذلك ربما لم يتم استدعاء هذا الجزء المهم من التعليمات البرمجية.

لذا ، في حين أن الإصلاح الدقيق لواصف الملف المكرر في القائمة غير معروف ، فمن الواضح أن FD القديم تتم إضافته مرة أخرى في إخراج اختبار Tuky. أتساءل مع تسمية كود الخروج ليكون EX_RECYCLE إذا كان منطق Celery / Billiard / Kombu ينوي إعادة استخدام أنابيب واصف الملفات أم لا - ربما تعطل تطبيق SQS عند إعادة تشغيل العامل؟ ربما يكون هذا خطأ لأكثر من مجرد SQS مثلما ذكرت بعض التقارير الأخرى التي ذكرت Redis وسأكون على استعداد لتصديق ذلك - باستثناء أننا نستخدم المهام القصوى لكل طفل مع الواجهة الخلفية AsyncPool و amqp ونادرًا ما يحصل العمال على تعليق مثل هذا عند إعادة تشغيل المهمة على الرغم من أنني أستطيع الإشارة إلى عدد قليل من المرات التي قد تكون ما حدث. إذا اختفى واصف الملف وكان يجب أن يكون موجودًا حتى تعمل إعادة التشغيل ، فقد يفسر ذلك ذلك. العلاقات العامة الأخرى الخاصة بي لا تفعل أي شيء لإعادة إنشاء ملفات FD سيئة ، إنها تضيف فقط بعض فحوصات السلامة حول الأماكن التي نذهب إليها لاستخدام تلك التي نعرف عنها بالفعل.

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

سيكون من الرائع حقًا إذا كان لدي طريقة لإعادة إنتاج هذا محليًا. حتى لو اضطررت إلى إعداد وضع الحماية AWS SQS للتشغيل مقابله ، فسيكون من المفيد الحصول على بعض نماذج التكوين والمهام للتشغيل دون الكثير من الإعداد.

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

دعوت لكmatteiusthedrow وauvipy لالريبو الخاص الذي هو على استعداد لاستخدام - بما في ذلك وثائق التفويض - لطابور SQS. شكرًا لك ، آمل أن تتمكن من إعادة إظهار المشكلة بهذا.

هل تم إصلاح هذا بالفعل؟ هل هناك أي عمل حولها؟

أعتقد أنني أواجه نفس المشكلة / نفس المشكلة بدون "worker_max_tasks_per_child". يبدو أن الأنبوب بين العمال قد اختفى وعلق.

أقوم بتشغيل python3.7-alpine مع:

celery[sqs]==4.4.0rc4 
kombu==4.6.6

إنها مهمة بسيطة لها معلمة واحدة وهي UUID. يتم تعيين max_retries على None و retry_backoff = 2 مع task_acks_late = True و task_acks_on_failure_or_timeout = False.

تثير المهمة في حالتي دائمًا استثناءً لإبقاء الرسالة في قائمة الانتظار لأغراض الاختبار.

/code # ps aux
PID   USER     TIME  COMMAND
    1 root      0:00 {start-celery.sh} /bin/sh /code/docker/start-celery.sh
    8 root      0:01 {celery} /usr/local/bin/python /usr/local/bin/celery -A my_module worker -l debug -c1
   11 root      0:00 {celery} /usr/local/bin/python /usr/local/bin/celery -A my_module worker -l debug -c1
$ strace -fp 11 -s 10000
strace: Process 11 attached
read(8, 
$ ls -l /proc/11/fd/8
lr-x------    1 root     root            64 Nov 30 04:10 /proc/11/fd/8 -> pipe:[23027111]
# (find /proc -type l | xargs ls -l | fgrep 'pipe:[23027111]') 2>/dev/null
lr-x------    1 root     root            64 Nov 30 04:10 /proc/11/fd/8 -> pipe:[23027111]
lr-x------    1 root     root            64 Nov 30 04:10 /proc/11/task/11/fd/8 -> pipe:[23027111]
lr-x------    1 root     root            64 Nov 30 04:10 /proc/8/fd/8 -> pipe:[23027111]
l-wx------    1 root     root            64 Nov 30 04:10 /proc/8/fd/9 -> pipe:[23027111]
lr-x------    1 root     root            64 Nov 30 04:10 /proc/8/task/8/fd/8 -> pipe:[23027111]
l-wx------    1 root     root            64 Nov 30 04:10 /proc/8/task/8/fd/9 -> pipe:[23027111]
$ strace -fp 8 -s 10000
# Normal messages
epoll_ctl(7, EPOLL_CTL_ADD, 10, {EPOLLIN|EPOLLERR|EPOLLHUP, {u32=10, u64=139878494896138}}) = -1 EEXIST (File exists)
mmap(NULL, 262144, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f3815615000
epoll_ctl(7, EPOLL_CTL_DEL, 9, 0x7ffdfeee653c) = -1 ENOENT (No such file or directory)
epoll_pwait(7, [], 1023, 666, NULL, 8)  = 0
epoll_ctl(7, EPOLL_CTL_DEL, 9, 0x7ffdfeee653c) = -1 ENOENT (No such file or directory)
sysinfo({uptime=58265, loads=[112224, 126656, 139264], totalram=24973213696, freeram=880857088, sharedram=1203023872, bufferram=2054041600, totalswap=8258580480, freeswap=7548694528, procs=2268, totalhigh=0, freehigh=0, mem_unit=1}) = 0
epoll_pwait(7, [], 1023, 67, NULL, 8)   = 0
epoll_ctl(7, EPOLL_CTL_DEL, 9, 0x7ffdfeee653c) = -1 ENOENT (No such file or directory)
epoll_pwait(7, [], 1023, 898, NULL, 8)  = 0
epoll_ctl(7, EPOLL_CTL_DEL, 9, 0x7ffdfeee653c) = -1 ENOENT (No such file or directory)
wait4(11, 0x7ffdfeee5e34, WNOHANG, NULL) = 0
epoll_pwait(7, [], 1023, 100, NULL, 8)  = 0
epoll_ctl(7, EPOLL_CTL_DEL, 9, 0x7ffdfeee653c) = -1 ENOENT (No such file or directory)
... same message

(كما ترون هنا الرسالة تكررها بنفسك إلى الأبد).

tuky لقد كنت

بعد الكثير من التجربة والخطأ ، نجح تخفيض مستوى بايثون واختفت ارتفاعات وحدة المعالجة المركزية. (تم تغيير صورة عامل الإرساء الأساسي من python:3.6 (الأحدث؟) -> python:3.6.8 ). أتفهم أن هذا ليس حلاً ، ما عليك سوى وضعه هنا في حال كان يساعد أي شخص أو في التحقيق نفسه.

tuky لقد كنت

بعد الكثير من التجربة والخطأ ، نجح تخفيض مستوى بايثون واختفت ارتفاعات وحدة المعالجة المركزية. (تم تغيير صورة عامل الإرساء الأساسي من python:3.6 (الأحدث؟) -> python:3.6.8 ). أتفهم أن هذا ليس حلاً ، ما عليك سوى وضعه هنا في حال كان يساعد أي شخص أو في التحقيق نفسه.
شكرا لك @ كود ملاذ.
هل تغير شيء ما بين 3.6.8 و 3.6.10 قد يؤثر على ذلك؟
هذا هو الطريق للتحقيق.

لقد حاولت باستخدام python: 3.6.8-slim (docker) و celery == 4.2.2 لكن الكرفس لا يزال يعمل بنفس المشكلة عند إعادة تحميل العامل بعد استهلاك max_task_per_child.

بدأت بنية PyPI الأساسية في رؤية هذا السلوك عند الترقية من python:3.7.3-slim-stretch إلى python:3.8.2-slim-buster في https://github.com/pypa/warehouse/pull/7828/files.

نحن نستخدم SQS مع --max-tasks-per-child .

مع python:3.8.2-slim-buster و kombu==4.6.8 و pycurl==7.43.0.2 ، دائمًا ما تكون المشكلة قابلة للتكرار مثل هذا:

  • تهيئة الكرفس بعامل واحد ومهام قصوى لكل طفل = 1
  • يقوم بمهمة واحدة ، لذلك يعيد تكوين العامل
  • انتظر لمدة عدة دقائق

بعد بعض الشد ، أستطيع أن أرى أنه في حالتي ، فإن الملف الذي يكسر epoll ليس أنبوبًا بين العمليات ، ولكنه مقبس لـ SQS. لذلك ربما يكون السبب غير مرتبط بالسبب الذي كان المؤلف في الأصل.

كما أرى ، تنكسر epoll مباشرة بعد إغلاق مقبس SQS (ولا يحذف kombu المقبس من epoll ، وكما نعلم جميعًا ، إنه خطأ ). عندما نبدأ حلقتنا الرئيسية ، ينشئ Pycurl اتصال Keepalive HTTPS بـ SQS. ثم يقوم Kombu بتعيين روتين دوري ، والذي يرسل طلب http give me some new messages عبر هذا الاتصال. ولكن بعد عدة دقائق من البداية ، قررت الضفيرة فجأة إغلاقها. لذلك عندما يستعيد Kombu التحكم داخل رد الاتصال CurlClient._handle_socket() ، فإنه يحتوي على واصف ملف مغلق بالفعل ، والذي لا يمكن إزالته من epoll.

بدون --max-tasks-per-child ستظل تعمل - تحتوي epoll على نوع من الإشارات الضعيفة إلى كائنات الملف ، لذلك عندما يتم إتلاف جميع المراجع القوية ، يتم حذف الملف أيضًا من epoll. ولكن في حالتنا ، يتم إنتاج العمال الجدد عن طريق التفرع من العملية الرئيسية ، مما يعني أن العامل يحصل على جميع واصفات الملفات المفتوحة من العامل الرئيسي.

لقد واجهت هذه المشكلة منذ حوالي عام وتمكنت من حلها بطريقة غبية للغاية ولكنها فعالة للغاية: ما عليك سوى الانتقال إلى kombu.asynchronous.hub.Hub.create_loop() وإدخال sleep() بعد كل N تكرار 🙂

لقد واجهت هذه المشكلة منذ حوالي عام وتمكنت من حلها بطريقة غبية للغاية ولكنها فعالة للغاية: ما عليك سوى الانتقال إلى kombu.asynchronous.hub.Hub.create_loop() وإدخال sleep() بعد كل تكرار N بشكل طفيف.

هل تمانع في إرسال العلاقات العامة على كومبو؟

بدأت بنية PyPI الأساسية في رؤية هذا السلوك عند الترقية من python:3.7.3-slim-stretch إلى python:3.8.2-slim-buster في https://github.com/pypa/warehouse/pull/7828/files.

نحن نستخدم SQS مع --max-tasks-per-child .

هل تمانع في التحقق من الإصلاح المقترح في كومبو؟ https://github.com/celery/kombu/pull/1189

هذه أخبار مثيرة. سيتحقق هذا الشهر المقبل ، إذا تم إصلاح كل شيء لنا أيضًا.

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