הגדרת ניסיונות חוזרים בצד הלקוח

ספריות הלקוח של Cloud ל-Java משתמשות בניסיונות חוזרים כדי לטפל בכשלים בלתי צפויים וחולפים (כלומר, השרת לא זמין באופן זמני). יכול להיות שיידרשו כמה ניסיונות כדי לקבל תגובה מהשרת.

ערכי ברירת המחדל לניסיון חוזר נבחרים על ידי הצוות שמפעיל את שירות הענן. ערכי הניסיון החוזר האלה מוגדרים לכל RPC. שירות יכול לבחור להפעיל ניסיונות חוזרים רק עבור קבוצת משנה של RPCs. יכול להיות שכל RPC של שירות מוגדר באופן שונה.

פרמטרים של ניסיון חוזר

יש שני סוגים של פרמטרים לניסיון חוזר שאפשר להגדיר בספריות לקוח:

  1. קוד סטטוס של ניסיון חוזר: קבוצת קודי סטטוס לניסיון חוזר.
  2. זמן קצוב לתפוגה של ניסיון חוזר או גבולות של ניסיון: אפשר להגדיר RetrySettings כדי להגדיר את הגבולות.

מיקום ברירת המחדל של הגדרות הניסיון החוזר של RPC

הגדרות ברירת המחדל של הניסיונות החוזרים מוגדרות בקובץ {Client}StubSettings שנוצר. בדוגמה הבאה, שמתבססת על קריאה לשירות מרוחק (RPC) מסוג ExportAssets ב-Java-Asset v3.64.0, מוגדרות הגדרות ברירת המחדל לניסיון חוזר:

  • קודי סטטוס של ניסיון חוזר: מוגדרים בקובץ AssetServiceStubSettings.java. דוגמה:

    ImmutableMap.Builder<String, ImmutableSet<StatusCode.Code>> definitions = ImmutableMap.builder();
    definitions.put("no_retry_0_codes", ImmutableSet.copyOf(Lists.<StatusCode.Code>newArrayList()));
    // ... More StatusCode configurations
    RETRYABLE_CODE_DEFINITIONS = definitions.build();
    
  • פרמטרים של ניסיון חוזר: מוגדרים בקובץ AssetServiceStubSettings.java. דוגמה:

    ImmutableMap.Builder<String, RetrySettings> definitions = ImmutableMap.builder();
    RetrySettings settings = null;
    settings =
        RetrySettings.newBuilder()
        .setInitialRpcTimeoutDuration(Duration.ofMillis(60000L))
        .setRpcTimeoutMultiplier(1.0)
        .setMaxRpcTimeoutDuration(Duration.ofMillis(60000L))
        .setTotalTimeoutDuration(Duration.ofMillis(60000L))
        .build();
    definitions.put("no_retry_0_params", settings);
    // ... More RetrySettings configurations
    RETRY_PARAM_DEFINITIONS = definitions.build();
    

שתי ההגדרות ממופות ל-RPC בקובץ AssetServiceStubSettings.java. דוגמה:

builder
  .exportAssetsSettings()
  .setRetryableCodes(RETRYABLE_CODE_DEFINITIONS.get("no_retry_0_codes"))
  .setRetrySettings(RETRY_PARAM_DEFINITIONS.get("no_retry_0_params"));

מושגים שקשורים לניסיונות חוזרים בספריות לקוח

הפעלת ניסיונות חוזרים מאפשרת ל-RPC לנסות כמה פעמים לבצע קריאה מוצלחת. קריאה מוצלחת היא תגובה משרת שמחזירה קוד סטטוס OK (מ-gRPC) או קוד סטטוס 2xx (מ-HttpJson).

ניסיון לעומת פעולה

ההגדרה הבאה של RetrySettings משנה את הגדרות הניסיון החוזר גם עבור ניסיון של RPC וגם עבור פעולה:

