מבוא ל- MBassador

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

במילים פשוטות, MBassador כן אוטובוס אירועים בעל ביצועים גבוהים המשתמש בסמנטיקה לפרסום-מנוי.

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

2. תלות של Maven

לפני שנוכל להשתמש בספרייה, עלינו להוסיף את תלות השגריר:

 שגריר net.engio 1.3.1 

3. טיפול אירועים בסיסי

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

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

משגר MBassador פרטי = MBassador חדש (); הודעת מחרוזת פרטית מחרוזת; @ לפני טרום פומבי preparTests () {dispatcher.subscribe (זה); } @Test ציבורי בטל כאשר StringDispatched_thenHandleString () {dispatcher.post ("TestString"). Now (); assertNotNull (messageString); assertEquals ("TestString", messageString); } @Handler חלל ציבורי handleString (הודעת מחרוזת) {messageString = הודעה; } 

בחלק העליון של שיעור המבחנים הזה אנו רואים יצירת א MBassador עם קונסטרוקטור ברירת המחדל שלו. הבא, ב @לפני שיטה, אנו קוראים הירשם () ולהעביר בהתייחסות לכיתה עצמה.

ב הירשם כמנוי (), השולח בודק את המנוי עבור @Handler ביאורים.

ובמבחן הראשון אנו מתקשרים dispatcher.post (...) .now () להעביר את ההודעה - מה שמביא handleString () נקרא.

מבחן ראשוני זה מדגים כמה מושגים חשובים. כל לְהִתְנַגֵד יכול להיות מנוי, כל עוד יש לו שיטה אחת או יותר עם הערות @Handler. מנוי יכול לכלול מספר מטפלים.

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

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

דומה ל הירשם () שיטה, שיטת ההודעה מקבלת כל לְהִתְנַגֵד. זֶה לְהִתְנַגֵד מועבר למנויים.

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

בואו נוסיף עוד מטפל בהודעות ונשלח סוג הודעה אחר:

הודעה שלמה פרטית שלם שלם; @ מבחן ציבורי בטל כאשרIntegerDispatched_thenHandleInteger () {dispatcher.post (42) .now (); assertNull (messageString); assertNotNull (messageInteger); assertTrue (42 == messageInteger); } @Handler חלל ציבורי handleInteger (הודעה שלמה) {messageInteger = הודעה; } 

כצפוי, כשאנחנו שולחיםan מספר שלם, handleInteger () נקרא, ו handleString () לא. ניתן להשתמש בשולח יחיד לשליחת יותר מסוג הודעה אחד.

3.2. מסרים מתים

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

אובייקט פרטי deadEvent; @ מבחן ציבורי בטל כאשר LongDispatched_thenDeadEvent () {dispatcher.post (42L) .now (); assertNull (messageString); assertNull (messageInteger); assertNotNull (deadEvent); assertTrue (מקרה אירוע מת של Long); assertTrue (42L == (Long) DeadEvent); } @Handler הריק פומבי handleDeadEvent (הודעת DeadMessage) {deadEvent = message.getMessage (); } 

במבחן זה אנו שולחים א ארוך במקום מספר שלם. לא זה ולא זה handleInteger () ולא handleString () נקראים, אבל handleDeadEvent () הוא.

כשאין מטפלים בהודעה, היא נעטפת ב DeadMessage לְהִתְנַגֵד. מאז הוספנו מטפל עבור Deadmessage, אנחנו לוכדים את זה.

DeadMessage ניתן להתעלם בבטחה; אם יישום לא צריך לעקוב אחר הודעות מתות, ניתן לאפשר להם ללכת לשום מקום.

4. שימוש בהיררכיית אירועים

שְׁלִיחָה חוּט ו מספר שלם אירועים מגבילים. בואו ניצור כמה שיעורי מסרים:

