Taskcls seledri: dekorator baru, fitur baru

Halo, Habr! Saya akan menceritakan kisah kejenuhan profesional saya.


Kebetulan saya benci treadmill rutin. Saya punya beberapa proyek menggunakan Seledri . Setiap kali tugas menjadi lebih rumit daripada 2 + 2 = 5 , templat solusi dikurangi untuk membuat kelas yang melakukan tugas, dan fungsi starter yang dapat digunakan Celery - pelat ketel. Dalam artikel ini saya akan memberi tahu Anda bagaimana saya berjuang dengan pelat dan apa yang terjadi.


Logo


Titik awal


Pertimbangkan tugas seledri yang biasa. Ada kelas yang melakukan tugas, dan fungsi starter yang membuat instance kelas dan memulai salah satu metodenya, di mana semua logika tugas diimplementasikan dan penanganan kesalahan diwarisi:


 class MyTask( FirstMixin, SecondMixin, ThirdMixin, ): def main(self): data = self.do_something() response = self.remote_call(data) parsed = self.parser(response) return self.process(parsed) @app.task(bind=True) def my_task(self, arg1, arg2): instance = MyTask( celery_task=self, arg1=arg1, arg2=arg2, ) return instance.full_task() 

Pada saat yang sama, metode full_task mencakup panggilan ke main , namun, ia juga menangani penanganan kesalahan, pencatatan dan omong kosong lain yang tidak terkait langsung dengan tugas utama.


Ide taskclass


Pada akar taskclass terletak ide sederhana: di kelas dasar, Anda dapat menentukan metode kelas task , menerapkan perilaku fungsi starter di dalamnya, dan kemudian mewarisi:


 class BaseTask: def __init__(self, **kwargs): for key, value in kwargs.items(): setattr(self, key, value) def full_task(self): try: return self.main() except: self.celery_task.retry(countdown=30) @classmethod def task(cls, task, **kwargs): self = cls( celery_task=celery_task, **kwargs, ) return self.full_task() 

Semua kebosanan tambahan dikumpulkan di kelas dasar. Kami tidak akan kembali padanya lagi. Kami menyadari logika tugas:


 @app.taskcls(bind=True) class MyTask( BaseTask, FirstMixin, SecondMixin, ThirdMixin, ): def main(self): data = self.do_something() response = self.remote_call(data) parsed = self.parser(response) return self.process(parsed) 

Tidak ada lagi kulit, lebih baik. Namun, bagaimana dengan titik masuknya?


 MyTask.task.delay(...) 

MyTask.task memiliki semua metode tugas yang biasa: delay , apply_async , dan, secara umum, itu.


Sekarang argumen sang dekorator. Sangat menarik untuk menarik bind = True ke dalam setiap tugas. Apakah mungkin untuk melewatkan argumen default melalui kelas dasar?


 class BaseTask: class MetaTask: bind = True def __init__(self, **kwargs): for key, value in kwargs.items(): setattr(self, key, value) def full_task(self): try: return self.main() except: self.celery_task.retry(countdown=30) @classmethod def task(cls, task, **kwargs): self = cls( celery_task=celery_task, **kwargs, ) return self.full_task() 

Kelas MetaTask bersarang berisi argumen default dan akan tersedia untuk semua kelas anak. Menariknya, itu juga bisa diwariskan:


 class BaseHasTimeout(BaseTask): class MetaTask(BaseTask.MetaTask): timeout = 42 

Argumen yang diteruskan ke dekorator @app.taskcls memiliki prioritas tertinggi:


 @app.taskcls(timeout=20) class MyTask( BaseHasTimeout, FirstMixin, SecondMixin, ThirdMixin, ): def main(self): ... 

Akibatnya, batas waktu untuk tugas tersebut adalah 20.


Melampaui


Dalam aplikasi web, seringkali ada kebutuhan untuk memulai tugas dari pandangan. Dalam hal adhesi tinggi, tampilan dan tugas dapat digabungkan:


 class BaseViewTask: @classmethod def task(cls, **kwargs): # Somehow init View class manually self = cls(...) return self.celery() @app.taskcls class MyView( BaseViewTask, FirstMixin, SecondMixin, ThirdMixin, APIView, ): queryset = MyModel.objects.all() def get_some_data(self, *args, **kwargs): # common methed return self.queryset.filtert(...) def get(self, request): data = self.get_some_data(request.field) # used in request handling return Response(json.dumps(data)) def post(self, request): self.task.delay(...) return Response(status=201) def celery(self): data = self.get_some_data(...) # also used in background task return self.do_something(data) 

By the way, untuk menghindari tabrakan nama, kelas bersarang disebut MetaTask , bukan Meta , seperti dalam Django.


Kesimpulan


Fungsi ini diharapkan di Celery 4.5 . Namun, saya juga menyiapkan paket yang memungkinkan Anda untuk mencoba dekorator taskcls hari ini. Gagasan dari paket ini adalah ketika Anda memutakhirkan Seledri ke versi 4.5, Anda dapat menghapus impornya tanpa mengubah satu baris kode pun.

Source: https://habr.com/ru/post/id470547/


All Articles