settings =
  RetrySettings.newBuilder()
      .setInitialRetryDelayDuration(Duration.ofMillis(100L))
      .setRetryDelayMultiplier(1.3)
      .setMaxRetryDelayDuration(Duration.ofMillis(60000L))
      .setInitialRpcTimeoutDuration(Duration.ofMillis(60000L))
      .setRpcTimeoutMultiplier(1.0)
      .setMaxRpcTimeoutDuration(Duration.ofMillis(60000L))
      .setTotalTimeoutDuration(Duration.ofMillis(60000L))
      .build();

ניסיון RPC הוא הניסיון הבודד שבוצע, ופעולת RPC היא אוסף של כל הניסיונות שבוצעו. קריאה יחידה ל-RPC תכלול ניסיון אחד או יותר בפעולה יחידה.

ההגדרות הבאות קובעות את הגבולות של RPC (ניסיון):

ההגדרות הבאות קובעות את הגבולות הכוללים של RPC (פעולה):

כשמנסים שוב לבצע RPC

מערכת Google תנסה שוב לבצע RPC אם מתרחש אחד משני התרחישים הבאים:

  • הספרייה מקבלת קוד סטטוס לא מוצלח וקוד הסטטוס מסומן כאפשרות לניסיון חוזר.
  • קריאה ל-RPC חורגת מהגבולות של ה-RPC הספציפי, אבל עדיין נמצאת בתוך הגבולות הכוללים של ה-RPC.

אם רק אחד מהתרחישים נכון, או אם אף אחד מהתרחישים לא נכון, לא יתבצע ניסיון חוזר של ה-RPC.

לדוגמה, אם לא חלף הזמן הכולל להמתנה, אבל הניסיון האחרון מקבל קוד סטטוס שלא ניתן לנסות שוב.

בנוסף, כשמגדירים את הגבולות של ה-RPC, אפשר להגדיר את הגבולות של כל ניסיון וגם את הגבולות הכוללים של ה-RPC. אלגוריתם הניסיון החוזר יבטיח שהגבולות של ניסיון בודד יהיו בתוך הגבולות של ה-RPC הכולל.

השהיה מעריכית לפני ניסיון חוזר (exponential backoff)

השהיה מעריכית לפני ניסיון חוזר (exponential backoff) תנסה שוב לשלוח בקשות עם עיכוב הולך וגדל בין כל ניסיון חוזר. אפשר להגביל את ערך ההשהיה של הניסיון החוזר באמצעות ערך מקסימלי של השהיה של הניסיון החוזר.

לדוגמה, הגדרות הניסיון החוזר הבאות יכולות לגרום לזמני השהיה הבאים:

Initial Retry Delay: 100ms
Retry Delay Multiplier: 2.0
Max Retry Delay: 500ms
  • ניסיון 1: השהיה של 100 אלפיות השנייה
  • ניסיון שני: השהיה של 200 אלפיות השנייה
  • ניסיון 3: השהיה של 400 אלפיות השנייה
  • ניסיון 4: השהיה של 500 אלפיות השנייה
  • ...
  • ניסיון X: השהיה של 500 אלפיות השנייה

רעידות

הוספת שונות אקראית (Jitter) היא שיטה להפצת הקריאות ל-RPC על פני זמן. Google Cloud ספריות הלקוח תמיד מפעילות שונות אקראית לניסיונות חוזרים. כך אפשר לפזר את הניסיונות החוזרים בלי להעמיס על השרת.

הערך האקראי של הג'יטר מחושב על סמך עיכוב הניסיון החוזר. לפני כל ניסיון, אלגוריתם הניסיון החוזר יחשב ערך אקראי בין [1, RETRY_DELAY]. הערך המחושב הזה הוא העיכוב המשוער לפני שהבקשה נשלחת לשרת.

ההגדרות הבאות לניסיון חוזר משתמשות ברעידות ובהשהיה מעריכית לפני ניסיון חוזר.

Initial Retry Delay: 100ms
Retry Delay Multiplier: 2.0
Max Retry Delay: 500ms

