לדעתי, Routing Service הוא אחת מהתכונות היותר מעניינות שהתווספו ל – WCF 4.0. למרות שניתן לפתח Routing Service עצמאי (ראה לדוגמא את המאמרים של Michele Leroux Bustamante's תחת הכותרת "Building a WCF Router") עדיין הגיוני יותר לבחון שימוש ב- Routing Service שמגיע OOTB עם .NET 4.0.
יש לא מעט מקורות מידע בנושא Routing Service, ואני אנסה שלא לחזור על החומר שמתואר בהם. בסוף הפוסט הזה ניתן למצוא לינקים לרשימת מקורות המידע המומלצים בעיניי.
עם זאת, ולמרות קיומם של מספר דוגמאות, לא הצלחתי למצוא דוגמא המתארת את היכולת לבצע Multicasting של הודעות (וזוהי התכונה בעלת פוטנציאל רב לדעתי). הדוגמא הקרובה ביותר שמצאתי בנושא היא AdvancedErrorHandling שעושה שימוש בשני תורי MSMQ על מנת להדגים כיצד ניתן לכתוב הודעה אחת לשני תורים: אחד כיעד ההודעה, והשני למטרת Logging.
אז בפוסט הזה אני אציג sample שמיישם את יכולת ה – Multicasting של WCF 4.0 Routing service. אך לפני שנקפוץ לתוך הקוד, אני מעוניין להסביר איך הסיפור הזה משתלב בתמונה הכללית של תבניות (patterns) הרלוונטיות לנושא.
Routing Service ומפת התבניות הרלוונטית
האפשרות לבצע routing מבוסס על בסיס תכני ההודעה (CBR, או Content Based Routing) היא יכולת שכלולה ברשימת התבניות הבסיסית של מוצרי ESB שונים (הבה נכנה אותם "ESB Usage Patterns").
האיור הבא מתאר את מפת התבניות הרלוונטית ל – ESB, והיחסים ביניהם (ישנה נטייה מסוימת לקרוא בשמות שונים לתבניות דומות, אבל הבה לא ניכנס כרגע לדיוני נומנקלטורה משמימים). מידע נוסף בנושא תבניות בתחום האינטגרציה ניתן למצוא באתר http://www.eaipatterns.com של Gregor Hohpe's, ובאופן כללי אני ממליץ בחום על ספרו בנושא.

ניתן ליישם את כל התבניות הנ"ל באמצעות ובשילוב Routing Service (אחרי הכל, Routing Service הינו רכיב המרחיב את WCF.
למטרות ה – smaple המתואר בפוסט הזה, אני מתמקד (בהתייחס לתרשים הנ"ל) ביכולות הבסיסיות של Routing Service וביישום של יכולות ה – multicast, תוך יישום תבנית שניתן לכנותה בהקשר זה כ – scatter-only (להבדיל מ – scatter-gather המופיע בתרשים).
דרך נוספת להציג את הקונטקסט של ה – sample, היא ע"י סקירה של היכולות הבסיסיות של Routing Service עצמו, כפי שהן מתוארות בתרשים הבא:

ניתן למצוא דוגמאות שמתארות את אופן היישום של היכולות שבתרשים לעיל ברשימות המקורות שבסוף הפוסט, אך כאמור, דוגמת Multicast היא יוצאת דופן בהקשר זה.
אז, הבה נעשה קצת multicasting:
תיאור ויזואלי של מה שמבצע ה – sample ניתן לראות בתרשים הבא:

הקליינט שולח הודעה דרך ה – Routing Service תוך שימוש בכל binding זמין (במקרה זה אני עושה שימוש ב – WsHttpBinding, אך כל binding אחר יכול לשמש למטרה זו). ה - Routing Service מעביר (routes) את ההודעה למספר כלשהו של שירותים (destination services) אשר לקיומם הוא מודע באמצעות הגדרות בקונפיגורציה (באמצעות קוד או קבצי קונפיגורציה). העברת ההודעות לשירותים מתבצעת תוך יישום פילטרים או חוקים המגדירים את מדיניות ההעברה (באמצעות מודל של Message Filters – ראו הרחבה בהמשך הפוסט).
ל - Routing Service יש גם את היכולת להגדיר ולעשות שימוש ב – Backup Endpoints המוגדרים ספציפית לכל נקודת שירות (Service Endpoint). במקרה של תקלה במשלוח הידיעה לנקודת שירות זו או אחרת, ה – beckup endpoint המקושר אליה יקבל את ההודעה במקומה, כיישום failover.
שימו לב שלא כללתי בדוגמא זו יישום של UDP transportation, למרות שהדבר מתבקש בכל דיון שעוסק ב – multicasting. ישנו יישום לדוגמא של UDP כ – WCF Sample, וניתן לעשות בו שימוש כ – binding בהקשר זה, אם כי זה כבר נושא לפוסט אחר.
ועכשיו - הגיע הזמן לראות קצת קוד.
The Client:
נתחיל עם ה – contract שמשתפים ביניהם ה – client ו – services. מדובר על contract שמיושם כ – interface פשוט למדי. שימו לב כי יישום multicast דורש יישום של one-way, היות ומדובר על תבנית של scatter-only ולא של scatter-gather מסוג כלשהו.

ה – contract הבא משותף בין ה – client וה – service. שימו לב של – Routing Service אין כל היכרות עם ה – contract הספציפי הזה, היות והוא אמור להיות אגנוסטי ל – contract או ה – interface הספציפי שאיתו עובדים ה – client ו – services מולם הוא מתקשר.
כאמור, ה -client שולח הודעת one-way רגילה למדי ל – Routing Service, ללא כל מודעות או התאמות לכך שמדובר ב – routing service ולא ב – service עצמו. ההתאמה היחידה הנדרשת היא כמובן שהכתובת (address) שבה ייעשה שימוש ה – client תהיה כתובתו של ה - Routing Service.
ב – interface שלעיל ניתן לראות שנשלח גם מידע על זמן משלוח ההודעה (כ - ticks). מטרת הפרמטר היא לאפשר ניטור של משך זמן משלוח ההודעה מה – client ל – service דרך ה - Routing Service. המידע מוצג הן ע"ג ה – client console והן ע"ג ה – service console (צילומי מסך בהמשך הפוסט). חשוב לציין כי נתוני זמני הביצועים יהיו אמינים ומדויקים רק כאשר גם ב – client וגם ה – service ממוקמים ע"ג אותו שרת\מחשב (ה - Routing Service יכול להיות ממוקם ע"ג שרת מרוחק).

The destination service:
למטרות ה – sample יושמו מספר endpoints על גבי ה – service, כאשר כל endpoint מיישמת binding מסוג אחר. היישום מבוצע במתודה בשם "GenerateEndpoints".

מעבר לזאת, ה – service הוא רגיל למדי. החומר המעניין נמצא, כצפוי, ב - Routing Service.
The Routing Service:
בצד של ה - Routing Service, החומר המעניין מרוכז בעיקר במתודה בשם "ConfigureRouterViaCode". מרכז הפעילות במתודה הוא סביב אובייקט בשם RoutingConfiguration אותו יוצרים, מגדירים ומזינים לתוך ה – ServiceHost.

סדר הפעילות, בקצרה, הוא:
- יצירת אובייקט RoutingConfiguration
- יצירת רשימת ה – endpoints של ה – services אליהם יש לשלוח הודעות (הרשימה תאוחזר, מן הסתם מתוך מאגר מידע כלשהו, כגון בסיס נתונים זה או אחר).
- הוספת כל endpoint לרשומה משלו באוסף ה - Filter Table, תוך ציון ה – Message Filter המתאים (הרחבה בהמשך).
- אופציונאלית, ניתן להגדיר לכל endpoint גם backup endpoint תואם.
- לסיום, הוספת ה - RoutingConfiguration ל – routing behavior, והוספת ה – routing behavior ל – ServiceHost.
חשוב להדגיש כי ה - Routing Service אינו מודע ל – contract type שאיתו עובדים ה – client ו\או ה – service. ה - Routing Service עושה שימוש ב – contract ייעודי שמטרתו לאפשר העברת ההודעות באופן שמתעלם מה -contract הספציפי ומודע רק ל – Message Exchange Pattern (ה - MEP) בו נעשה שימוש.
.
ב – sample זה, אנו עושים שימוש ב – ISimplexDatagramRouter, אשר, כפי ששמו מרמז, מהווה interface פשוט לייצור contract שמיועד להעברת הודעות כ – datagram (כלומר: בסמנטיקה של one-way). ניתן לעשות שימוש במספר contracts / interfaces אחרים של Routing Service ליישום sessions (ISimplexSessionRouter), וכן ליישום Request/Reply MEP (IRequestReplyRouter).
כברירת מחדל ב – sample זה, ה - Routing Service מעביר את כל ההודעות לכל ה- services המוגדרים ב – routing configuration. זאת, מכיוון שנעשה שימוש בדוגמא שלעיל ב – MatchAllMessageFilter. יש עוד מגוון של Message Filters אחרים שמגיעים OOTB, אך גם מאוד קל ליישם Custom Message Filters באופן עצמאי. ב – sample כלולים שני Custom Message Filters אשר מאפשרים לבצע routing בהתאם לבדיקת והשוואת ערך ה – SOAP Action header וכן ע"י השוואות ערכים ב – Message Body (היישום אינו אופטימלי בהיבטי ביצועים, אך ניתן לתקן זאת בהשקעה נמוכה יחסית, ובהתאם לתסריט הספציפי הנדרש ליישום).
לדוגמא, דוגמאת הקוד הבאה, מציגה את ה – ActionContainsMessageFilter הכלול ב – sample.

ניתן להפעיל message filter מסוים באמצעות שורת הקוד הבאה, הממוקמת (כהערה שניתן לבצע לה uncomment) במתודה ConfigureRouterViaCode שצוינה לעיל:

הפעלת שורת קוד זו תאפשר העברה רק של הודעות אשר ב – SOAP Action header שלהם מופיעה המילה "Write".
ניתן להגדיר את ה – Routing Service באמצעות קובץ קונפיגורציה סטנדרטי (app.config או web.config), אך ב – sample בחרתי לבצע זאת בקוד מסיבות שכבר צוינו. עם זאת, ב – sample כלול קובץ קונפיגורציה לדוגמא אשר יכול להדגים זאת.
הפעלת ה - Sample:
ה – sample כולל שלושה executables:
- client console
- Routing Service console
- service console
מסיבה זו, ה – solution הוגדר להפעלה של Multiple Startup Projects:

חשוב לשים לב שה – client מוגדר ש – Start without debugging. הסיבה לכך היא באג אשר מתיו סניידר, ה – Program Manager של Routing Service, הסב את תשומת ליבי לקיומו. כאשר פרויקט WCF נמצא ב – debug mode, ה – debugger מוסיף custom SOAP header ייחודי להודעה. במקרה של Routing Service, היות וגם ה – client וגם ה - Routing Service עצמו יכולים להיות תחת debugger, יתבצע ניסיון גם ב - Routing Service עצמו להוסיף את אותו ה – custom SOAP header, אך מכיוון שהוא כבר שם, תתרחש שגיאה. המעקף לכך הוא פשוט מאוד – הפעלה של ה – client או ה - Routing Service שלא תחת ה – debugger, כפי שמתבצע ב – sample זה.
לחיצה על F5 מפעילה את 3 ה – executables ומאתחלת את הרכיבים, כאשר ה – client ממתין לאישור פעולה של המשתמשע"י לחיצה על enter.
לחיצה על enter בחלון ה – client תשלח הודעות אתחול ל - Routing Service ותספק היזון חוזר לגבי הביצועים ההתחלתיים.

שימוש לב שבאתחול, ההודעה הראשונית יוצאת לאחר פרק זמן ארוך יחסית, בשל אילוצי אתחול רכיבים ו – transport.
בצד השירות, ניתן לראות כי הודעה אחת אשר נשלחה ע"י ה – client, הופצה ע"י ה - Routing Service למספר רב של endpoints מסוגים שונים, בהתאם להגדרות ב - RoutingConfiguration settings.
עם משלוח ההודעה השנייה ע"י ה – client, ניתן לראות את הביצועים הסטנדרטיים של הרכיבים לאחר האתחול:

והנה התוצאה במסך ה – service:

קוד מקור של ה - sample:
ניתן להוריד את הקוד של ה – sample בלינק הזה. חשוב לציין שדוגמת הקוד ניתנת כמות שהיא, ללא אחריות או התחייבות כלשהי, מפורשת או משתמעת.
מקורות מידע נוספים על Routing Service:
|
שמי דני כהן ואני ארכיטקט ויועץ בצוות MCS Israel, ומתמחה במערכות מבוזרות, Cloud Computing, מתודולוגיות פיתוח וארכיטקטורת תוכנה. |