Celery notları
Jul 20, 2018 · 3 minute read · Commentsprogrammingpython
task_acks_late
Suan çalışan bir task varken herhangi bir sebepten ötürü(restart vs) celery worker kapanırsa ne olacak?
Celery’de default olarak bir task işleme alındığı anda kuyruktan çıkartılır. Eğer task bitmeden celery worker kapanırsa task kuyruktan çıkartıldığı için worker tekrar çalıştırıldığında task tekrardan işleme alınmayacaktır.
Celery workerlarda çalışan kod worker restartlanmadığı sürece değişmez. Bu yüzden her deployment yaptığımızda celery workerları kapatıp açmamız gerekir. Fakat deployment yaptığımız anda çalışan bir task varsa celery workeri kapattığımız an task yarıda kalacaktır ve celery workeri tekrar başlattığımızda task tekrar işleme alınmayacaktır çünkü yukarıda da belirttiğim gibi default olarak celery taskı işleme aldığı an kuyruktan çıkartır.
task_acks_late = True
olduğu durumda ise task bitmeden kuyruktan çıkartılmaz. Bu sayede celery worker nezaman kapatılırsa kapatılsın tekrar başlatıldığında task yeniden işleme alınacaktır.
worker_max_tasks_per_child
Senin için önemli olan taskların hızlı işleme alınması mı yoksa az memory kullanımı mı?
Normalde celery worker başlatıldığında (aksi belirtilmediği takdirde) işlemci CPU core sayısı kadar process açılır ve gelen tasklar bu processlerde işleme alınır. Task bitse bile process öldürülmez. Sıradaki gelen tasklar aynı process içerisinde işleme alınır.
Python’da(hatta sadece Python’da da değilmiş) şöyle bir problem var: Bir process 100MB ram kullandığında o process öldürülmediği sürece kullandığı memorynin tamamını işletim sistemine bırakmaz. Bu yüzden ram kullanımı her gelen task ile birlikte, memoryde boş yer kalmayıncaya kadar artarak devam eder. Bakınız
Bu yüzden uzun süre çalışan processler kullanmak yerine her task için bir process açıp task bittiğinde processi öldürmek daha mantıklı olabiliyor. Bu sayede task nekadar memory kullanırsa kullansın task bittiğinde process ölecek ve processin kullanıdığı memory işletim sistemi tarafından tekrardan kullanılabilir hale gelecektir.
worker_max_tasks_per_child
her bir process üzerinde kaç task çalıştırıldıktan sonra öldürüleceğini belirtir. Defaultta None
‘dır, Bu celery worker ölene kadar processin öldürülmeyeceği anlamına gelir. Ben worker_max_tasks_per_child
i genelde 1 yapıyorum. Bu sayede her task için yeni bir process açılacak, task bittiğinde ise process öldürülecektir.
Peki processin hiç ölmemesinin böyle bir dez avantajı varsa neden adamlar default olarak sınırsız yapmış? diye sorarsanız eğer buda sundan dolayı;
Her process başladığında taskın çalışması için gereken bütün kaynakların memorye yüklenmesi biraz zaman alabilir(Django üzerinden örnek vermek gerekirse projenizi çalıştırdığınızda projenin requestleri karşılamak için hazır duruma gelene kadar geçen süreye yakın bir süre). Process öldürülmediğinde çok daha kısa bir süre içerisinde task işleme alınacaktır.
Yav kardeşim benim hemen işleme alınaması gereken tasklarım var fakat memoryden de kar edeyim derseniz iki ayrı kuyruğu consume eden iki ayrı worker açıp tasklarınızı bu kuyruklara gönderebilirsiniz.
Hızlı işleme alınması gereken tasklar için çalıştırdığımız worker processleri daha nadir veya hiç öldürmezken, bir kaç saniyenin okadarda önemli olmadığı taskları diğer workerda çalışacaktır.
Bunun için celery confiğimizde şu şekil bir tanımlama yapabiliriz:
worker_max_tasks_per_child = env.int('CELERY_WORKER_MAX_TASK_PER_CHILD', default=None)
Her tasktan sonra processin ölmesini istediğimiz workerı calıştırırken
$ export CELERY_WORKER_MAX_TASK_PER_CHILD=1
$ celery ....