העיכובים האפשריים:

  • ניסיון 1: השהיה של ערך אקראי בין [1, 100] אלפיות השנייה
  • ניסיון שני: השהיה של ערך אקראי בין [1, 200] אלפיות שנייה
  • ניסיון 3: השהיה של ערך אקראי בין [1, 400] אלפיות השנייה
  • ניסיון 4: השהיה של ערך אקראי בין [1, 500]ms
  • ...
  • ניסיון X: השהיה של ערך אקראי בין [1, 500] אלפיות השנייה

דוגמאות לניסיון חוזר

בדוגמאות הבאות מוצגת ההתנהגות של כמה הגדרות של ניסיונות חוזרים.

ללא ניסיון חוזר

בדוגמה הזו מושבתים הניסיונות החוזרים.

RetrySettings defaultNoRetrySettings =
    RetrySettings.newBuilder()
    // Use the default configurations for other settings
    .setTotalTimeoutDuration(Duration.ofMillis(5000L))
    // Explicitly set retries as disabled (maxAttempts == 1)
    .setMaxAttempts(1)
    .build();

לחלופין, אפשר להגדיר את ההתנהגות הזו באמצעות הדוגמה הזו:

RetrySettings defaultNoRetrySettings =
    RetrySettings.newBuilder()
    .setLogicalTimeoutDuration(Duration.ofMillis(5000L))
    .build();

בטבלה הבאה מוצגים הניסיונות:

מספר הניסיון הזמן הקצוב לתפוגת RPC השהיית ניסיון חוזר התקשרות לשיחה השיחה הסתיימה
1 ‫5,000 אלפיות השנייה 0 אלפיות שנייה 0 אלפיות שנייה ‫5,000 אלפיות השנייה

דוגמה לניסיון חוזר

בדוגמה הזו מופעלים ניסיונות חוזרים עם השהיות וזמני קצובים לתפוגה שצוינו.

RetrySettings.newBuilder()
    .setInitialRetryDelayDuration(Duration.ofMillis(200L))
    .setRetryDelayMultiplier(2.0)
    .setMaxRetryDelayDuration(Duration.ofMillis(500L))
    .setInitialRpcTimeoutDuration(Duration.ofMillis(1500L))
    .setRpcTimeoutMultiplier(2.0)
    .setMaxRpcTimeoutDuration(Duration.ofMillis(3000L))
    .setTotalTimeoutDuration(Duration.ofMillis(5000L))
    .build();

בטבלה הבאה מוצגים הניסיונות:

מספר הניסיון הזמן הקצוב לתפוגת RPC השהיית ניסיון חוזר התקשרות לשיחה השיחה הסתיימה
1 ‫1,500 אלפיות השנייה 0 אלפיות שנייה 0 אלפיות שנייה ‫1,500 אלפיות השנייה
‫2 (ניסיון חוזר) ‫3,000 אלפיות השנייה ‫200 אלפיות השנייה ‫1,700 אלפיות השנייה ‫4,700 אלפיות השנייה
‫3 (לא בוצע ניסיון חוזר) - ‫400 אלפיות השנייה - -

דוגמה לניסיון חוזר: זמן כולל ארוך יותר לתפוגה

הדוגמה הזו דומה לדוגמה הראשונה של ניסיון חוזר, אבל יש בה זמן קצוב לתפוגה כולל ארוך יותר כדי להציג ניסיון חוזר נוסף ואת הזמן הקצוב לתפוגה המקסימלי של RPC בניסיון החוזר האחרון.

RetrySettings.newBuilder()
    .setInitialRetryDelayDuration(Duration.ofMillis(200L))
    .setRetryDelayMultiplier(2.0)
    .setMaxRetryDelayDuration(Duration.ofMillis(500L))
    .setInitialRpcTimeoutDuration(Duration.ofMillis(1500L))
    .setRpcTimeoutMultiplier(2.0)
    .setMaxRpcTimeoutDuration(Duration.ofMillis(3000L))
    .setTotalTimeoutDuration(Duration.ofMillis(10000L))
    .build();