הודעה בכיתה ציבורית {} מחלקה ציבורית AckMessage מרחיב הודעה {} מחלקה ציבורית RejectMessage מרחיב הודעה {קוד קוד; // סטרים וגטררים}

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

4.1. שליחת כיתת בסיס הוֹדָעָה

נתחיל עם הוֹדָעָה אירועים:

משגר MBassador פרטי = MBassador חדש (); הודעת הודעה פרטית; פרטי AckMessage ackMessage; פרטי RejectMessage rejectMessage; @ לפני הריקון הציבורי preparTests () {dispatcher.subscribe (this); } @Test הציבור בטל כאשר MessageDispatched_thenMessageHandled () {dispatcher.post (הודעה חדשה ()). עכשיו (); assertNotNull (הודעה); assertNull (ackMessage); assertNull (rejectMessage); } @Handler public void handleMessage (הודעת הודעה) {this.message = הודעה; } @Handler public void handleRejectMessage (הודעת RejectMessage) {rejectMessage = הודעה; } @Handler ציבור ריק בטל handleAckMessage (הודעת AckMessage) {ackMessage = הודעה; }

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

כשאנחנו שולחים א הוֹדָעָה, handleMessage () מקבל את זה. שני המטפלים האחרים לא.

4.2. שליחת הודעת מחלקה משנה

בוא נשלח a דחה הודעה:

@Test הציבור בטל כאשרRejectDispatched_thenMessageAndRejectHandled () {dispatcher.post (RejectMessage חדש ()). עכשיו (); assertNotNull (הודעה); assertNotNull (rejectMessage); assertNull (ackMessage); }

כשאנחנו שולחים א דחה הודעה שניהם handleRejectMessage () ו handleMessage () לקבל את זה.

מאז דחה הודעה מרחיב הוֹדָעָה, ה הוֹדָעָה המטפל קיבל אותו, בנוסף ל- רejectMessage מטפל.

בואו נאמת התנהגות זו באמצעות AckMessage:

@Test ציבורי בטל כאשרAckDispatched_thenMessageAndAckHandled () {dispatcher.post (AckMessage חדש ()). עכשיו (); assertNotNull (הודעה); assertNotNull (ackMessage); assertNull (rejectMessage); }

בדיוק כפי שציפינו, כאשר אנו שולחים הודעה AckMessage, שניהם handleAckMessage () ו handleMessage () לקבל את זה.

5. סינון הודעות

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

5.1. סינון בכיתה ותת-מחלקה

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

אנו יכולים לפתור סוגיה זו של היררכיה על ידי ביצוע הוֹדָעָה מופשט ויצירת מעמד כגון GenericMessage. אבל מה אם אין לנו את המותרות האלה?

אנו יכולים להשתמש במסנני הודעות:

הודעה פרטית baseMessage; הודעה פרטית של הודעה; @ מבחן פומבי בטל כאשר MessageDispatched_thenMessageFiltered () {dispatcher.post (הודעה חדשה ()). עכשיו (); assertNotNull (baseMessage); assertNull (subMessage); } @ מבחן ציבורי בטל כאשר RejectDispatched_thenRejectFiltered () {dispatcher.post (RejectMessage חדש ()). עכשיו (); assertNotNull (subMessage); assertNull (baseMessage); } @Handler (filters = {@Filter (Filters.RejectSubtypes.class)}) חלל ציבורי handleBaseMessage (הודעת הודעה) {this.baseMessage = הודעה; } @Handler (filters = {@Filter (Filters.SubtypesOnly.class)}) חלל ריק handSubMessage (הודעת הודעה) {this.subMessage = הודעה; }

ה פילטרים פרמטר עבור @Handler ההערה מקבלת א מעמד שמיישם IMessageFilter. הספרייה מציעה שתי דוגמאות:

ה Filters.RejectSubtypes עושה כשמו כן הוא: הוא יסנן כל תת-סוגים. במקרה זה אנו רואים זאת דחה הודעה אינו מטופל על ידי handleBaseMessage ().

ה מסננים. סוגים בלבד עושה גם כשמו כן הוא: הוא יסנן את כל סוגי הבסיסים. במקרה זה אנו רואים זאת הוֹדָעָה אינו מטופל על ידי handleSubMessage ().

5.2. IMessageFilter

ה Filters.RejectSubtypes וה מסננים. סוגים בלבד שניהם מיישמים IMessageFilter.

RejectSubTypes משווה את מחלקת ההודעה לסוגי ההודעות שהוגדרו ותאפשר רק דרך הודעות השוות לאחד מסוגיה, בניגוד לכל מחלקות המשנה.

5.3. סינון עם תנאים

למרבה המזל, יש דרך קלה יותר לסינון הודעות. MBassador תומך בקבוצת משנה של ביטויי Java EL כתנאים לסינון הודעות.

בואו נסנן א חוּט הודעה על סמך אורכה:

test test String מחרוזת; @ מבחן ציבורי בטל כאשר LongStringDispatched_thenStringFiltered () {dispatcher.post ("foobar!"). Now (); assertNull (testString); } @Handler (condition = "msg.length () <7") handleStringMessage (הודעת מחרוזת) public public ריק {this.testString = הודעה; }

ה"פובאר! " אורכו של שבעה תווים ומסונן. בואו נשלח קצר יותר חוּט:

 @ מבחן ציבורי בטל כאשרShortStringDispatched_thenStringHandled () {dispatcher.post ("foobar"). Now (); assertNotNull (testString); }

כעת, "הפובאר" הוא בן שש דמויות בלבד והוא מועבר דרכו.

שֶׁלָנוּ דחה הודעה מכיל שדה עם אביזר. בואו נכתוב מסנן לכך:

פרטי RejectMessage rejectMessage; @Test הציבור בטל כאשר WrongRejectDispatched_thenRejectFiltered () {RejectMessage testReject = RejectMessage חדש (); testReject.setCode (-1); dispatcher.post (testReject) .now (); assertNull (rejectMessage); assertNotNull (SubMessage); assertEquals (-1, ((RejectMessage) subMessage) .getCode ()); } @Handler (condition = "msg.getCode ()! = -1") handle public HandRejectMessage (RejectMessage rejectMessage) {this.rejectMessage = rejectMessage; }

שוב, אנו יכולים לשאול שיטה על אובייקט או לסנן את ההודעה או לא.

5.4. צלם הודעות מסוננות

דומה ל DeadEvents, אולי נרצה ללכוד ולעבד הודעות מסוננות. ישנו מנגנון ייעודי ללכידת אירועים מסוננים. התייחסות לאירועים מסוננים שונה מאירועים "מתים".

בואו לכתוב מבחן שממחיש זאת:

test test String מחרוזת; פרטי FilteredMessage filteredMessage; פרטי DeadMessage deadMessage; @ מבחן ציבורי בטל כאשר LongStringDispatched_thenStringFiltered () {dispatcher.post ("foobar!"). Now (); assertNull (testString); assertNotNull (filteredMessage); assertTrue (filteredMessage.getMessage () מופע מחרוזת); assertNull (deadMessage); } @Handler (תנאי = "msg.length () <7") handledringStringMessage (הודעת מחרוזת) ציבורי ריק {this.testString = הודעה; } @Handler public void handleFilterMessage (הודעת FilteredMessage) {this.filteredMessage = הודעה; } @Handler public void handleDeadMessage (DeadMessage deadMessage) {this.deadMessage = deadMessage; } 

בתוספת א FilteredMessage מטפל, אנחנו יכולים לעקוב מיתרים שמסוננים בגלל אורכם. ה filterMessage מכיל שלנו ארוך מדי חוּט בזמן deadMessage שְׂרִידִים ריק.

6. משלוח וטיפול בהודעות אסינכרוניות

עד כה כל הדוגמאות שלנו השתמשו בהעברת הודעות סינכרוניות; כשהתקשרנו post.now () ההודעות נמסרו לכל מטפל באותו שרשור אליו התקשרנו הודעה() מ.

6.1. משלוח אסינכרוני

ה MBassador.post () מחזירה פקודת SyncAsyncPostCommand. שיעור זה מציע מספר שיטות, כולל:

  • עַכשָׁיו() - העבר הודעות באופן סינכרוני; השיחה תחסום עד למסירת כל ההודעות
  • באופן אסינכרוני () - מבצע את פרסום ההודעות בצורה אסינכרונית

בואו נשתמש בשיגור אסינכרוני בכיתת מדגם. נשתמש ב- Awaitility בבדיקות אלה כדי לפשט את הקוד:

משגר MBassador פרטי = MBassador חדש (); test מחרוזת פרטית String; פרטי AtomicBoolean מוכן = AtomicBoolean חדש (שקר); @ מבחן ציבורי בטל כאשר AsyncDispatched_thenMessageReceived () {dispatcher.post ("foobar"). באופן אסינכרוני (); לחכות (). untilAtomic (מוכן, שווה ל- (נכון)); assertNotNull (testString); } @Handler ציבור ריק ריק handleStringMessage (הודעת מחרוזת) {this.testString = הודעה; ready.set (נכון); }

אנו קוראים באופן אסינכרוני () במבחן זה, והשתמש ב- אטומי בוליאני כדגל עם לְהַמתִין() לחכות לשרשור המסירה כדי להעביר את המסר.

אם נגיב לשיחה ל לְהַמתִין(), אנו מסתכנים במבחן כישלון, מכיוון שאנו בודקים testString לפני השלמת חוט המסירה.

6.2. קריאת מטפל אסינכרוני

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

זה יכול להוביל לבעיות אם מטפל אחד מבצע פעולה יקרה.

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

מבחן שלם פרטי פרטי שלם; פרטי מחרוזת invocationThreadName; פרטי AtomicBoolean מוכן = AtomicBoolean חדש (שקר); @ מבחן ציבורי בטל כאשר HandlerAsync_thenHandled () {dispatcher.post (42) .now (); לחכות (). untilAtomic (מוכן, שווה ל- (נכון)); assertNotNull (testInteger); assertFalse (Thread.currentThread (). getName (). שווה (invocationThreadName)); } @Handler (מסירה = Invoke.Asynchronously) public void handleIntegerMessage (הודעה שלמה) {this.invocationThreadName = Thread.currentThread (). GetName (); this.testInteger = הודעה; ready.set (נכון); }

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

7. התאמה אישית של MBassador

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

7.1. טיפול בחריגים

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

מחלקה ציבורית MBassadorConfigurationTest מיישמת IPublicationErrorHandler {שולח MBassador פרטי; הודעת מחרוזת פרטית מחרוזת; שגיאה פרטית לזריקה סיבה; @ לפני הכנה בטלנית פומבית () {dispatcher = MBassador חדש (זה); dispatcher.subscribe (זה); } @ מבחן ציבורי בטל כאשרErrorOccurs_thenErrorHandler () {dispatcher.post ("שגיאה"). עכשיו (); assertNull (messageString); assertNotNull (errorCause); } @ מבחן ציבורי בטל כאשר NoErrorOccurs_thenStringHandler () {dispatcher.post ("שגיאה"). עכשיו (); assertNull (errorCause); assertNotNull (messageString); } @Handler public void handleString (הודעת מחרוזת) {if ("שגיאה". שווה (הודעה)) {זרוק שגיאה חדשה ("BOOM"); } messageString = הודעה; } @Override public void handleError (שגיאת פרסום שגיאה) {errorCause = error.getCause (). GetCause (); }}

מתי handleString () זורק שְׁגִיאָה, הוא נשמר ל errorCause.

7.2. עדיפות המטפל

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

אנו יכולים להגדיר עדיפות מטפל במפורש:

רשימת LinkedList פרטית = LinkedList חדש (); @ מבחן ציבורי בטל כאשר RejectDispatched_thenPriorityHandled () {dispatcher.post (RejectMessage חדש ()). עכשיו (); // על הפריטים להקפיץ () את סדר העדיפות ההפוכה assertTrue (1 == list.pop ()); assertTrue (3 == list.pop ()); assertTrue (5 == list.pop ()); } @Handler (עדיפות = 5) handle public publicRejectMessage5 (RejectMessage rejectMessage) {list.push (5); } @Handler (עדיפות = 3) handle בטל ציבוריRejectMessage3 (RejectMessage rejectMessage) {list.push (3); } @Handler (עדיפות = 2, rejectSubtypes = נכון) handgerMessage (הודעה rejectMessage) logger.error ("דחה מטפל # 3"); list.push (3); } @Handler (עדיפות = 0) handle בטל ציבוריRejectMessage0 (RejectMessage rejectMessage) {list.push (1); } 

המטפלים נקראים בראש סדר העדיפויות הנמוך ביותר. מטפלים עם עדיפות ברירת המחדל, שהיא אפס, נקראים אחרונים. אנו רואים שמספר המטפל פּוֹפּ() כבוי בסדר הפוך.

7.3. דחה תתי סוגים, הדרך הקלה

מה קרה ל handleMessage () במבחן למעלה? אנחנו לא צריכים להשתמש RejectSubTypes.class כדי לסנן את סוגי המשנה שלנו.

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

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

8. מסקנה

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

וכמו תמיד, הדוגמה זמינה בפרויקט GitHub זה.


$config[zx-auto] not found$config[zx-overlay] not found