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

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

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

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

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

  1. קוד סטטוס של ניסיון חוזר: קבוצת קודי סטטוס לניסיון חוזר.
  2. Retry Timeout or Attempt Bounds: הגדרת 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 אלפיות השנייה
  • ניסיון 2: השהיה של 200 אלפיות השנייה
  • ניסיון 3: השהיה של 400 אלפיות השנייה
  • ניסיון 4: השהיה של 500 אלפיות השנייה
  • ...
  • ניסיון X: השהיה של 500 אלפיות השנייה

ג'יטר

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

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

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

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

אלה זמני העיכוב האפשריים:

  • ניסיון 1: השהיה של ערך אקראי בין [1, 100] אלפיות השנייה
  • ניסיון 2: השהיה של ערך אקראי בין [1, 200] אלפיות השנייה
  • ניסיון 3: השהיה של ערך אקראי בין [1, 400] אלפיות השנייה
  • ניסיון 4: השהיה של ערך אקראי בין [1, 500] אלפיות השנייה
  • ...
  • ניסיון 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 (ניסיון חוזר) ‫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 (ניסיון חוזר) ‫1900 אלפיות השנייה ‫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 RPC של ExportAssets. הוא מגדיר את 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 לפעול בצורה אגרסיבית מדי. ערכי ברירת המחדל של הניסיון החוזר נבחרים על ידי הצוות שמפעיל את השירות.

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

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