מבוא להיסטריקס

1. סקירה כללית

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

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

כמובן שקיימים פתרונות העוזרים להפוך יישומים לגמישים וסובלניים לתקלות - מסגרת כזו היא Hystrix.

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

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

2. דוגמה פשוטה

הדרך שבה Hystrix מספקת סובלנות לתקלות וחביון היא לבודד ולעטוף שיחות לשירותים מרוחקים.

בדוגמה פשוטה זו אנו עוטפים שיחה ב- לָרוּץ() שיטת ה- HystrixCommand:

class CommandHelloWorld מרחיב את HystrixCommand {שם מחרוזת פרטי; CommandHelloWorld (שם מחרוזת) {super (HystrixCommandGroupKey.Factory.asKey ("ExampleGroup")); this.name = שם; } @Override מוגן הפעלת מחרוזת () {להחזיר "שלום" + שם + "!"; }}

ואנחנו מבצעים את השיחה באופן הבא:

@ מבחן חלל ציבורי שניתןInputBobAndDefaultSettings_whenCommandExecuted_thenReturnHelloBob () {assertThat (CommandHelloWorld חדש ("בוב"). לבצע (), שווה ל- ("שלום בוב!")); }

3. הגדרת Maven

כדי להשתמש בהיסטריקס בפרויקטים של Maven, עלינו לעשות זאת hystrix-core ו ליבת rxjava תלות מנטפליקס בפרויקט pom.xml:

 com.netflix.hystrix hystrix-core 1.5.4 

הגרסה האחרונה תמיד נמצאת כאן.

 com.netflix.rxjava rxjava-core 0.20.7 

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

4. הגדרת שירות מרחוק

נתחיל בדמות דוגמה של עולם אמיתי.

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

מחלקה RemoteServiceTestSimulator {המתנה ארוכה פרטית; RemoteServiceTestSimulator (המתנה ארוכה) זורק את InterruptedException {this.wait = wait; } מחרוזת ביצוע () זורק InterruptedException {Thread.sleep (חכה); להחזיר "הצלחה"; }}

והנה הלקוח לדוגמא שלנו זה קורא RemoteServiceTestSimulator.

השיחה לשירות מבודדת ועטופה ב- לָרוּץ() שיטת א HystrixCommand. עטיפתו זו המספקת את החוסן שנגענו לעיל:

מחלקה RemoteServiceTestCommand מרחיב HystrixCommand {RemoteServiceTestSimulator remoteService פרטי; RemoteServiceTestCommand (Setter config, RemoteServiceTestSimulator remoteService) {super (config); this.remoteService = שירות מרחוק; } @Override מוגן מחרוזת לרוץ () זורק חריג {להחזיר remoteService.execute (); }}

השיחה מתבצעת על ידי קריאה ל- לבצע() שיטה על מופע של RemoteServiceTestCommand לְהִתְנַגֵד.

המבחן הבא מדגים כיצד זה נעשה:

@Test הציבור בטל givenSvcTimeoutOf100AndDefaultSettings_whenRemoteSvcExecuted_thenReturnSuccess () זורק InterruptedException {HystrixCommand.Setter config = HystrixCommand .Setter .withGroupKey (HystrixCommandGroupKey.Factory.as. שירות). assertThat (חדש RemoteServiceTestCommand (config, RemoteServiceTestSimulator חדש (100)). execute (), equalTo ("הצלחה")); }

עד כה ראינו כיצד לעטוף שיחות שירות מרחוק ב- HystrixCommand לְהִתְנַגֵד. בחלק שלהלן בואו נסתכל על איך להתמודד עם מצב שבו השירות המרוחק מתחיל להידרדר.

5. עבודה עם שירות מרחוק ותכנות הגנתי

5.1. תכנות הגנתי עם פסק זמן

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

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