בטבלה הבאה מוצגים הניסיונות:

מספר הניסיון הזמן הקצוב לתפוגת RPC השהיית ניסיון חוזר התקשרות לשיחה השיחה הסתיימה
1 ‫1,500 אלפיות השנייה 0 אלפיות שנייה 0 אלפיות שנייה ‫1,500 אלפיות השנייה
‫2 (ניסיון חוזר) ‫3,000 אלפיות השנייה ‫200 אלפיות השנייה ‫1,700 אלפיות השנייה ‫4,700 אלפיות השנייה
‫3 (Retry) ‫4,900 אלפיות השנייה ‫400 אלפיות השנייה ‫5,100 אלפיות השנייה ‫10,000 אלפיות השנייה

הערך של הזמן הקצוב לתפוגה של ה-RPC של הניסיון החוזר השלישי מוגבל בגלל הערך של הזמן הכולל הקצוב לתפוגה. השימוש במכפיל (2.0) עם ערך הזמן הקצוב לתפוגה הקודם (3,000 אלפיות שנייה) מוביל לזמן קצוב לתפוגה של RPC של 6,000 אלפיות שנייה. עם זאת, הזמן הקצוב לתפוגה של ה-RPC לא יכול להיות ארוך מהזמן הכולל לתפוגה, והוא יצטמצם ל'זמן שנותר' (10,000 פחות 5,100 שווה ל-4,900).

דוגמה לניסיון חוזר: זמן קצוב לתפוגת RPC עם הגבלה

RetrySettings defaultRetrySettings =
    RetrySettings.newBuilder()
        .setInitialRetryDelayDuration(Duration.ofMillis(200L))
        .setRetryDelayMultiplier(2.0)
        .setMaxRetryDelayDuration(Duration.ofMillis(500L))
        .setInitialRpcTimeoutDuration(Duration.ofMillis(500L))
        .setRpcTimeoutMultiplier(2.0)
        .setMaxRpcTimeoutDuration(Duration.ofMillis(2000L))
        .setTotalTimeoutDuration(Duration.ofMillis(4000L))
        .build();

בטבלה הבאה מוצגים הניסיונות:

מספר הניסיון הזמן הקצוב לתפוגת RPC השהיית ניסיון חוזר התקשרות לשיחה השיחה הסתיימה
1 ‫500 אלפיות השנייה 0 אלפיות שנייה 0 אלפיות שנייה ‫500 אלפיות השנייה
‫2 (ניסיון חוזר) ‫1,000 אלפיות השנייה ‫200 אלפיות השנייה ‫700 אלפיות השנייה ‫1,700 אלפיות השנייה
‫3 (Retry) ‫1,900 אלפיות השנייה ‫400 אלפיות השנייה ‫2,100 אלפיות השנייה ‫4,000 אלפיות השנייה

דוגמה נוספת שבה הזמן הקצוב לתפוגה של RPC מוגבל כדי שלא יעלה על הזמן הקצוב לתפוגה הכולל.

איך מגדירים פרמטרים מותאמים אישית לניסיון חוזר של RPC

