Throttling ב-API: ויסות הדרגתי מול חסימה קשיחה
מאת צוות מדיה דיל · 10.08.2026 · API Engineering · 8 דק׳
rate limiting דוחה, throttling מווסת. מדריך עומק ל-load shedding, adaptive throttling, backpressure, ומתי כל גישה מתאימה בפועל.
שני צוותים מתכננים הגנה מפני עומס יתר ב-API, ושניהם קוראים לזה "rate limiting" — אבל אחד מתכוון לחסימה קשיחה עם שגיאת 429, והשני מתכוון להאטה הדרגתית שנותנת לצרכן להמשיך לעבוד, רק לאט יותר. הבלבול הזה נפוץ כי throttling ו-rate limiting חולקים מטרה משותפת — הגנה על המערכת מפני עומס — אבל מתממשים אחרת לגמרי, ובחירה לא מודעת בין הגישות יכולה ליצור חוויית צרכן גרועה גם כשהכוונה הייתה טובה.
ההבדל המהותי הוא בתגובה לחריגה. rate limiting, כפי שהרחבנו במדריך הגבלת קצב ב-API, בדרך כלל דוחה בקשה עודפת מיידית עם שגיאה ברורה. Throttling, לעומת זאת, שואף לווסת את הקצב באופן חלק יותר — על ידי עיכוב תשובות, הפחתת עדיפות, או האטה הדרגתית — כדי לשמור על יציבות המערכת הכוללת מבלי לחסום כל צרכן בנפרד באופן בינארי.
Throttling ברמת המערכת מול Throttling ברמת הצרכן
יש הבדל חשוב בין שני סוגי throttling. throttling ברמת צרכן בודד מווסת את הקצב שצרכן ספציפי מקבל, בדומה ל-rate limiting אך בצורה הדרגתית יותר. throttling ברמת המערכת כולה הוא מנגנון שונה לגמרי — הוא מגן על המערכת עצמה מפני קריסה תחת עומס מצטבר מכל הצרכנים גם יחד, גם אם אף אחד מהם בנפרד לא חורג ממכסה אישית. כשהעומס הכולל על שירות מסוים עולה מעבר לקיבולת שלו, המערכת יכולה להתחיל להאט תשובות לכולם באופן שווה, או לתעדף בקשות קריטיות על פני בקשות פחות דחופות, כדי למנוע קריסה מלאה שהייתה פוגעת בכל הצרכנים בבת אחת.
ההבחנה הזו קריטית לתכנון: מערכת שמטמיעה רק throttling ברמת צרכן בודד עדיין יכולה לקרוס אם מספיק צרכנים לגיטימיים, כל אחד בתוך המכסה האישית שלו, פשוט מגיעים יחד לעומס כולל שהתשתית לא מסוגלת לספוג. הגנה מלאה דורשת שתי השכבות במקביל.
Load Shedding: כשצריך לוותר על חלק כדי להציל את השאר
כשעומס המערכת מתקרב לקיבולת המקסימלית, אסטרטגיית throttling מתקדמת היא load shedding — דחייה מכוונת ומבוקרת של חלק מהבקשות (בדרך כלל הפחות קריטיות) כדי לשמור על יכולת שירות לבקשות החשובות ביותר. במקום לתת לכל הבקשות להאט באופן שווה עד שהמערכת כולה נעשית לא-שימושית לאף אחד, load shedding מקריב במודע חלק מהתעבורה כדי לשמר את היתר בביצועים סבירים.
יישום נכון של load shedding דורש תיעדוף ברור מראש: אילו endpoints או סוגי בקשות נחשבים קריטיים (למשל checkout בחנות online) לעומת פחות קריטיים (למשל recommendations או analytics). כשעומס עולה, המערכת מתחילה לדחות או לעכב את הבקשות הפחות קריטיות תחילה, ומשאירה את מלוא הקיבולת לבקשות החשובות ביותר. זו החלטה עסקית לא פחות מטכנית, ולכן צריכה מעורבות של בעלי מוצר, לא רק צוות תשתית.
Adaptive Throttling: התאמה דינמית לפי מצב המערכת בפועל
throttling סטטי מוגדר מראש עם ספים קבועים; adaptive throttling מתאים את עצמו בזמן אמת לפי מדדים בפועל — latency נוכחי, שיעור שגיאות, או עומס CPU בשירותים הפנימיים. כשהמערכת בריאה, throttling יכול להיות רפוי יחסית; כשמתגלה עומס או האטה, המנגנון מחמיר אוטומטית בלי התערבות ידנית, ומשתחרר שוב כשהמצב משתפר. הגישה הזו נפוצה במיוחד במערכות עם עומס משתנה מאוד לאורך היום (למשל שיא בשעות ערב), ששם סף קבוע אחד תמיד יהיה או מחמיר מדי בשעות שקטות או מקל מדי בשעות שיא.
מימוש adaptive throttling דורש לולאת משוב — ניטור רציף של מדדי בריאות המערכת, והזנתם חזרה לרכיב שמחליט על רמת ה-throttling הנוכחית. זו מורכבות הנדסית משמעותית מעבר ל-throttling סטטי, ולכן משתלמת בעיקר במערכות עם עומס משתנה מאוד ותקציב הנדסי לתחזק לוגיקה דינמית כזו, לא כברירת מחדל לכל מערכת.
Queueing ו-Backpressure: לעכב במקום לדחות
אסטרטגיה נוספת של throttling, שונה מדחייה מיידית, היא queueing — במקום לדחות בקשה עודפת, המערכת מכניסה אותה לתור וממתינה עד שיש קיבולת פנויה לעבד אותה, עד timeout מוגדר. זה מתאים לתרחישים שבהם עיכוב קצר מקובל אבל דחייה מוחלטת לא — למשל עיבוד תשלום שאפשר להשהות במקצת אבל לא לבטל לגמרי. backpressure הוא מנגנון קשור: ה-consumer מאותת ל-producer שהוא לא מסוגל לקלוט תעבורה בקצב הנוכחי, וה-producer מאט את עצמו בהתאם, במקום להמשיך לשלוח ולגרום ל-buffer להתמלא ולגלוש.
ב-gRPC, backpressure מובנה בפרוטוקול עצמו דרך HTTP/2 flow control, מה שהופך אותו לכלי טבעי לתרחישי streaming עם קצבי עיבוד משתנים, כפי שהרחבנו במדריך ארכיטקטורת gRPC. ב-REST, backpressure אמיתי דורש תכנון מפורש — לרוב דרך תורים חיצוניים (כמו RabbitMQ או SQS) שקולטים עומס ומעבדים אותו בקצב שהמערכת יכולה לספוג, ולא ישירות מ-HTTP request ל-processing מיידי.
Circuit Breaking כשכבת throttling הדדית בין שירותים
throttling לא חייב לקרות רק כלפי צרכנים חיצוניים — הוא רלוונטי באותה מידה לתקשורת פנימית בין microservices. כשמבצע קריאה עלול "להציף" שירות תלוי בקצב גבוה מדי, circuit breaker ממלא תפקיד דומה ל-throttling ברמת המערכת: הוא מווסת את קצב הקריאות בפועל לפי מצב הבריאות של השירות המרוחק, ולא רק חוסם בבינריות מלאה. שילוב בין circuit breaking לבין throttling הדרגתי — למשל, הפחתת קצב הקריאות בהדרגה ככל שהשירות המרוחק מראה סימני עומס, לפני שמגיעים למצב open מלא — נותן הגנה חלקה יותר מאשר מעבר פתאומי בין "עובד לגמרי" ל"חסום לגמרי". הרחבנו על מנגנון ה-circuit breaker עצמו במדריך ארכיטקטורת API Gateway.
throttling בממשקי streaming ו-webhooks
בממשקי streaming (gRPC streaming, WebSocket), throttling מתבטא אחרת מאשר בקריאות בקשה-תשובה בודדות — במקום להחליט אם לאשר בקשה חדשה, המערכת צריכה להחליט באיזה קצב לשלוח הודעות בתוך stream פעיל שכבר קיים. throttling כזה קשור הדוקות ל-backpressure: אם ה-consumer לא מצליח לעבד הודעות בקצב שהן נשלחות, buffer בצד השולח מתחיל להתמלא, וללא מנגנון האטה מכוון, זה עלול לגרום ל-out-of-memory בצד השולח או לניתוק חיבור בלתי צפוי.
webhooks מציבים אתגר דומה בכיוון ההפוך: מערכת ששולחת webhooks להרבה subscribers צריכה throttling לכל subscriber בנפרד, כדי שלא לשלוח יותר בקשות ממה שהשרת המקבל מסוגל לעבד. פרקטיקה נפוצה היא queue ייעודי לכל subscriber עם קצב שליחה מבוקר, ו-exponential backoff אוטומטי כשה-subscriber מתחיל להחזיר שגיאות או timeouts, כדי לא להעמיס עוד יותר על שירות שכבר מתקשה.
תקשורת עם הצרכן: Retry-After ואינדיקציה הדרגתית
גם ב-throttling, כמו ב-rate limiting, תקשורת ברורה עם הצרכן קריטית. כשעיכוב מתרחש, שווה לשקול להחזיר סטטוס ביניים או header שמסמן שהעיבוד מואט (למשל זמן תגובה ארוך משמעותית מהרגיל, יחד עם header מפורש), כדי ש-client-ים חכמים יוכלו להתאים את עצמם — למשל להראות למשתמש הודעת "העיבוד לוקח יותר זמן מהרגיל" במקום להשאיר אותו מול spinner בלי הסבר. שקיפות כזו משפרת משמעותית את חוויית הצרכן גם כשהמערכת בפועל מתמודדת עם עומס גבוה.
איפה בארכיטקטורה קורה ה-Throttling
בדומה ל-rate limiting, throttling ברמת צרכן בודד לרוב נאכף ב-API Gateway, שכבר מרכז את כל התעבורה הנכנסת. הרחבנו על תפקידו המרכזי במדריך ארכיטקטורת API Gateway. throttling ברמת מערכת (load shedding, adaptive throttling) לרוב דורש שיתוף פעולה בין ה-Gateway לבין השירותים הפנימיים עצמם, כי רק הם יודעים בזמן אמת את מצב הבריאות והעומס האמיתי שלהם — ה-Gateway לבדו, בלי מידע מהשירותים, לא יכול לקבל החלטת throttling מדויקת ומבוססת.
טעויות נפוצות בפרודקשן
הטעות הראשונה היא בחירה בין throttling ל-rate limiting בלי מחשבה מודעת — יישום ברירת מחדל של ספרייה מסוימת בלי לשאול אם ההתנהגות (חסימה קשיחה מול האטה הדרגתית) בכלל מתאימה לתרחיש השימוש. טעות שנייה היא throttling בלי תיעדוף ברור — כשכל סוגי הבקשות מואטים באופן שווה תחת עומס, בקשות קריטיות (כמו checkout) סובלות באותה מידה כמו בקשות שוליות (כמו analytics), במקום שהמערכת תקריב את הפחות חשוב כדי להגן על החשוב באמת.
טעות שלישית היא היעדר ניטור על אפקטיביות ה-throttling עצמו — בלי מדידה של כמה בקשות בפועל מואטות, לכמה זמן, ומה ההשפעה על שביעות רצון צרכנים, קשה לדעת אם המנגנון עוזר או רק מוסיף latency מיותר בלי תועלת אמיתית. טעות רביעית היא queueing בלי timeout מוגדר — בקשה שנכנסת לתור ומחכה בלי הגבלת זמן עלולה "לתקוע" משאבי client (thread, connection) לזמן ארוך מדי, וגורמת לבעיות זמינות אצל הצרכן גם אם השרת עצמו לא קרס. טעות חמישית היא הפעלת throttling אגרסיבי מדי בתגובה לאירוע חד-פעמי, בלי מנגנון לחזור אוטומטית להתנהגות רגילה כשהעומס חולף — מערכת שנתקעת במצב "מוגן" גם הרבה אחרי שהעומס האמיתי כבר ירד פוגעת בחוויית משתמש לגיטימית בלי סיבה טובה, ולעיתים דורשת התערבות ידנית לאתחל מחדש את מצב ה-throttling כי אין תנאי יציאה ברור שהוגדר מראש.
מתי כדאי, ומתי rate limiting פשוט מספיק
throttling מתקדם (adaptive, load shedding) שווה את ההשקעה במערכות עם עומס משתנה מאוד, דרישות זמינות גבוהות במיוחד, ומגוון סוגי בקשות עם עדיפויות שונות בבירור. עבור מרבית ה-API-ים, rate limiting קשיח ופשוט עם token bucket, כפי שהרחבנו במדריך הגבלת קצב ב-API, נותן הגנה מספקת בלי המורכבות ההנדסית הנוספת של throttling אדפטיבי — עדיף להתחיל שם ולעבור ל-throttling מתוחכם יותר רק כשיש עדות אמיתית שהוא נחוץ.
מדידה: איך יודעים שה-throttling עובד
throttling טוב נמדד לא רק בכך שהמערכת לא קורסת, אלא גם באיכות החוויה שהוא נותן לצרכנים לגיטימיים תחת עומס. מדדים שכדאי לעקוב אחריהם כוללים את אחוז הבקשות שמושפעות מ-throttling בכל חלון זמן, ההשפעה בפועל על latency ממוצע וב-percentile גבוה (p95, p99), ושיעור ה-timeout-ים שנגרמים ישירות ממנו. בלי מדידה שיטתית, קל להטמיע throttling שנראה סביר בתיאוריה אבל בפועל פוגע בחלק גדול מהתעבורה הלגיטימית בכל פעם שהעומס עולה במעט מעל הרגיל.
סיכום
Throttling הוא כלי עדין יותר מ-rate limiting — במקום לחסום בקשות עודפות באופן בינארי, הוא מווסת את הקצב באופן הדרגתי, לעיתים ברמת המערכת כולה ולא רק ברמת צרכן בודד. הבחירה בין הגישות תלויה בשאלה אם דחייה מיידה מקובלת, או שעיכוב חלק עדיף לחוויית הצרכן — ואין תשובה אוניברסלית, רק התאמה מדויקת לתרחיש השימוש הספציפי.
תגיות: API throttling · load shedding · backpressure · adaptive throttling · circuit breaker · queueing