تورنادو و تسک‌های periodic

چهارشنبه 09 شهریور 1401 توسط Reganto

مقدمه

یکی از ویژگی‌های بسیار جذاب تورنادو توانایی آن در اجرای تسک‌های پریودیک‌ است. ابزارهای دیگری نیز برای این دسته از تسک‌ها وجود دارند که شاید معروف‌ترین آن‌ها Celery Beat و Crond باشد اما تورنادو این کار را بدون هیچگونه پیش‌نیازی و به سادگی و تنها با فراخوانی یک متد انجام می‌دهد.

امضای متد

متد مورد نظر برای اجرای تسک‌های پریودیک PeriodicCallback نام دارد آن را می‌توان از مسیر tornado.ioloop.PeriodicCallback ایمپورت کرد. این متد سه آرگومان می‌گیرد اولی تابعی است که قرار است اجرا شود «کال‌بک مورد نظر ما» دومی فاصله زمانی بین اجرای متوالی کال‌بک است«همان interval یا callback time» سومی هم پارامتری است به نام jitter

یک مثال

می‌خواهیم یک تابع داشته باشیم که در فواصل زمانی معین یک پیام را به خروجی می‌برد.

import asyncio

from tornado.ioloop import PeriodicCallback


def say_hello():
    print("Hello Tornado")


callback = PeriodicCallback(say_hello, 3000)
callback.start()

loop = asyncio.get_event_loop()
loop.run_forever()

تابع say_hello هر سه ثانیه یکبار به صورت پریودیک اجرا می‌شود. اکثر زمان‌ها در تورنادو به صورت ثانیه وارد می‌شود اما در این مورد یک استثنا وجود دارد. پارامتر callback_time در PeriodicCallback باید به صورت میلی‌ثانیه یا شی‌ای از datetime.timedelta وارد شود.

import asyncio
from datetime import timedelta

from tornado.ioloop import PeriodicCallback


def say_hello():
    print("Hello Tornado")


callback = PeriodicCallback(say_hello, timedelta(seconds=3))
callback.start()

loop = asyncio.get_event_loop()
loop.run_forever()

فراخوانی کال‌بک با آرگومان

بعضی از توابع‌ برای اجرا نیازمند آرگومان‌هایی هستند و به دو روش می‌توان آرگومان‌هایی را به کال‌بک مورد نظر فرستاد

1- استفاده از lambda

import asyncio

from tornado.ioloop import PeriodicCallback


def say_hello(name):
    print(f"Hello {name}")


callback = PeriodicCallback(lambda: say_hello("Reganto"), 3000)
callback.start()

loop = asyncio.get_event_loop()
loop.run_forever()

2- استفاده از partial

import asyncio
from functools import partial

from tornado.ioloop import PeriodicCallback


def say_hello(name):
    print(f"Hello {name}")


callback = PeriodicCallback(partial(say_hello, name="Reganto"), 3000)
callback.start()

loop = asyncio.get_event_loop()
loop.run_forever()

یک مثال ملموس‌تر

سناریویی را در نظر بگیرید که در یک سیستم تعدادی کاربر داشته باشیم. کاربران مورد نظر ما برای دریافت سرویسی ماهانه مبلغی را پرداخت می‌کنند در این‌صورت تسکی باید به صورت ماهیانه اجرا شود. این تسک می‌بایست کاربرانی را که برای سرویس مورد نظر پرداختی نداشته‌اند پیدا کرده و دسترسی آن‌ها را به سرویس مسدود کند و درنهایت ایمیلی با متن مناسب برای کاربرانی که اکانت خود را شارژ نکرده‌اند بفرستد.

این تسک را می‌توان به سه زیرتسک «سه تابع» تقسیم کرد:

1- پیدا کردن کاربرانی که پرداختی نداشته‌اند

2- مسدود کردن دسترسی کاربرانی که پرداختی نداشته‌اند

3- ارسال ایمیل به کاربران مسدودشده و درخواست برای شارژ اکانت

import asyncio
from datetime import timedelta

from tornado.ioloop import PeriodicCallback

def scan_for_expired_users():
    expired_users = ["Finrod", "Boromir", "Theoden"]
    return expired_users

def deactivating_process(user):
    print(f"Deactivating account: {user}")

def send_expiration_email(user):
    print(f"Sending expiration email to: {user}")


def arda_task():
    expired_users = scan_for_expired_users()
    for user in expired_users:
        deactivating_process(user)
        send_expiration_email(user)


task = PeriodicCallback(arda_task, timedelta(days=30))
task.start()
loop = asyncio.get_event_loop()
loop.run_forever()

درباره‌ی jitter

جیتر یک مفهوم ریاضی است و برای کاهش alignment رویدادهای با پریود مشابه استفاده می‌شود.

بدون جیتر

بدون جیتر

با جیتر

بدون جیتر

اجرا با مقادیر متفاوت جیتر

بدون جیتر

اگر مقدار جیتر را 0.1 قرار دهیم، باعث به‌ وجود آمدن تنوع 10 درصدی در فاصله زمانی فراخوانی کال‌بک خواهد شد.

در آخر

1- اگر اجرای کال‌بک بیشتر از callback_time طول بکشد، از فراخوانی‌های بعدی چشم‌پوشی می‌شود.«اضافه شده در تونادو 6.2»

2- چنان‌چه برای alignment معادلی مناسب یافتید، دریغ نکنید. tell.regnato[at]gmail.com

3- برای نکات جزیی‌تر می‌توانید مستندات تورنادو برای PeriodicCallback را چک کنید.

4- مثالی از اجرای همین تسک با Celery Beat را می‌توانید اینجا بیابید.

5- عکس‌های مربوط به جیتر از اینجا گرفته شده‌اند.