Rate limiting ב-nginx: היכן השרת נשבר בלי הגבלות
פרץ בקשות מגיע לעיתים רחוקות בצורה אחידה. לרוב זה לקוח אחד, סקריפט סריקת קטלוג שהשתחרר, או רובוט חיצוני שפותח מאות חיבורים במקביל ולא משחרר אותם עד לפקיעת זמן ההמתנה. השרת בפועל נשאר חי: התהליך רץ, הפורט מאזין, אבל המטפלים תפוסים על ידי מקור אחד.
מאגר המטפלים הוא משאב משותף וסופי. כשכל המטפלים הזמינים ממתינים לתשובה מבסיס הנתונים או משירות חיצוני על בקשות כבדות, פניות חדשות עומדות בתור לקבלה. התור גדל מהר יותר מקצב הטיפול בו, וזמן התגובה לשאר הלקוחות עובר מחלקיק שנייה לעשרות שניות.
משם נכנסת שרשרת: המשתמשים לא ממתינים ועוזבים, וניסיונות חוזרים אוטומטיים מוסיפים עומס. בלוגים זה נראה אותו דבר — ניתוקי חיבורים בשיא כשבלי קוד משתנה, עלייה בשיעור השגיאות ואזהרות על מחסור במטפלים פנויים. הסיבה אינה בחומרה, אלא במיצוי מאגר המטפלים ובהמתנות שהצטברו.
מסך חיצוני לפני השרת לא מבחין בתמונה הזאת: הוא חותך את כולם ללא הבחנה, כולל התראות של מערכת התשלומים וסריקה של רובוטים של מנועי חיפוש. Rate limiting nginx עובד קרוב יותר לאפליקציה — סופר קצב לפי מפתח (כתובת, סשן, טוקן גישה, מסלול) ומחזיר לעודפים תשובה תקינה במקום מקום בתור.
עומס יתר על שרת האינטרנט מפסיק להיות אירוע כולל: ההגבלה מוטלת על מקור מסוים, והאחרים מטופלים כרגיל. נסביר כיצד מגדירים הגנה כזו מפני פרץ בקשות והיכן עובר הגבול בינה לבין חסימה של המשתמשים שלנו עצמם.
מה נותנת הגבלת קצב בקשות בכניסה
זמן התגובה מפסיק לגדול תחת עומס שיא
כלל בפרוקסי הכניסה (nginx) פועל לפני האפליקציה: זרימה עודפת מקבלת סירוב קצר ולא תופסת תהליכי עבודה. התור לא מצטבר, ולכן זמן תגובה יציב נשמר גם בשיא — בשעת דיוור או בתחילת מבצע, במקום עלייה פי כמה. הספים נקבעים לפי פרופיל עומס שנמדד על תעבורה אמיתית, ולא אחרי תקלה.
נחסמים ניסיונות כוח על סיסמאות ופרסינג המוני של הקטלוג
כלל נפרד לטופס ההתחברות סופר לא רק קצב, אלא גם את שיעור הניסיונות הכושלים מכתובת אחת — סדרה של מאות שגיאות לא הופכת לניסיון כוח. עבור הקטלוג הסף רך יותר: חיתוך פרסרים משאיר לבני אדם גישה מלאה, ולסקריפט מכתובת אחת — רק חלק מהעמודים בדקה. רובוטים של מנועי חיפוש עוברים לפי רשימה נפרדת ופועלים בלי הגבלה.
סירוב של לקוח אחד לא מפיל את האחרים
בידוד לקוח נחוץ במקום שבו מאחורי כתובת אחת נמצא משרד או ייצוא של שותף: בלעדיו פרץ אחד תופס את כל תהליכי העבודה ומפיל את השירות לכולם. עם הפרדה לפי כתובת, מפתח גישה וסשן — רק המפריע נתקע, והשאר ממשיכים לעבוד. כך נעלם סוג שלם של כשלים — לא "השרת נפל", אלא "מקור מסוים מיצה את המכסה שלו".
Rate limiting ב-nginx: רמות הגנה ומגבלותיהן
הגבלות מציבים בכמה רמות, וכל אחת מכסה סוג אחר של עומס יתר. ככל שהמונה קרוב יותר ללקוח, כך הסירוב זול יותר: בקשה שנחתכת בפרוקסי החיצוני לא מגיעה לא לאפליקציה ולא לבסיס הנתונים.
היכן להציב הגבלה
| רמה | מתי מתאים | מגבלות |
|---|---|---|
| פרוקסי חיצוני לפני הכניסה | פרצים המוניים, סורקים, ניסיונות כוח על טפסי התחברות | נראית רק כתובת הלקוח: מאחורי רשת משרדית המכסה מתחלקת בין כולם, וזרימה מבוזרת עוקפת אותה |
| nginx בכניסה: הגבלה לפי קצב ולפי חיבורים בו-זמניים | פתרון עבודה לאתר ול-API | האזור ומפתח הספירה חייבים להיות מתואמים; בכמה צמתים בלי אחסון משותף המונים בלתי תלויים, והמגבלה הכוללת מיטשטשת |
| אפליקציה או שער | הגבלות לפי חשבון, טוקן, תפקיד, מסלול נפרד | נספר לאחר ניתוח הבקשה — יקר יותר; הכללים לא תואמים לקונפיגורציית המאזן |
מפתח הספירה הוא הבחירה המרכזית: כתובת הלקוח, כתובת תוך התחשבות במקור האמיתי מאחורי פרוקסי, סשן או טוקן. ככל שהמפתח מדויק יותר, כך יש פחות הפעלות שגויות בכתובות משותפות.
מה ההגבלות לא פותרות
הגבלת קצב עוסקת בעומס לגיטימי ובפרצים גסים, לא בהגנה שלמה. היא לא תעצור ניסיון כוח איטי בעצמה נמוכה מהרבה כתובות, ולא תציל מבקשה יקרה שנכנסת למכסה אבל מעמיסה על בסיס הנתונים. גם לא תבחין בין בוט ללקוח אם המפתח הוא רק כתובת.
לכן הגבלות עובדות יחד עם מטמון, פסקי זמן, הגבלת גודל גוף הבקשה וניתוח לוגים: כל אחד מכסה את חלקו, ובלעדיהם אף אחד מהם לא נותן יציבות.
שלבי הטמעת הגבלות: מביקורת ועד נהלים
מתחילים בביקורת של תעבורה נכנסת: מנתחים לוגים של פרוקסי הפוך, מוצאים מסלולים ומקורות עם עיקר הנפח, מרווחי שיא וחלק של בקשות שירות. מזה מרכיבים פרופיל עומס — הקצב שהשירות מחזיק בו עם מרווח, והנקודות שבהן מתחילה ירידה בביצועים.
- ביקורת תעבורה נכנסת — לוגים, מסלולים, מקורות, מרווחי שיא.
- פרופיל עומס — איזה קצב השירות סופג, ואיזה מוביל לעלייה בזמן התגובה.
- סכימת אזורים וספים — היכן להציב הגבלה: בכניסה לאשכול, בשירות נפרד או במסלול יקר, עם ספים שונים ללקוחות מזוהים ואנונימיים.
- הגדרת כללים — ההגבלות עצמן, קודי תגובה, כותרות עם יתרת המכסה, התנהגות בחריגה.
- בדיקה בסביבת בדיקות — מריצים את הכללים על העתק של הסביבה כדי לראות את מי בדיוק ההגבלה מגבילה.
- ריצת עומס ומסירת נהלים — מחזיקים שיא, משווים מדדים לתוכנית, מוסרים לצוות מסמך עם ספים וסדר העדכון שלהם.
כיצד מתבצעת הבדיקה תחת עומס שיא
הריצה חוזרת על פרופיל העומס מהביקורת, כולל פרצים קצרים ממקור אחד. בודקים שההגבלה פועלת לפני שזמן התגובה של האחרים עולה, ושתעבורה לגיטימית לא נופלת תחת הסף — אחרת ההגבלה פוגעת בשלנו.
מה נדרש מצדכם
גישה ללוגים ולמדדים של השירות, חלון לבדיקה על העתק של הסביבה, ואיש קשר של תורן שיאשר את הספים. השאר — העבודה שלנו: כללים, ריצה, נהלים וניתוח תוצאות.
מה נשאר אצלכם: קונפיגורציות, סכימות אזורים ונהלים
אנחנו מוסרים לא "שרת מכוון", אלא ערכה עובדת: קונפיגורציות להגבלת קצב בקשות ב-nginx, סכימת אזורים וספים, רשימת כתובות מהימנות, הוראה לתורן ונוהל תגובה לאירוע. הכול נמצא במאגר של הלקוח, השינויים עוברים סקירה ומתפרסים לפי תהליך האספקה הרגיל.
הרכב חבילת המסמכים
- קונפיגורציות עם הערות: לפי איזה מפתח נספרת ההגבלה (כתובת, טוקן, cookie) ואילו אזורים הוצהרו.
- סכימת אזורים וספים — טבלה «אזור → הגבלה → פרץ → קוד תגובה», כדי שהערכים ישתנו במודע ולא באקראי.
- רשימת כתובות וכתובות משנה מהימנות: רשימות לבנות לניטור, callbacks של תשלומים, אינטגרציות ורשתות משרדיות.
- הוראה לתורן: כיצד להבחין בין פרץ לגיטימי להתקפה וכיצד להפעיל מצב מקל.
- נוהל תגובה: מי מסיר את ההגבלה, את מי מודיעים, מתי מעלים את הסף ומתי חותכים חזק יותר.
- תרחישי ריצת עומס — עליהם מאשרים ששיאים חוקיים עוברים, בעוד ניסיון כוח גס מקבל סירוב.
- יומן שינויים של הגבלות: תאריך, סיבה, מי אישר, — כך רואים מאין הגיע הערך הנוכחי.
כיצד לתחזק זאת בלעדינו
הספים חיים בקובץ אחד ומשתנים בלי לשכתב את הלוגיקה. ההוראה והנוהל מתעדכנים יחד עם הסכימה, ולכן מהנדס חדש נכנס לקונטור בערב, ולא בשבוע.
מקרה בוחן: כיצד חנות אינטרנטית עברה את שיא המבצע
שיא מבצע מכירות הוא היום שבו התשתית של חנות אינטרנטית נבחנת באמת: הזרימה גדלה פי כמה תוך דקות, וכל מה שלא עמד בכך נראה מיד. להלן נתונים מדוחות ניטור לפני ואחרי הטמעת הגבלת קצב בקשות בצומת הכניסה על nginx.
מה היה לפני
ברגע תחילת המבצע צומת הכניסה קיבל את כל הזרימה בלי הבחנה: שיעור הבקשות המוצלחות ירד ל-62%, זמן התגובה בשיא הגיע ל-9 שניות — הקונה סגר את הקטלוג לפני שקיבל תשובה. חלק מהתעבורה היו בוטים: הם סרקו את הקטלוג ומילאו עגלות, ותפסו את כל תהליכי העבודה של האפליקציה.
שגיאות שרת גדלו פי 40 לעומת יום רגיל, והתמודדו עם זה רק אחרי המבצע, לפי הלוגים.
מה קרה אחרי
את ההגבלות פיצלו לפי רמות: סף קשיח לכתובת המקור עבור חיפוש וביצוע הזמנה, סף רך — עם תור ועיכוב עבור הקטלוג והתמונות, ופרופיל נפרד עבור רובוטים של מנועי חיפוש.
שיעור הבקשות המוצלחות בשיא עלה ל-99.4%, זמן התגובה לא חצה 1.2 שניות, ומספר שגיאות השרת ירד בסדר גודל — עם אותה זרימת קונים.
הפעלות נראות בדוח הניטור לפי שם הכלל, ולכן חסימה שגויה מוסרת בתיקון אחד, בלי הפעלה מחדש של השירות. הספים נבחרו לפי פרופיל תעבורה אמיתי ונבדקו מחדש אחרי כל חזרת עומס; הקונפיגורציה מנוהלת בגרסאות יחד עם שאר התשתית, ובמבצע המכירות הבא התוצאה משוחזרת.
במה הגבלה כוללת שונה מהגבלה לכתובת ספציפית
הגבלה כוללת קובעת תקרה אחת לכל השירות: מפתח הספירה הוא קבוע, ולכן כל הבקשות נופלות למונה משותף. מצב כזה תופס זרימת יתר אחידה ותקלות שבהן התעבורה גדלה בגלל כשל אצל לקוח או שגיאה באינטגרציה.
לעומת זאת, הוא מפספס חוסר איזון: אם כתובת אחת לוקחת תשע עשיריות מהמכסה, האחרים מקבלים סירובים, אף שהסף הכולל לא חרג פורמלית.
הגבלה לפי כתובת לקוח סופרת כל מקור בנפרד, וחריגה של אחד לא פוגעת באחרים. כאן רואים סקרים תכופים, איסוף נתונים אוטומטי, סביבת בדיקות נשכחת עם בקשות חוזרות. אבל זרימה מבוזרת ממאות כתובות עוברת במצב כזה: כל אחת עומדת במכסה שלה, והעומס הכולל על השירות נשאר הרסני.
בחירת מפתח ההגבלה תלויה במה שאנחנו מגנים עליו: עבור ממשק חיצוני לוקחים כתובת, עבור קריאות פנימיות — מזהה לקוח או מסלול, עבור פעולות יקרות — שילוב של סימנים. לצד זה מגדירים מצבי ספירת בקשות: בנפרד מהירות ממוצעת ובנפרד פרץ מותר. אחרת הפעלה לגיטימית של אפליקציה, ששולחת מיד סדרת בקשות, נזרקת כמו התקפה.
בפועל מחזיקים את שני המצבים יחד: כולל עובד כמנתק מגן על כל השירות, והגבלה לפי כתובת — כהגנה מפני מקור מסוים. מה ההגבלה מפספסת תלוי גם במה נחשב לבקשה אחת: אם פניות למסלולים שנשמרים במטמון לא נספרות, אפשר להוריד את הסף בלי סיכון למשתמשים חיים.
ספקות טכניים: מפתח לקוח, פרצים ורשימות לבנות
חמישה ספקות שצפים לפני הגבלת קצב בקשות — לכל אחד יש פתרון מוכן.
מה לקחת כמפתח ספירה: כתובת, טוקן או מסלול?
מפתח בסיסי הוא כתובת הלקוח, אבל מאחורי פרוקסי היא מוחלפת, ולכן לוקחים את הכתובת האמיתית מכותרת ההעברה. עבור API המפתח נבנה לפי טוקן, עבור מסלולים כבדים — לפי הצמד «כתובת + מסלול»: פרץ בחיפוש לא מפיל את הכניסה.
כיצד להבחין בין הפעלה שגויה לעומס אמיתי?
תחילה מצב התבוננות: הכללים סופרים בקשות וכותבים ללוג, אבל לא דוחים. תוך שבוע רואים איזה חלק מהלקוחות מקבל סירוב, ובאילו מסלולים. לרוב אלו עמודים עם טעינת משאבים — מתקנים את המפתח או את הפרץ נקודתית.
האם מבקרים רגילים לא יתחילו לקבל סירובים?
גלישה שקטה מייצרת בקשות בודדות בשנייה, ואנחנו מציבים את ההגבלה בסדר גודל גבוה יותר. הפרץ מאפשר סדרה קצרה, ולכן עמוד עם עשרה קבצים נטען במלואו. סירוב מקבל אוטומציה, לא אדם.
האם אפשר להפעיל בשירות פעיל בלי השבתה?
הקונפיגורציה מיושמת בקריאה מחדש, חיבורים שכבר פתוחים לא נקטעים. את הכלל מכניסים תחילה במסלול אחד או בחלק מהצמתים, בודקים את שיעור הסירובים ואת זמן התגובה, ואז פורסים. החזרה לאחור — הסרת הכלל באותה קריאה מחדש.
מה לעשות עם מקורות מהימנים?
ניטור, בדיקות שירות ואינטגרציות של שותפים מוציאים לאזורים נפרדים עם ספים גבוהים, ולא לחריגה מלאה. כתובת מהימנה לא הופכת לחור: בחריגה ההגבלה בכל זאת תפעל. הרשימה חיה לצד הכללים ועוברת סקירה בכל שינוי.
נדון בעומס שלכם ונחזור עם הערכה ותוכנית
דיון במשימת הגבלת קצב בקשות מתנהל מהר יותר כשיש עובדות על העומס, ולא רק תיאור של תסמינים. ננתח אותן יחד עם הטופולוגיה של הקונטור ונסתדר על איזו רמה להציב הגבלות: בכניסה לפני הפרוקסי, בתוכו לפי מסלולים, או באפליקציה עצמה.
כדאי להכין לשיחה:
- פרופיל שיאים: מקסימות יומיות ושבועיות, פרצים משליחות דיוור, חלק התעבורה האנונימית והשירותית;
- סכימת הקונטור: היכן נמצאים מאזן העומסים והפרוקסי, כמה צמתי אפליקציה, האם יש מטמון ותור;
- רשימת נקודות כניסה ציבוריות ומתודות שאסור להגביל באותה צורה;
- היסטוריית אירועים: מתי ועל מה נפל, אילו בקשות נראו כמו ניסיון כוח או פרסינג המוני;
- צמיחה צפויה: השקות מתוכננות, עונתיות, אינטגרציות חדשות ולקוחות מובייל;
- גישה לסביבת בדיקות, כדי לבדוק כללים לפני העלאה לפרודקשן.
מהניתוח הזה נבנית הערכה של היקף העבודות ותוכנית הטמעה בשלבים. מתחילים ממצב התבוננות: הכללים סופרים חריגות אבל לא חוסמים, ורואים את מי זה פוגע. אחר כך מפעילים סירוב עם קודי תגובה וכותרות ברורים, וקובעים ספים נפרדים למסלולים קריטיים. הצעדים הבאים — פיילוט בשירות אחד, מדידת שיעור הבקשות שנדחו וזמן התגובה, ופריסה לשאר הצמתים.







