معماری Message Queue

Message Queue مفهومی در زمینه برنامه نویسی Async است. فرض کنید بعد از انجام یک عملیات مثل پرداخت باید یک پیامک یا ایمیل ارسال شود. در این صورت عملیات پرداخت نباید به ارسال ایمیل وابسته باشد. در واقع این عملیات های باید Async باشند. پرداخت انجام میشود و یک Task به اسم ایمیل در صف پرداخت قرار میگیرد که در اولین فرصت ایمیل ارسال شود. در مورد اینکه چرا باید از این معماری استفاده کرد یا مزایا یا معایب آن چیست صحبتی ندارم و میشود در اینترنت این موارد را جست.

اینجا معماری کلی آن را میبینیم که در اینترنت ممکن است مورد مفهومی دیده نشود.

عکس بالا رو ببینید:

اول از همه یک سری Task داریم. مثلا گزارش ماهانه مالی، ارسال ایمیل، تهیه thumbnail از عکس بارگزاری شده و.. اینها مثالهایی از تسک هایی هستند که باید Async انجام بشه. گزارش ماهانه که قراره سر ماه نصب شب آماده بشه که فردا صبح کارمندان ازش استفاده کنن باید مستقل از نرم افزار باشه و به صورت تسکی ماهانه انجام بشه.

این تسک ها توی یک صف قرار میگیرند که به ترتیب انجام شن. این صف رو Rabbit MQ یا Redis براموان فراهم میکنن.

بعد تسک ها به ترتیب به Worker هایی داده میشن. در واقع worker ها این تسک ها رو انجام میدن. توی پایتون از celery استفاده میشه معمولا. خود Task ها هم توابعی هستند که به Celery داده میشن.

بعد از اینکه تسک انجام شد ممکنه بخوایم خروجیش رو نگه داریم معمولا Redis گزینه خوبیه. اگر بخواهیم که به صورتی دایمی داشته باشیم خروجی رو توی یک دیتابیس باید ذخیره کنیم.

گفتیم مثلا اگر پرداخت انجام شد باید ایمیل ارسال بشه. خوب مشخصه توی این مثال بعد از انجام پرداخت تسک ارسال ایمیل فراخوانی میشه. اما بعضی تسک ها خود به خود توی بازه های زمانی خاصی باید انجام بشه. اینجا به Schedualer نیاز داریم. Celery قابلیتی داره به اسم Beat که برنامه ریزی با اون انجام میشه و تسک ها رو خودش triger میکنه.

Referer در request های وبسایت

در جایی که به اشتباه route ای تعریف شده بود و تغییر آن وسط پروژه مشکل بود شرایط بررسی شد و دیدیم که در شرایطی که کاربر از طریق خود سایت به این صفحه نیاید باید به صفحه home منتقل شود و با این تحلیل مشکل حل شده و نیاز به تغییر route نیست و در اینجا مفهوم Referer مطرح می شود. در صورتی که کاربر از طریق لینکهای خود سایت به صفحات مختلف برود request دارای Referer است اما اگر خودش مستقیما لینک را بزند خارج از چارچوب سایت Referer نداریم بنابراین با کد زیر در view مربوطه مشکل حل شد:

افزودن Template کاستوم توی جنگو

امروز لازم بود که یک فیلتر توی template engine جنگو اضافه کنم که یک ایمیل رو بگیره و بخش قبل از @ رو نشون بده. مثل زیر:

بنابراین یه تمپلیت تعریف کردم. یک فایل به اسم custom_filters.pyt ساختم و کد اون از قرار زیره:

و در نهایت باید به TEMPLATES توی setting.py اضافه بشه:

load دیتا در migration

گاهی پیش میاد که یک سری جدول توی دیتابیس باید همیشه یک دیتای از پیش تعریف شده توش ذخیره بشه. در واقع وقتی جدول رو میسازیم و migrate میکنیم بهتره که بلافاصله دیتای خودمون رو توش ذخیره کنیم. برای این کار توی فایل migration میتونیم operations رو تعریف کنیم و یک تابع رو توش صدا بزنیم مثال زیر همین کار رو کرده و توی اون تابع جدول رو پر میکنه:

پیاده سازی Odoo روی داکر Docker

برای پیاده سازی Odoo توی داکر باید اول docker-compose.yml رو داشته باشیم:

داکرفایل postgress:

یه فایل init.sql هم استفاده شده:

فایل odoo17.conf

از همه مهمتر Dockerfile خود Odoo:

getFOOdisplay یا get_foo_display()

اگر توی یک مدل جنگو از choice استفاده کنیم برای اینکه با اندیس توی template یا توی view به مقدار choice دسترسی پیدا کنیم باید با شورت کات های زیر کار کنیم مثلا برای مدل و فیلد زیر»:

باید از دستورات زیر استفاده کنیم

سایز هدر و timeout توی پروژه وب

