آرش قصد دارد یک وبسرویس خفن پیادهسازی کند، اما او میداند که از همان ابتدای کار، تعداد درخواستهای کاربران به endpoint هایی که بار پردازشی بالایی دارند بسیار زیاد خواهد بود. بنابراین او تصمیم گرفته که از rate limiter استفاده کند. از آنجایی که او نمیداند این rate limiter را چگونه پیادهسازی کند، از شما خواسته تا آن را برایش پیادهسازی کنید.
جزئیات پروژه
پروژهی اولیه را از این لینک دانلود کنید. ساختار فایلهای پروژه بهصورت زیر است:
rate-limit
├── db
│ └── connection.go
├── handlers
│ └── root.go
├── limiters
│ ├── by_app_key.go
│ └── by_ip.go
├── test
│ └── ratelimit_sample_test.go
├── go.mod
├── go.sum
└── main.go
وابستگی time/rate در پروژه تعریف شده و برای پیادهسازی rate limiter باید از آن استفاده کنید.
پایگاه داده
در این پروژه از پایگاه دادهی PostgreSQL استفاده شده است. برنامه شامل جدولی با نام app_keys است که شامل دو ستون id از نوع BIGSERIAL و key از نوع VARCHAR(255) است.
در پکیج db تابعی با نام GetConnection تعریف شده که باید از آن برای دریافت کانکشن دیتابیس استفاده کنید. میتوانید اطلاعات اتصال به دیتابیس را در این فایل تغییر دهید.
روتها
تنها یک روت با آدرس / در برنامه تعریف شده است که میتوان rate limiter های مختلف را روی handler آن اعمال کرد.
تابع ByIp
این تابع بهترتیب شامل سه پارامتر زیر است:
nextاز نوعhttp.Handler: همان handler اصلی روت است.refillRateاز نوعrate.Limit: میزان افزایش تعداد درخواستهای مجاز کاربر در هر ثانیه (برای مثال اگر مقدار آن برابر با ۳ باشد، در هر ثانیه، ۳ واحد به تعداد درخواستهای مجاز کاربر اضافه میشود، به شرطی که تعداد درخواستهای مجاز فعلیاش کوچکتر ازtokenBucketSizeباشد.)tokenBucketSizeاز نوعint: سقف تعداد درخواستهای پیاپی کاربر
این تابع را طوری پیادهسازی کنید که درخواستها را براساس آیپی محدود کند. در صورتی که کاربر با محدودیت مواجه نشده باشد، پردازش درخواست باید توسط next صورت گیرد. در غیر اینصورت، کد پاسخ باید به 429 تغییر کند، مقدار هدر Content-Type باید برابر با application/json قرار داده شود و بدنهی پاسخ بهصورت زیر باشد:
{"error": "too many requests"}
تابع ByAppKey
امضای این تابع مشابه تابع ByIp است.
این تابع را طوری پیادهسازی کنید که درخواستها را براساس مقدار هدر X-App-Key موجود در درخواست محدود کند. اگر مقدار این هدر در جدول app_keys وجود نداشته باشد، محدودیت نباید اعمال شود. در غیر اینصورت، محدودیت باید اعمال شود. اگر کاربر با محدودیت مواجه شده باشد، پاسخ باید مشابه پاسخ مورد انتظار برای تابع ByIp باشد.
تضمین میشود که هنگام استفاده از این تابع، هدر X-App-Key در درخواست موجود است.
ترکیب rate limiter ها
ممکن است بخواهیم rate limiter ها را ترکیب کنیم. اگر از توابع ByIp و ByAppKey در کنار یکدیگر استفاده شود، محدودیت باید هم براساس آیپی و هم براساس مقدار هدر X-App-Key اعمال شود. مثلاً اگر مقدار tokenBucketSize برابر با ۵ باشد و ۵ درخواست پیاپی با آیپیهای مختلف، اما با X-App-Key یکسان (بهطوری که در جدول app_keys موجود باشد) ارسال کنیم، باید با محدودیت مواجه شویم. همچنین اگر این درخواستها را با آیپی یکسان، اما با X-App-Keyهای متفاوت (بهطوری که در جدول app_keys موجود باشند) ارسال کنیم، باز هم باید با محدودیت مواجه شویم.
نکات
- نیازی به persistent بودن اطلاعات مربوط به rate limiter ها نیست (با خاتمهی برنامه، دادههای مربوط به rate limiter ها میتوانند از بین بروند).
- شما تنها مجاز به اعمال تغییرات در پوشهی
limitersهستید. - در صورت نیاز، میتوانید فایلهای جدیدی در پوشهی
limitersتعریف کنید.
آنچه باید آپلود کنید
پس از پیادهسازی موارد خواستهشده، پوشهی limiters را زیپ کرده و آپلود کنید.
ارسال پاسخ برای این سؤال