اگر با celery و کاربردش آشنا هستین می تونین مستقیم به بخش ویژگی ها برین و از اونجا بخونین. اما اگر آشنا نیستین همراه باشین.
اول از همه: celery چیه؟
احتمالا تا حالا موقع برنامه نویسی به مواردی برخوردین که مدت زمان اجرای اون بخش از کد طولانی هستش و یا به یک سرویس خارجی وابستگی داره(مثل ارسال ایمیل یا sms). اگر بخوایم یه مثال خیلی روتین بزنیم فرض کنیم که کاربر روی دکمه ایجاد گزارش کلیک می کنه. اینجاست که برنامه ما باید بره کلی اطلاعات جمع کنه و بعد با یک ساختار توی فایل بنویسه و در نهایت اون رو به کاربر تحویل بدیم. این کار علاوه بر اینکه از لحاظ میزان حجم کار یکم زیاد هستش و بار به سیستم تحمیل می کنه از لحاظ زمانی هم خیلی طول می کشه پس اینکه کل برنامه معطل اون بخش بمونه و حالت هنگ طور داشته باشه، از اون طرف هم کاربر هم بشینه زل بزنه به مانیتور تا این کار تموم بشه(تازه اگر برنامه تحت وب هم باشه بدتر چون نمیشه یک رکوئست رو برای مدت طولانی منتظر گذاشت و تایم اوت میخوریم) منطقی نیست! اما باید چیکار کرد؟
اژدها وارد می شود(اژدها = celery)
استفاده از celery(و به طور کلی task manager ها ) این امکان رو به ما میده تا بتونیم این طور کارها که طول میکشن رو تا وقتی که اماده میشن در پس زمینه اجرا کنیم.
طبق گفته خود وبسایت celery:
Celery is an asynchronous task queue/job queue based on distributed message passing
که طبق ادعاش :
Celery is used in production systems to process millions of tasks a day.
به طور کلی بخوایم بگیم در واقع ما میایم یک سری کار تعریف می کنیم و یک سری هم worker (هرچندتا که خودمون بخوایم) برای این کارها تعریف می کنیم و این worker ها میان و کارهارو از صف هایی که دارن برمیدارن و میرن انجام شون میدن.
نکته: celery برای ایجاد صف به چیزهایی مثل rabbitmq یا redis و... نیاز دارد.
من بیشتر از این دیگه توضیح نمیدم. برای اشنایی بیشتر توی نت و خود ویرگول مطالب مختلفی هستش که خیلی خوب و جامع معرفیش کردن.
اما celery یک سری ویژگی ها دارد که باعث می شود تا بتونیم به کمک اونا هم راحت تر باشیم و بهتر کارها رو مدیریت کنیم.
۱) روش های تعریف کردن کار:
به طور کلی تو celery دو نوع روش تعریف کار وجود داره:
تفاوت هاشون چیه:
تقریبا میشه از اسم شون حدس زد، وقتی ما یه چیزی می نویستم که استفاده تو جاهای مختلف داره مثل موقع نوشتن یک library که می تونه به پروژه های مختلف اضافه بشه میایم از shared_task استفاده می کنیم. اما اگر یه چیزی توسعه میدیم که مختص کار خودمونه و قرار نیست جای دیگه ازش استفاده کنیم بهتره که حالت دوم استفاده کنیم. اگر کامل متوجه نشدین این لینک و این لینک رو یه نیگا بندازین.
یک نکته: موقعی که کاری تعریف میشه می تونین مقدار bind رو برابر True قرار بدین تا به object اون کار هم دسترسی داشته باشین. البته در این صورت باید متغیر self رو به عنوان اولین متغیر اون تابع تون قرار بدین. یعنی این طوری میشه:
app.task(bind=True) def test_task(self, *args, **kwargs): ....
حالا بدون bind:
app.task() def test_task(*args, **kwargs): .....
۲) تعریف chain
ممکنه مواقعی پیش بیاد که یک سری کار داشته باشیم که باید تو ترتیب خاصی اجرا بشن و نتیجه کار قبلی به کار بعدی پاس داده بشه. این جور مواقع ست که chain به دردمون می خوره. دیدن مثال و لینک مرجع
تو مثال پایین وقتی کار اول اجرا شد نتیجه به عنوان ارگومان کار دوم پاس داده میشه و همین طور الی آخر(پس کار دوم هم در واقع دوتا ورودی گرفته).
res = chain(test_task.s(2, 2), test_task.s(3), test_task.s(2))()
۳) تعریف group
اگر بخوایم که یک سری از کارها به صورت موازی انجام بشن. از group استفاده میکنیم. دیدن مثال و لینک مرجع
res = group(test_task.s(i, i) for i in range(10))()
۴) فراخوانی کارها:
برای فراخوانی کارها می تونیم از دو روش استفاده کنیم:
حالت دوم ویژگی های بیشتری به ما میده یعنی علاوه بر اینکه که می تونیم ارگومان های اون کار رو بهش پاس بدیم برای اون کار موارد دیگه رو هم تنظیم کنیم. مواردی مثل اینکه چند بار تلاش انجام بشه برای انجام کار، حداکثر مدت زمان اجرای کار چقدر باشه ویا برای اون کار callback تعریف کنیم. لینک همه موارد در دسترس
test_task.apply_async( args=(2,2), expires=60, countdown=10, retry=True, retry_policy={ 'max_retries': 3, 'interval_start':0.5, 'interval_max':0.2 }
۵) تعریف retry
از اسمش مشخصه اینکه مشخص کنیم یک کار اگر به صورت موفقیت امیزی اجرا نشد دوباره اجرا بشه. همچنین می تونیم مشخص کنیم چند بار تلاش کنه، بین هر تلاش چقدر فاصله بندازه، حداکثر فاصله بین تلاش ها چقدر باشه و اینکه برای چه نوع exceptionهایی دوباره تلاش کنه.
@task(max_retries=10, autoretry_for=(Exception,), retry_backoff=60)
۶) تعریف link
اگر بخوایم برای یک کار در صورتی که موفق بود یک call back(یعنی اگر فلان اتفاق اتفاد بعدش یک چیزی رو فراخوانی کن) تعریف کنیم. از link استفاده می کنیم. در این صورت نتیجه کار به هدفی که به عنوان link گذاشتیم پاس داده میشه.تو مثال زیر اگر کار test_task با موفقیت اجرا بشه test_task_success فراخوانی خواهد شد.
test_task.apply_asyc(args=(2, 2), link=test_task_success.s())
۷) تعریف link_error
اگر موقع انجام کار با خطا مواجه شدی چه چیزی رو فراخوانی کن. که در این صورت هم id اون کار رو به هدف پاس میده. تو مثال زیر اگر کار موفقیت امیز بود که هیچی اگر نبود میاد test_task_failرو فراخوانی می کنه.
test_task.apply_async(kwargs={"arg1":2, "arg2":2}, link_error=test_task_fail.s())
برای اینکه مطلب طولانی و خسته کننده نشه اینجا تمومش می کنیم و اگر عمری بود تو یک مطلب دیگه ادامه ویژگی های celery رو با هم مرور می کنیم.
راستی عیدتون مبارک باشه :)