איך בניתי Sliding Window Rate Limiter ב-Redis
ה-video API שלנו נטה לקרוס בכל ערב בשעה 20:00 UTC.
זה לא היה תעבורה אמיתית. כמה scrapers מצאו את ה-trending endpoint שלנו והציפו אותו. שאילתות מסד הנתונים שלנו נערמו. משתמשים אמיתיים ראו סמלי טעינה (loading spinners) במקום סרטונים.
היינו צריכים rate limiting. רוב השיטות הפשוטות רק החמירו את הבעיה.
הבעיה עם מוני Fixed Window: רוב האנשים מתחילים עם חלונות קבועים (fixed windows). אתם סופרים בקשות בבלוק זמן מוגדר. אם המגבלה שלכם היא 100 לבקשה לדקה, משתמש יכול לשלוח 100 בקשות ב-11:59:59 ועוד 100 ב-12:00:00. זה אומר 200 בקשות בשנייה אחת. scrapers מנצלים את הגבולות האלו כדי לעקוף את המגבלות שלכם.
הפתרון: Sliding Window. sliding window סופר בקשות ב-N השניות האחרונות ביחס לרגע הנוכחי. אין גבולות שאפשר לנצל.
אנחנו משתמשים בשתי שיטות שונות בהתאם לצרכים שלנו:
- Sliding Window Log (לדיוק גבוה) אנחנו משתמשים ב-Redis Sorted Sets (ZSET) עבור משתמשים מחוברים (authenticated users). אנחנו שומרים timestamp עבור כל בקשה.
- אנחנו משתמשים ב-
ZREMRANGEBYSCOREכדי למחוק רשומות ישנות. - אנחנו משתמשים ב-
ZCARDכדי לספור בקשות נוכחיות. - אנחנו משתמשים ב-
ZADDכדי לתעד בקשות חדשות.
כדי למנוע race conditions, אנחנו מריצים את הלוגיקה הזו בתוך Lua script יחיד. זה הופך את הפעולה לאטומית (atomic). סקריפט אחד מטפל בהכל בסיבוב אחד ל-Redis. זה מונע משתי בקשות לחשוב שהן מתחת למגבלה בו-זמנית.
- Sliding Window Counter (לסקייל) עבור תעבורה אנונימית, יש לנו מיליוני מפתחות (keys). אנחנו לא יכולים לשמור כל timestamp. במקום זאת, אנחנו משתמשים במונה (counter) שנותן משקל לחלון הנוכחי ולחלון הקודם. זה משתמש בהרבה פחות זיכרון והוא יעיל מאוד.
לקחים מרכזיים משימוש בייצור (production):
- השתמשו ב-Lua scripts. זה מבטיח שהבדיקה והכתיבה יתבצעו כפעולה אחת יחידה.
- שלחו headers סטנדרטיים. תמיד שלחו
X-RateLimit-Remainingו-Retry-After. זה עוזר ללקוחות (clients) "מתנהגים היטב" להאט אוטומטית. - Fail open. אם Redis קורס, אפשרו את הבקשה. API עמוס עדיף על API שמת ואינו מגיב כלל.
- זהו לקוחות בצורה נכונה. השתמשו ב-API keys עבור משתמשים וב-hashed IPs עבור אורחים.
- התאימו מגבלות ל-endpoints. endpoint חיפוש כבד זקוק למגבלות הדוקות יותר מאשר endpoint רשימה פשוט.
התוצאה הייתה מיידית. קפיצות העומס בשעות הערב נעלמו. ביצועי מסד הנתונים שלנו חזרו לנורמה.