در نرم افزاری طول URL از حدی که بیشتر میشد سرور ارور بر میگردوند. با بررسی لاگ Nginx و uWSIG متوجه شدم که سایز هدری که مرورگر میفرسته از حدی بیشتره و سرور قبول نمیکنه. از طرفی اون عملیات زمان بر هم بود و وقتی که سایز هدر را هم بیشتر کردم باز هم چون طول میکشید سرور کانکشن رو میبست. با تنظیمات Nginx زیر تونستم هم طول هدر رو بیشتر کنم هم مدت زمان کانکشن ها:

فایل compose کل سرویس ها هم مثل زیره:

قابلیت لاگین به عنوان کاربران دیگر در جنگو

برای اینکه ببینیم توی یه پروژه کاربرها با دسترسی های مختلف چطور سایتمون رو میبینن میخایم یه قابلیت به سایت اضافه کنیم که کاربر ادمین بتونه بجای هر کاربری لاگین کنه. login as

برای این کار route های زیر رو اضافه میکنیم:

ویوهای اون

حالا یه middleware مینویسیم و میزاریم آخر بقیه که توی هر request این رو ست کنه:

حالا از اینا توی تمپلیتها استفاده میکنم:

کانفیگ uWSGI و Nginx روی Socket

قبلا Nginx و uWSGI رو به صورت زیر اجرا کرده بودم که وقتی طول URL زیاد میشد ارور 502 میگرفتم. ارور رو بررسی که کردم دیدم uWSGI ارور میده. و ماکسیموم طول هدر برابر است با 4096 ولی طول این url بیشتره. Nginx رو به صورت زیر اجرا کرده بودم.

nginx.conf

و Dockerfile ای که مربوط به uWSGI هست به صورت زیر اجرا شده بود:

بعد –buffer-size رو بهش اضافه کردم که درست بشه

اما مشکل حل نشد. مشکل اینجا بود که Nginx به صورت پراکسی کل درخواست http رو به uWSGI میفرسته ولی بهتره که درخواست رو خودش پردازش کنه و بعد روی socket درخواست رو به uWSGI بده چون اون پروتکل wsgi رو بهتر و با پرفورمنس بهتر مدیریت میکنه.

قبلا uWSGI هم درخواست های http رو پردازش میکرد هم به عنوان یک application server درخواست های wsgi رو مدیریت میکرد یعنی Nginx یک reverse proxy معمولی بود ولی بهتره که http رو nginx مدیریت کنه و uWSGI به عنوان application server کار کنه که این از نظر پرفورمنس بهتره.

حالا که uWSGI رو روی socker کانفیگ کردیم باید Nginx هم درخواست ها رو روی socket بگیره و بده. بنابراین تنظیمات جدید به صورت زیر میشه:

معماری scrapy

عکس زیر معماری و روند کار scrapy را نشان میدهد. روند کار خیلی جالبه

scrapy بخش های زیر رو داره

spiders: در واقع اون کدی هست که ما میزنیم و میخایم با روش خودمون از سایت های مختلف دیتا بگیریم و به روش خودمون ذخیره کنیم. در واقع اون کدی که ما میزنیم توی بخش spider قرار میگیره. مثلا میگیم از فلان سایت این قسمت های صفحه رو بگیر و ذخیره کن

Engine: موتور scrapy که در واقع مدیریت و ارتباطات بین اجزای مختلف رو داره

ITEM PIPELINES: توی این قسمت میگیم که دیتایی که گرفتیم چه بلایی سرش بیاد. مثلا بگیم توی دیتابیس یا فایل ذخیره بشه. تمیز کردن دیتا و صحت سنجیش هم همینجا انجام میشه.

Downloader: وظیفه گرفتن یک صفحه و دادنش به Engine رو داره

Scheduler: درخواست ها رو توی صف میزاره و سر وقتش به جریان میندازه

Downloader middleware: تمامی درخواست های بین Downloader و Engine از این واسط ها رد میشه. میتونه خیلی از درخواست ها رو بلاک کنه میتونه response ای که میگیره رو به Engine نده و با درخواست های بعد جمع کنه یه جا بده. درخواستهایی که جوابش رو داره دیگه نفرسته و هر چیزی رو کنترل کنه

Spider middleware: بین Engin و Spider میشینه درخواست ها رو به Engine میفرسته و جوابش رو به Spider میده از دوباره Item هایی که از Spider میاد رو بر میگردونه و این وسط تغییرات لازم رو انجام میده.

یه درخواست مسیرهای زیر رو میگذرونه توی Spider. عکس بالا یک Spider رو نشون میده.

اول متد start_request یک آبجکت Request رو به callback ای به اسم pars() میفرسته.

pars() اگه لازم باشه درخواست رو به یه callback دیگه میفرسته و درنهایت Response ای که از Downloader میگیره رو به Item تبدیل میکنه به Item pipline میفرسته که توی دیتابیس ذخیره بهشه یا به Feed Exports مفرسته که توی فایل ذخیره بشه