@Test ציבורי בטל givenSvcTimeoutOf5000AndExecTimeoutOf10000_whenRemoteSvcExecuted_thenReturnSuccess () זורק InterruptedException {HystrixCommand.Setter config = HystrixCommand .Setter .withGroupKey (HystrixCommandGroupKey. HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter (); commandProperties.withExecutionTimeoutInMilliseconds (10_000); config.andCommandPropertiesDefaults (commandProperties); assertThat (חדש RemoteServiceTestCommand (config, RemoteServiceTestSimulator חדש (500)). execute (), equalTo ("הצלחה")); }

במבחן הנ"ל אנו מעכבים את תגובת השירות על ידי הגדרת פסק הזמן ל 500 אלפיות שנייה. אנו גם מגדירים את זמן הקצאת הזמן לביצוע HystrixCommand להיות 10,000 אלפיות שניים, ובכך לאפשר מספיק זמן לשירות מרחוק להגיב.

עכשיו בואו נראה מה קורה כאשר פסק הזמן לביצוע הוא פחות משיחת פסק הזמן לשירות:

@Test (צפוי = HystrixRuntimeException.class) חלל ציבורי givenSvcTimeoutOf15000AndExecTimeoutOf5000_whenRemoteSvcExecuted_thenExpectHre () זורק InterruptedException {HystrixCommand.Setter config = HystrixCommand .Setter. HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter (); commandProperties.withExecutionTimeoutInMilliseconds (5_000); config.andCommandPropertiesDefaults (commandProperties); RemoteServiceTestCommand חדש (config, RemoteServiceTestSimulator חדש (15_000)). execute (); }

שימו לב כיצד הורדנו את הסרגל והגדרנו את פסק הזמן לביצוע ל- 5,000 ms.

אנו מצפים שהשירות יגיב בתוך 5,000 אלפיות השנייה, ואילו הגדרנו את השירות להגיב לאחר 15,000 אלפיות שנייה. אם אתה מבחין בעת ​​ביצוע הבדיקה, הבדיקה תצא לאחר 5,000 אלפיות במקום לחכות ל -15,000 אלפיות השנייה ותזרוק HystrixRuntimeException.

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

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

5.2. תכנות הגנתי עם מאגר חוטים מוגבל

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

כאשר שירות מרוחק מתחיל להגיב לאט, יישום טיפוסי ימשיך להתקשר לשירות מרחוק זה.

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

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

בואו נראה כיצד להגדיר את גודל מאגר החוטים ב HystrixCommand:

@Test ציבורי בטל givenSvcTimeoutOf500AndExecTimeoutOf10000AndThreadPool_whenRemoteSvcExecuted _thenReturnSuccess () זורק InterruptedException {HystrixCommand.Setter config = HystrixCommand .Setter .withGroupKey (HystrixCommand). HystrixCommandProperties.Setter commandProperties = HystrixCommandProperties.Setter (); commandProperties.withExecutionTimeoutInMilliseconds (10_000); config.andCommandPropertiesDefaults (commandProperties); config.andThreadPoolPropertiesDefaults (HystrixThreadPoolProperties.Setter () .withMaxQueueSize (10) .withCoreSize (3) .withQueueSizeRejectionThreshold (10)); assertThat (חדש RemoteServiceTestCommand (config, RemoteServiceTestSimulator חדש (500)). execute (), equalTo ("הצלחה")); }

במבחן הנ"ל אנו קובעים את גודל התור המרבי, גודל התור הליבה וגודל דחיית התור. היסטריקס יתחיל לדחות את הבקשות כאשר מספר השרשורים המרבי הגיע ל -10 ותור המשימות הגיע לגודל 10.

גודל הליבה הוא מספר החוטים שתמיד נשארים בחיים במאגר החוטים.

5.3. תכנות הגנתי עם תבנית מפסק קצר

עם זאת, עדיין קיים שיפור שנוכל לבצע בשיחות שירות מרחוק.

הבה נבחן את המקרה שהשירות המרוחק החל להיכשל.

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

בואו נראה כיצד Hystrix מיישם דפוס זה:

@Test ציבורי בטל givenCircuitBreakerSetup_whenRemoteSvcCmdExecuted_thenReturnSuccess () זורק InterruptedException {HystrixCommand.Setter config = HystrixCommand .Setter .withGroupKey (HystrixCommandGroupKey.Factory.asKeyoups) ("RemoteService =") מאפייני HystrixCommandProperties.Setter = HystrixCommandProperties.Setter (); properties.withExecutionTimeoutInMilliseconds (1000); properties.withCircuitBreakerSleepWindowInMilliseconds (4000); properties.withExecutionIsolationStrategy (HystrixCommandProperties.ExecutionIsolationStrategy.THREAD); properties.withCircuitBreakerEnabled (נכון); properties.withCircuitBreakerRequestVolumeThreshold (1); config.andCommandPropertiesDefaults (מאפיינים); config.andThreadPoolPropertiesDefaults (HystrixThreadPoolProperties.Setter () .withMaxQueueSize (1) .withCoreSize (1) .withQueueSizeRejectionThreshold (1)); assertThat (this.invokeRemoteService (config, 10_000), equalTo (null)); assertThat (this.invokeRemoteService (config, 10_000), equalTo (null)); assertThat (this.invokeRemoteService (config, 10_000), equalTo (null)); Thread.sleep (5000); assertThat (חדש RemoteServiceTestCommand (config, RemoteServiceTestSimulator חדש (500)). execute (), equalTo ("הצלחה")); assertThat (חדש RemoteServiceTestCommand (config, RemoteServiceTestSimulator חדש (500)). execute (), equalTo ("הצלחה")); assertThat (RemoteServiceTestCommand החדש (config, RemoteServiceTestSimulator חדש (500)). execute (), equalTo ("הצלחה")); }
מחרוזת ציבורית invokeRemoteService (HystrixCommand.Setter config, פסק זמן int) זורק InterruptedException {String response = null; נסה {respons = RemoteServiceTestCommand חדש (config, RemoteServiceTestSimulator חדש (פסק זמן)). execute (); } לתפוס (HystrixRuntimeException ex) {System.out.println ("ex =" + ex); } תשובת תשובה; }

בבדיקה שלעיל קבענו מאפייני מפסק שונים. החשובים שבהם הם:

  • ה CircuitBreakerSleepWindow אשר מוגדר ל -4,000 ms. זה מגדיר את חלון המפסק ומגדיר את מרווח הזמן שאחריו תחדש הבקשה לשירות המרוחק
  • ה CircuitBreakerRequestVolumeThreshold אשר מוגדר ל- 1 ומגדיר את המספר המינימלי של בקשות הדרושות לפני שנשקול שיעור הכישלונות

עם ההגדרות לעיל במקום, שלנו HystrixCommand כעת יפתח המסע לאחר שתי בקשות כושלות. הבקשה השלישית אפילו לא תפגע בשירות המרוחק למרות שהגדרנו את עיכוב השירות ל- 500 ms. היסטריקס יקצר וקצרה והשיטה שלנו תחזור ריק כתגובה.

בהמשך נוסיף א Thread.sleep (5000) על מנת לחצות את גבול חלון השינה שקבענו. זה יגרום היסטריקס כדי לסגור את המעגל והבקשות הבאות יזרמו בהצלחה.

6. סיכום

לסיכום Hystrix נועד:

  1. ספק הגנה ושליטה על כשלים ואיחור משירותים שבדרך כלל ניגשים אליהם ברשת
  2. הפסק את דריסת התקלות כתוצאה מכך שחלק מהשירותים לא היו
  3. נכשל במהירות ומתאושש במהירות
  4. דרדר בחן במידת האפשר
  5. ניטור והתראה בזמן אמת של מרכז הפיקוד על כשלים

בפוסט הבא נראה כיצד לשלב בין היתרונות של Hystrix למסגרת Spring.

את קוד הפרויקט המלא ואת כל הדוגמאות ניתן למצוא בפרויקט github.