בדוגמה הבאה נעשה שימוש בספריית הלקוח Java-Asset:

  1. יוצרים את הכיתה RetrySettings עם ההגדרות המותאמות אישית:

    RetrySettings customRetrySettings =
      RetrySettings.newBuilder()
        // ... Retry Configurations
        .build();
    RetrySettings customRetrySettings2 =
      RetrySettings.newBuilder()
        // ... Retry Configurations
        .build();
    
  2. יוצרים את StubSettings.Builder עבור הלקוח ומגדירים אותו עבור ה-RPC:

    AssetServiceStubSettings.Builder assetStubSettingsBuilder = AssetServiceStubSettings.newBuilder();
    assetStubSettingsBuilder
      .exportAssetsSettings()
      // Set your custom Retry Settings
      .setRetrySettings(customRetrySettings)
      // Set your custom Retryable Codes
      .setRetryableCodes(ImmutableSet.of(StatusCode.Code.DEADLINE_EXCEEDED));
    

    קטע הקוד שסופק מגדיר הגדרות מותאמות אישית לניסיון חוזר עבור AssetServiceClient של ExportAssets RPC. הוא מגדיר את ExportAssets RPC כך שישתמש בהגדרות הניסיון החוזר שהוגדרו ב-customRetrySettings, ומגדיר את הקודים שאפשר לנסות שוב כ-DEADLINE_EXCEEDED.

  3. יוצרים את ההגדרות של הלקוח כ-assetSettings:

    AssetServiceSettings assetSettings = AssetServiceSettings.create(assetStubSettingsBuilder.build());
    
  4. יוצרים את הלקוח עם ההגדרות assetClient.

    try (AssetServiceClient assetClient = AssetServiceClient.create(assetSettings)) {
      ...
    }
    

חוזרים על שלב 2 לכל RPC שרוצים להגדיר. לדוגמה:

AssetServiceStubSettings.Builder assetStubSettingsBuilder = AssetServiceStubSettings.newBuilder();
  
// Modify the retry params for ExportAssets RPC
assetStubSettingsBuilder
  .exportAssetsSettings()
  .setRetrySettings(customRetrySettings)
  .setRetryableCodes(ImmutableSet.of(StatusCode.Code.DEADLINE_EXCEEDED));

// Modify the retry params for ListAssets RPC
assetStubSettingsBuilder
  .listAssetsSettings()
  .setRetrySettings(customRetrySettings2)
  .setRetryableCodes(ImmutableSet.of(StatusCode.Code.UNAVAILABLE));

שאלות נפוצות

ריכזנו כאן כמה שאלות נפוצות לגבי התנהגות של ניסיונות חוזרים של לקוחות.

ציפיתי ל-X ניסיונות חוזרים, אבל המערכת ניסתה Y פעמים.

אלא אם מציינים במפורש את מספר הניסיונות המקסימליים (בנוסף להשבתת הגדרות הזמן הקצוב לתפוגה), יכול להיות שלא תראו באופן עקבי את אותו מספר של ניסיונות חוזרים. הוספת ערכים אקראיים של תנודות לעיכוב ב-RPC מקשה על חיזוי המועד שבו הבקשה נשלחת בפועל.

ה-RPC החזיר שגיאת כשל לפני שהגיע לערך הזמן הקצוב לתפוגה הכולל.

אלגוריתם הניסיון החוזר יחשב את ערך השהיית הניסיון החוזר עם רעש אקראי במהלך כל ניסיון חוזר. ההשהיה המחושבת בין הניסיונות תתוזמן להפעלה בעתיד (כלומר, currentTime() + jitteredRetryDelay). אם זמן הניסיון המתוזמן חורג מהזמן הכולל להמתנה, לא יתבצע ניסיון חוזר אחרון.

הגדרתי הגדרות בהתאמה אישית ואני רואה בעיות שקשורות למכסת השימוש.

יכול להיות שהגדרתם את RetrySettings לפעול בצורה אגרסיבית מדי. ערכי ברירת המחדל של הניסיון החוזר נבחרים על ידי הצוות שמפעיל את השירות.

כדאי להגדיל את ההשהיה של הניסיון החוזר (ההשהיה הראשונית של הניסיון החוזר והמכפיל של הניסיון החוזר) כדי שהניסיונות החוזרים יהיו מרווחים יותר ובתדירות נמוכה יותר. לתשומת ליבך, התגובה שלנו עשויה להתעכב.

יכול להיות שתרצו לקבל תשובה מהירה יותר או לנסות שוב בתדירות גבוהה יותר, או גם וגם. במקרה כזה, כדאי לנסות להגדיל את מכסת השימוש.