מבוא
בהתפתחות תוכנה, לוגיקת חזרה אמינה היא חיונית לטיפול בכשלים זמניים, כגון בעיות רשת או הפסקות זמניות. לאחרונה, נתקלתי בקוד שבו מפתח השתמש בלולאת for
עם מרווח זמן קבוע כדי לנסות שוב פעולות שכשלו. בעוד גישה זו עשויה להיראות פשוטה, היא חסרה את העמידות הנדרשת עבור יישומים בעולם האמיתי. כאן נכנס לתמונה חזרתיות אקספוננציאלית – אסטרטגיה שנועדה להפוך את הנסיונות החוזרים לחכמים ויעילים יותר.
במאמר זה, נבחן כיצד פועלת חזרתיות אקספוננציאלית, את היתרונות שלה על פני לולאת חזרה בסיסית, וכיצד ניתן ליישם אותה כדי לשפר את האמינות של המערכת שלך. אני גם אדריך אותך בדוגמה מעשית תוך שימוש במודול שליחת דוא"ל, מראה לך כיצד להשתמש בחזרתיות אקספוננציאלית כדי להבטיח טיפול בכשלים בצורה יותר עמידה.
מהי חזרתיות אקספוננציאלית?
חזרתיות אקספוננציאלית היא אסטרטגיית חזרה שבה הזמן ההמתנה בין ניסי החזרה עולה באופן אקספוננציאלי לאחר כל כישלון. במקום לנסות שוב במרווחים קבועים, כל ניסיון שלא הצליח מחכה יותר מהניסיון הקודם – בדרך כלל מכפיל את העיכוב בכל פעם. לדוגמה, אם העיכוב הראשוני הוא שנייה אחת, הניסיונות הבאים יתרחשו ב-2, 4, 8 שניות, וכן הלאה. גישה זו מסייעת להפחית את העומס על המערכת ומצמצמת את הסיכון להעמיס על שירותים חיצוניים בתקופות של ביקוש גבוה.
על ידי מתן יותר זמן בין ניסי החזרה, חזרתיות אקספוננציאלית נותנת לבעיות זמניות סיכוי להיפתר, מה שמוביל לטיפול בכשלים בצורה יותר יעילה ולשיפור יציבות היישום.
יתרונות וחסרונות של חזרתיות אקספוננציאלית
יתרונות:
-
עומס מערכת נמוך: על ידי הרחבת הזמן בין ניסיונות, ההשהיה הנפוציאלית מפחיתה את הסיכוי לכפות מעמד על שרתים, מיוחד לטיפול במגבלות קצב או תקלות חולפות.
-
טיפול בשגיאות יעיל: ההשהיה המוגברת מאפשרת לבעיות חולפות יותר זמן להיפתר באופן טבעי, שמשפר את הסיכוי לניסיון חוזר מוצלח.
-
יציבות משופרת: במיוחד למערכות עם תעבורת כביכול, זה מונע מבול של ניסיונות חוזרים, ששומר על האפליקציות פועלות בצורה חלקה ללא צריכת משאבים יתר.
חסרונות:
- האטת תגובה: עם כל ניסיון חוזר שמתמשך יותר, ההשהיה הנפוציאלית עשויה לגרום להשהיות, במיוחד אם נדרשים רבים עד שהצליח.
מקרי שימוש מרכזיים להשהיית אקספוננציאלית
ההשהיה האקספוננציאלית שימושית במיוחד בתרחישים בהם מערכות פועלות עם שירותים חיצוניים או ניהול תעבורה בכמויות גדולות. הנה כמה מקרי שימוש נפוצים נוספים:
-
ממשקי API עם הגבלת קצב: כמה ממשקי API יש להם מגבלות קצב, המגבילות בקשות בתוך פרק זמן מסוים. חזרה אקספוננציאלית עוזרת להימנע מניסיונות חוזרים מיידיים שעשויים לחרוג מהמגבלה, ונותנת זמן למגבלה להתאפס.
-
חוסר יציבות ברשת: במקרים של כשלי רשת זמניים או תפוסות זמן, חזרה אקספוננציאלית עוזרת על ידי המתנה זמן רב יותר בין ניסיונות, ומאפשרת לרשת להתייצב.
-
חיבורים למסדי נתונים: כאשר מתחברים למסדי נתונים תחת עומס כבד, חזרה אקספוננציאלית עוזרת למנוע עומס נוסף על ידי דחיית ניסיונות חוזרים, ומאפשרת למסד הנתונים זמן להתאושש.
-
מערכות תורים: במערכות תורים של הודעות, אם הודעה נכשלת עקב שגיאה, שימוש בחזרה אקספוננציאלית עבור ניסיונות חוזרים יכול למנוע עיבוד מחדש מהיר ולאפשר זמן לפתרון בעיות זמניות.
בניית שירות שולח דוא"ל בסיסי עם Exponential Backoff
כדי להדגים Exponential Backoff, נבנה שירות שולח דוא"ל בסיסי שמנסה לשלוח מיילים מחדש אם נתקל בשגיאה. הדוגמה מראה כיצד Exponential Backoff משפר את תהליך הניסיון החוזר בהשוואה ללולאת for פשוטה.
import nodemailer from "nodemailer";
import { config } from "../common/config";
import SMTPTransport from "nodemailer/lib/smtp-transport";
const emailSender = async (
subject: string,
recipient: string,
body: string
): Promise<boolean> => {
const transport = nodemailer.createTransport({
host: config.EMAIL_HOST,
port: config.EMAIL_PORT,
secure: true,
auth: { user: config.EMAIL_SENDER, pass: config.EMAIL_PASSWORD },
} as SMTPTransport.Options);
const mailOptions: any = {
from: config.EMAIL_SENDER,
to: recipient,
subject: subject,
};
const maxRetries = 5; // maximum number of retries before giving up
let retryCount = 0;
let delay = 1000; // initial delay of 1 second
while (retryCount < maxRetries) {
try {
// send email
await transport.sendMail(mailOptions);
return true;
} catch (error) {
// Exponential backoff strategy
retryCount++;
if (retryCount < maxRetries) {
const jitter = Math.random() * 1000; // random jitter(in seconds) to prevent thundering herd problem
const delayMultiplier = 2
const backOffDelay = delay * delayMultiplier ** retryCount + jitter;
await new Promise((resolve) => setTimeout(resolve, backOffDelay));
} else {
// Log error
console.log(error)
return false; // maximum number of retries reached
}
}
}
return false;
};
כיוון פרמטרים של Exponential Backoff
יישום של Exponential Backoff משלים כמה פרמטרים כדי לוודא שאסטרטגיית הנסיון החוזר עובדת טוב לצרכי היישום שלך. הפרמטרים המרכזיים הבאים משפיעים על ההתנהגות והביצועים של Exponential Backoff במנגנון ניסיון חוזר:
- השהייה הראשונית
-
מטרה: מגדיר את זמן ההמתנה לפני הניסיון הראשון. עליו להיות מספיק ארוך כדי למנוע ניסיונות חוזרים מיידיים אך מספיק קצר כדי למנוע השהיות מורגשות.
-
הגדרה מומלצת: התחל עם השהייה בין 500 מילישניות ל-1000 מילישניות. למערכות קריטיות, השתמש בהשהייה קצרה, בעוד פעולות פחות דחופות יכולות לקבל השהייה ארוכה יותר.
- מכפיל השהיה
-
מטרה: שולט בכמה מהר השהיה גדלה לאחר כל ניסיון מחדש. מכפיל של 2 מכפיל את השהיה (למשל, 1ש, 2ש, 4ש).
-
הגדרה מומלצת: בדרך כלל, מכפיל בין 1.5 ל2 מאזן בין תגובתיות ויציבות. מכפילים גבוהים יותר (למשל, 3) עשויים להיות מתאימים אם המערכת יכולה להתמודד עם השהיות ארוכות יותר בין ניסיונות מחדש.
- ניסיונות מחדש מקסימליים
-
מטרה: מגביל את ניסיונות החזרה כדי למנוע ניסיונות מופרזים שעשויים לנקז משאבים או להגדיל את העומס על המערכת.
-
הגדרה מומלצת: טווח של 3 עד 5 ניסיונות בדרך כלל מספיק לרוב היישומים. מעבר לכך, הפעולה עשויה להזדקק להירשם כהצלחה או לנהל אותה אחרת, כמו להודיע למשתמש או להפעיל התראה.
- זעזוע (רנדומליות)
-
מטרה: מוסיפה רנדומליות לכל עיכוב כדי למנוע את איחוד הניסיונות החוזרים ואת השפעת הצקת הצפרדע.
-
הגדרה מומלצת: הוספת עיכוב רנדומלי בין 0 ל-500 מילישניות לכל תקופת ניסיון חוזר. הזעזוע הזה עוזר לפרק את ניסיונות הניסיון באופן יותר אחיד במהלך הזמן.
מסקנה
על ידי שימוש ב-Exponential Backoff, אתה מוסיף עמידות ליישומך, מכין אותו לטיפול בבעיות בלתי צפויות. זה שינוי קטן עם השפעה גדולה, במיוחד כאשר היישום שלך גדל.
וזהו בשביל עכשיו חברים. אל תהססו להשאיר תגובה ולשאול שאלה אם יש לכם. לבריאות וליישומים אמינים ועמידים יותר.
קוד שמח! 👨💻❤️
Source:
https://timothy.hashnode.dev/implementing-exponential-backoff-for-reliable-systems