9 روز پیش
در این قسمت یاد میگیریم چطور می توان از nginx برای کنترل و مدیریت ترافیک استفاده کرد.
با استفاده از nginx بر اساس یکسری پارامتر می توان جریانِ درخواست های ورودی را کنترل و یا به مسیر های مختلف هدایت کرد. در این قسمت یکسری از روش ها برای مدیریت و کنترل درخواست ها آشنا می شویم.
فرض کنید دو یا چندین نسخه از یک اپلیکیشن را ساخته اید یا اینکه تغییری در اپلیکیشن داده اید و میخواهید ابتدا آن را توسط درصد کمی از کاربرها تست کنید و ببینید که آیا تغییرات به درستی کار میکنند یا اینکه تغییرات چیزی هستند که کاربردی باشند یا نه! در این حالت به نسخه فعلی اپلیکیشن A و به نسخه جدیدی که شامل تغییرات است B میگوییم.
در A/B Testing، معمولاً بخش عمدهای از ترافیک (مثلاً 90٪) به نسخه A و بخش کوچکی (مثلاً 10٪) به نسخه B هدایت میشود. با مانیتورینگ (monitoring) رفتار کاربران و تحلیل دادهها در نسخه B، میتوان بررسی کرد که آیا تغییرات عملکرد مورد انتظار را دارند یا خیر.
اگر نسخه B نتایج مثبت داشت، میتوان ترافیک بیشتری به سمت آن هدایت کرد یا آن را به نسخه اصلی تبدیل نمود. در غیر این صورت، تغییرات به راحتی قابل بازگشت هستند. به این فرآیند A/B Testing گفته میشود که هدف آن ارزیابی عملی و کنترلشده تغییرات در محصول پیش از اعمال گسترده آن برای تمام کاربران است.
در Nginx با استفاده از split_clients
میتوان ترافیک ورودی را به چندین قسمت تقسیم کرد. این دستور دو پارامتر میگیرد. پارامتر اول یک پارامتر متنی است که بر اساس آن ترافیک تقسیم میشود و پارامتر دوم یک آبجکت است که نحوه تقسیم شدن ترافیک را مشخص میکند. در مثال بالا ما ترافیک را بر اساس IP کاربران تقسیم میکنیم. به ابتدای آیپی کلمه ای که خواستیم رو اضافه کردیم. این بدین معنی است که شما میتوانید به پارامتر اول متن یا متغیرهایی را بر حسب نیاز اضافه کنید. با توجه به پارامتر دوم 20 درصد از ترافیک به backendv2 و باقی ترافیک به backendv1 ارسال میشود.
دستور split_clients
بدین صورت عمل میکند که به ازای هر درخواست با استفاده از پارامتر اول یک مقدار عددی که مقدار آن میتواند بین 0 تا 100 باشد ساخته می شود. سپس با توجه به پارامتر دوم این ترافیک بین سرورهایی که مشخص کرده ایم تقسیم میشود.
نکته تکمیلی: برای ساخته شدن مقدار عددی در حال حاضر ابتدا از تابع هش CRC32 استفاده میشود و سپس نتیجه آن به عددی در بازه 0-100 نرمال سازی می شود.
فرض کنید دو نسخه از فایل index.html داریم و میخواهیم ترافیک خود را بین این دو نسخه تقسیم کنیم:
همچنین میتوان دو سرور مختلف داشت و ترافیک را با proxy_pass
بین آنها تقسیم کرد.
میتوان از split_clients
برای استقرار (deploy) به روش Blue/Green یا Canary استفاده کرد.
Blue/Green: در این روش دو محیط مجزا و همسان (یکی به نام Blue و دیگری Green) آماده نگه داشته میشوند. نسخه فعلی برنامه روی محیط Blue اجرا میشود و نسخه جدید روی محیط Green مستقر میشود. پس از اطمینان از صحت عملکرد نسخه جدید در محیط Green، تمام ترافیک به صورت یکجا از محیط Blue به محیط Green منتقل میشود. این روش باعث کاهش زمان قطع سرویس و ریسکهای مرتبط با انتشار میشود، زیرا در صورت بروز مشکل میتوان سریعاً به محیط قبلی بازگشت.
Canary: در این روش, نسخه شامل تغییرات, ابتدا به بخش کوچکی از کاربران ارائه میشود تا عملکرد و پایداری آن در محیط واقعی به دقت مانیتور و ارزیابی شود. در صورت موفقیتآمیز بودن، به تدریج درصد بیشتری از ترافیک به نسخه جدید هدایت میشود تا نهایتاً جایگزین نسخه قدیمی شود.
برای محدود کردن تعداد کانکشن های فعال از طرف هر کلاینت, میتوان از دستور limit_conn
استفاده کرد. این دستور به همراه دستور های limit_conn_zone
و limit_conn_status
مورد استفاده قرار میگیرد.
دستور limit_conn_zone
یک فضای حافظه اشتراکی برای نگهداری اطلاعات مربوط به کانکشنهای فعال تعریف میکند. همچنین در این دستور مشخص میشود که شمارش کانکشنها بر اساس چه کلیدی انجام شود؛ برای مثال میتوان از IP کاربر یا شناسه جلسه (Session ID) بهعنوان کلید استفاده کرد.
در اینجا از متغیر $binary_remote_addr
استفاده شده که آدرس IP کلاینت را بهصورت دودویی (باینری) در نظر میگیرد تا فضای کمتری مصرف شود. با گزینه zone=limitbyaddr
نام ناحیه حافظه را تعیین میکنیم و اندازه آن را برابر با ۱۰ مگابایت در نظر گرفتهایم.
دستور limit_conn_status
مشخص میکند که در صورتی که تعداد کانکشنهای یک کلاینت از حد مجاز فراتر رفت، چه کد وضعیت HTTP به او بازگردانده شود. در این مثال از کد 429 (Too Many Requests) استفاده شده که نشاندهنده تعداد بیش از حد مجاز درخواستها است.
در نهایت با استفاده از دستور limit_conn
محدودیت اعمال میشود. در این دستور ابتدا نام ناحیه حافظهای که برای مدیریت کانکشنها تعریف شده است (در اینجا limitbyaddr) را وارد میکنیم و سپس حداکثر تعداد کانکشن مجاز برای هر کلاینت را تعیین میکنیم که در این مثال 40 کانکشن همزمان است.
دستورات limit_conn
و limit_conn_status
را میتوان درون بلاک های http
, stream
و location
بکار برد در حالی که دستور limit_conn_zone
را تنها درون بلاک http
میتوان استفاده کرد.
به صورت پیش فرض مقدار limit_conn_status
برابر با 503 (Service Unavailable) است.
برای محدود کردن نرخ درخواست های هر کلاینت, میتوان از دستور limit_req استفاده کرد. این دستور به همراه limit_req_zone
و limit_req_status
مورد استفاده قرار میگیرد.
در مثال بالا به اندازه 10MB حافظه برای نگهداری اطلاعات مربط با rate limit در نظر گرفتیم. همچنین محدودیت نرخ درخواست ها بر اساس آیپی کاربران خواهد بود که به صورت باینری درون حافظه نگهداری خواهد شد. همچنین مشخص کردیم کاربر 3 درخواست در هر ثانیه میتواند داشته باشد و اگر از این مقدار بیشتر شود به باقی درخواست ها با استاتوس 429 پاسخ داده خواهد شد.
در دستور limit_req
مقدار zone
به صورت اجباری باید حتما مشخص شود. مقادیر دیگری مانند burst
, delay
و nodelay
هستند که در صورت نیاز میتوانند مورد استفاده قرار بگیرند.
burst=12
: این مقدار تعیین میکند که یک آیپی مجاز است تا چه تعداد درخواست اضافی (فراتر از نرخ مجاز) را به صورت پشت سر هم (بدون رد شدن فوری) ارسال کند قبل از اینکه Nginx شروع به رد درخواستها کند. در اینجا، عدد 12 یعنی هر کاربر با آیپی خود میتواند در یک بازه کوتاه تا 12 درخواست اضافه بفرستد که موقتا در صف میمانند.
delay=9
: این یعنی اولین 9 درخواست اضافه (از همان burst) به صورت تأخیری پردازش میشوند (یعنی با توجه به نرخ ۳ درخواست بر ثانیه، به آرامی وارد میشوند). اما اگر تعداد درخواستهای پشت صف بیشتر از 9 شود (یعنی به درخواستهای 10، 11 و 12 برسیم)، این درخواستها بدون تأخیر قبول میشوند (اگرچه باز هم در محدودیت burst هستند). اگر بیشتر از ۱۲ درخواست اضافه شوند (یعنی burst تمام شود)، درخواستها با خطای 429 Too Many Requests رد خواهند شد.
اگر از nodelay
استفاده کنیم درخواستها پشت هم و سریع قبول میشن تا وقتی که ظرفیت burst پر شود. فقط وقتی تعداد درخواستها بیشتر از burst شود، درخواستها با خطای 429 رد خواهند شد.
برای درک بهتر حالت burst به همراه delay و nodelay میتوانید از نمودار زیر استفاده کنید. این نمودار 7 ثانیه از تعداد درخواست های قبول شده و ریجکت شده ی کلاینتی که به سمت سرور در هر ثانیه ۲۰ درخواست ارسال میکند را نشان میدهد:
همانطور که میبنیم وقتی burst=12 است, اگر nodelay باشد در ثانیه اول به ۱۲ درخواست پاسخ داده میشود و در ثانیه های بعدی به ۳ درخواست در هر ثانیه پاسخ داده میشود و با تمام شدن درخواست ها نرخ پاسخ نیز صفر می شود.
اما در حالتی که delay=9 استفاده شود در ثانیه اول به ۳ درخواست پاسخ داده میشود و 9 درخواست دیگر به درون صف برده میشوند. نرخ پاسخ دهی همیشه 3 می ماند. چون درخواست ها به درون صف برده میشوند زمانی که دیگر کاربر درخواست جدیدی ارسال نکند همچنان nginx تا 3 ثانیه بعد به درخواست هایی که درون صف قرار داده نیاز است تا پاسخ دهد.
در حالت nodelay
شما یکبار میتوانید تا burst=12
درخواست را یکجا (instant) بپذیرید.
به محض مصرف این 12 توکن، حسابگر (token bucket) «خالی» میشود و از آن به بعد فقط با نرخ rate=3r/s
توکن جدید اضافه میکند.
بنابراین در ثانیهٔ اول 12 درخواست پردازش (instant) میشوند، اما در ثانیهٔ دوم و سوم هرکدام فقط 3 تا (به اندازهٔ سرعت refill) قابل پردازش هستند. هیچوقت نمیتوانید پشت سر هم سه بار 12 درخواست instant بفرستید مگر اینکه بین آنها حداقل 4 ثانیه صبر کنید تا توکنها دوباره پر شوند.
در حالت delay
هیچ پردازشی فراتر از rate=3r/s
انجام نمیشود.
تا burst=12
درخواست اول «تأخیر» داده و به صف داخلی NGINX میروند، ولی باز هم پاسخها با همان نرخ 3r/s
خارج میشوند.
بنابراین آن 9 درخواستی که «در صف» ماندهاند، بهتدریج (3 تا در ثانیه) پاسخ داده میشوند و نه یکجای 9 تای یکهو.
خلاصه اینکه:
حالت nodelay تنها یکبار اجازهٔ burst میدهد، بعد از آن refill با rate انجام میشود.
حالت delay کلاً با نرخ rate پاسخ میدهد و burst فقط اندازهٔ queue برای تاخیر است، نه اینکه پاسخها را یکجا ارسال کند.
برای ایجاد محدودیت پهنای باند میتوان از ترکیب دستورات limit_rate_after
و limit_rate
استفاده کرد.
محدودیت پهنای باند به ازای هر کانکشن اعمال می شود. بدین معنی که اگر کاربر چندین کانکشن داشته باشد محدودیت برای هر کانکشن به صورت مجزا محاسبه خواهد شد.
در مثال بالا, سرعتی که پاسخ به کلاینت ارائه میشود، بعد از 10 مگابایت به 1 مگابایت در ثانیه محدود خواهد شد. محدودیت پهنای باند به ازای هر اتصال است، بنابراین میتوانید در صورت لزوم، علاوه بر محدودیت پهنای باند، محدودیت اتصال نیز اعمال کنید.
بنابراین با توجه به تنظیمات بالا سرعت دانلود تا ۱۰ مگابایت اول نامحدود است. بعد از آن سرعت به ۱ مگابایت بر ثانیه محدود میشود. این تنظیمات معمولا به این دلیل اعمال میشود که کاربرانی که فایلهای خیلی بزرگ دانلود میکنند باعث اشغال کل پهنای باند سرور نشوند، اما همچنان کاربران عادی بتوانند با سرعت بالا فایلهای کوچک را سریع دریافت کنند.
قسمت قبل: لود بالانس | Nginx from scratch
قسمت بعد: کش (cache) | Nginx from scratch