בפוסט זה אני רוצה לדבר על דפוס עיצוב נפוץ למדי בעולם ה micro-services - ה API Gateway.
בדפוס העיצוב הזה נתקלתי מכמה מקורות, כאשר כולם מפנים בסוף לבלוג של נטפליקס - משם נראה שדפוס העיצוב התפרסם.
לפני שנתחיל - אני רוצה להצביע על אלמנט מבלבל במינוח של תבנית העיצוב:
Facade הוא הוא אובייקט המספק תמונה (View) פשוטה של רכיבים פנימיים מורכבים (״complex internals״ - מתוך ההגדרה של GoF) - למשל כמו ב Layered Architecture.
Gateway הוא אובייקט שמספק הכמסה לגישה למערכות או משאבים חיצוניים - למשל כמו רכיב הרשת Proxy שנקרא לפעמים גם Gateway.
Proxy הוא אובייקט שמספק אותו ממשק כמו אובייקט אחר, אך מספק ערך מוסף בגישה לאובייקט (למשל Laziness או Caching).
יש משהו לא מדויק ומבלבל בשם ״API Gateway״. כפי שנראה, דפוס העיצוב הוא בעצם שילוב של שלושת הדפוסים הנ״ל.
מכל השמות, דווקא ״API Facade״ נראה לי המדויק ביותר, וזה שמכווין בצורה הטובה ביותר לאופן בו כדאי לממש את דפוס העיצוב - ולכן אצמד אליו. נחזור לדיון על השם במהלך הפוסט.
להזכיר: דפוס עיצוב, ככלל, הוא מקור השראה יותר משהוא "אמת מוחלטת" שכדאי להיצמד אליה בלהבלהבלה.....
API Facade החל בשל צורך של נטפליקס לארגון ה APIs שלהם: המערכת המונוליטית הפכה לעוד ועוד שירותים - עוד שירותים שעל ה clients היה להכיר. הצורך בהיכרות עם כל השירותים יצר dependency ב deployment שהפך את משימת ה deployment לקשה יותר. למשל: פיצול שירות לשני שירותים חדשים דרש עדכון של כל ה clients ו deploy של גרסה חדשה - גרסה שמודעת לכך ש API X עכשיו שייך לשירות B ולא לשירות A (מה אכפת ל Clients בכלל מהחלוקה הפנימית לשירותים?!).
ה API Facade הוא רכיב שיושב בין ה Client לשירותים, ומסתיר את פרטיהם הלא מעניינים. הוא מציג ל Client סדרת API פשוטים, כאשר מאחורי כל אחד מהם יש flow = סדרת קריאות ל APIs של שירותים שונים.
באופן זה, שינויים ב APIs של השירותים (פיצול, איחוד, או שינויים קלים בחתימה) לא משפיעים על ה Client ולא דורשים את עדכונו.
תפקיד זה הוא Facade קלאסי.
עוד בעיה שצצה היא עניין ה Latency:
כאשר ה Client ביצע יותר ויותר קריאות (כי יש יותר ויותר שירותים) עלויות ה latency של הרשת גברו:
Latency לאמזון יכול בקלות להגיע ל 100-200ms. במקום לשלם על latency של קריאה אחת - שילמו על latency של הרבה קריאות. חלק מהקריאות הן טוריות (4 בתרשים לעיל) - ואז ה Latency האפקטיבי הוא פי 4 מ latency של קריאה יחידה.
בעזרת הצבת API Facade שמקבל קריאה יחידה, ואז מבצע סדרה של קריאות בתוך הרשת הפנימית (שהן זולות בהרבה: פחות מ 10ms באמזון, ופחות מ 1ms ב Data Center רגיל) - ניתן לקצר באמת את ה Latency של הרשת שעל ה Client "לשלם":
חשוב לממש את ה API Facade כך, שהוא לא יגרע מהמקביליות שהייתה קיימת קודם לכן ב Client - אחרת הוא יכול לגרום ליותר נזק מתועלת. לצורך כך ממשים את ה API Facade בעזרת שפה/כלים שקל לבצע בהם מקביליות וסנכרון שיהיו גם קלים לתחזוקה, וגם יעילים מבחינת ביצועים. נטפליקס בחרה למשימה ב Closure (תכנות פונקציונלי) או Groovy עם מודל ראקטיבי (RX observables). אחרים בחרו בשפת Go או סתם #C או ג'אווה.
תפקיד זה, של ניהול קישוריות בצורה יעילה, היא תפקיד קלאסי של Gateway.
וריאציה אחרת של תפקיד Gateway היא כאשר ה API Facade יודע לתרגם את פרוטוקול ה Client האחיד (נאמר HTTP) לפרוטוקולים שונים של השירותים השונים (נאמר SOAP, ODATA, ו RPC) - וכך לסייע בקישוריות.
עניין אחרון שבו ה API Facade מסייע הוא עניין של העשרת התקשורת. למשל: אתם רוצים שכל ה clients יבצעו SSO (קיצור של Single Sign-On, דוגמת SAML 2.0) או Audit על הגישה לשירותים שלכם. מה יותר נוח מלעשות זאת במקום אחד מרכזי (מאשר בכל אחד מהשירותים, ואז להתמודד עם פערי-גרסאות)?
כנקודת גישה מרכזית, ה API Facade יכול לרכז פעולות אלו ואחרות. זהו תפקיד קלאסי של Proxy.
בנקודה זו כדאי לעצור ולציין מכשלה שהולכת ומסתמנת בדפוס העיצוב של ה API Facade והיא הפיתוי להפוך את ה API Facade ל"כלבוייניק" של כל מה שקשור לקישוריות, או אבטחה, או סנכרון בין השירותים.
עצה חוזרת ונשנה היא להשאיר את ה API Facade רזה ומינימלי ככל האפשר - כדי שלא יסתבך.
מדוע?
משום שה API Facade הוא ריכוז של תלויות:
אם עושים למישהו Deploy תכוף מאוד, כדאי מאוד שהוא יהיה פשוט, אמין, ולא "ייתפס באמצע פיתוח" שיעכב את ה deploy. מצד כזה בדיוק יכול להיות לרכיב שמטפל גם בתרגום פרוטוקולים (Gateway), וגם בסיפוק שירותי אבטחה (Proxy).
לכן, ה Best Practice הוא לבצע extraction של כל לוגיקה אפשרית מה API Facade לשירותי-עזר שיספקו שירותים שכאלה (תרגום, SSO, וכו'), בעוד ה API Facade הוא רק נקודת החיבור של שירותי העזר האלו ל API של ה Client.
התוצאה: ה API Facade נשאר "easily deployable" - הוא מתמחה רק בהחזקת תלויות רבות ובקריאה לרצפים (flows) של APIs של שירותים עסקיים, בעוד שירותי העזר שלו הם בעלי הלוגיקה, ולהם ניתן לבצע עדכון / deploy - רק ע"פ הצורך ובקצב שנוח לעשות כן.
כך בערך זה נראה:
איך תקשרו את זה לצוות שלכם? את העובדה שאתם רוצים שה API Facade יהיה רזה ומינימליסטי, ולא יכלול שום דבר אחר? שאתם לא רוצים "ליפול בפח" של יצירת רכיב מורכב שתלוי כמעט-בכולם, ובאופן זה הוא מערער את יציבות המערכת?
נכון! ע"י כך שתקראו לו "API Facade", ולא "API Gateway" או "API Proxy".
מכיוון ש "Facade", בהגדרה, הוא "שלד ועצמות", רזה ומינימליסטי, בעוד "Gateway" או "Proxy" - הם לא.
הנה עוד המלצה לאופן שיצמצם את התלויות שה-API Facade נושא עליו:
לבצע "partitioning" של ה API Facade לכך שיהיו כמה API Facades במערכת - אחד לכל Client.
נטפליקס גילו שהקמת צוות ייעודי לתחזוקת ה API Facade יצרה צוואר-בקבוק וחוסר תקשורת מול צוותי ה client - הלקוח העיקרי של התוצר. הם העבירו את האחריות לקוד ה API Facade לצוות ה Client - כאשר יש API Facade לכל Client שקיים. התוצאות היו חיוביות - וגם חברות אחרות אימצו גישה זו.
מאוחר יותר, נטפליקס איחדו את התשתית של כל ה API Facades לתשתית אחידה - עבור יעילות ואחידות גבוהות יותר. עדיין כל צוות אחראי להגדרת ה flows שלו (flow = קריאה נכנסת מה Client, שמתתרגמת לסדרת קריאות מול השירותים השונים).
בתבנית ה APIGateway Facade נתקלתי כבר מספר פעמים. בקרוב, אנחנו ננסה גם ליישם אותה אצלנו ב GetTaxi.
התבנית נראית כמו חלק טבעי במחזור החיים של ארכיטקטורה: ארכיטקטורת Micro-Services הופכת לפופולרית, ואיתה צצות כמה בעיות חדשות. מה עושים? מתארים Best Practices, או דפוסים - שמציעים פתרונות לבעיות הללו, ומתקשרים אותם כ"דפוסי עיצוב".
עם הזמן, הפתרונות המוצלחים שביניהם יהפכו לחלק מהגדרת הארכיטקטורה של MSA, ויפנו את מקומם ל Best Practices חדשים...
API Facade הוא דפוס שעוזר להתמודד עם בעיה ספציפית שנוצרת בעקבות ריבוי השירותים של Micro-Services Architecture, והצורך של ה Client לתקשר עם כל "הסבך הזה" (יש כאלו שהחלו לצייר את השירותים כ"ענן שירותים"). API-Facade מקל על הבעיה, אך גם יוצר פיתוי מסוים ליצור "גוש מונוליטי של פונקציונליות, מלא תלויות" ממנו כדאי להיזהר...
שיהיה בהצלחה!
----
מקורות:
בדפוס העיצוב הזה נתקלתי מכמה מקורות, כאשר כולם מפנים בסוף לבלוג של נטפליקס - משם נראה שדפוס העיצוב התפרסם.
מקור: microservices.io |
לפני שנתחיל - אני רוצה להצביע על אלמנט מבלבל במינוח של תבנית העיצוב:
Facade הוא הוא אובייקט המספק תמונה (View) פשוטה של רכיבים פנימיים מורכבים (״complex internals״ - מתוך ההגדרה של GoF) - למשל כמו ב Layered Architecture.
Gateway הוא אובייקט שמספק הכמסה לגישה למערכות או משאבים חיצוניים - למשל כמו רכיב הרשת Proxy שנקרא לפעמים גם Gateway.
Proxy הוא אובייקט שמספק אותו ממשק כמו אובייקט אחר, אך מספק ערך מוסף בגישה לאובייקט (למשל Laziness או Caching).
יש משהו לא מדויק ומבלבל בשם ״API Gateway״. כפי שנראה, דפוס העיצוב הוא בעצם שילוב של שלושת הדפוסים הנ״ל.
מכל השמות, דווקא ״API Facade״ נראה לי המדויק ביותר, וזה שמכווין בצורה הטובה ביותר לאופן בו כדאי לממש את דפוס העיצוב - ולכן אצמד אליו. נחזור לדיון על השם במהלך הפוסט.
להזכיר: דפוס עיצוב, ככלל, הוא מקור השראה יותר משהוא "אמת מוחלטת" שכדאי להיצמד אליה בלהבלהבלה.....
דפוס העיצוב
API Facade החל בשל צורך של נטפליקס לארגון ה APIs שלהם: המערכת המונוליטית הפכה לעוד ועוד שירותים - עוד שירותים שעל ה clients היה להכיר. הצורך בהיכרות עם כל השירותים יצר dependency ב deployment שהפך את משימת ה deployment לקשה יותר. למשל: פיצול שירות לשני שירותים חדשים דרש עדכון של כל ה clients ו deploy של גרסה חדשה - גרסה שמודעת לכך ש API X עכשיו שייך לשירות B ולא לשירות A (מה אכפת ל Clients בכלל מהחלוקה הפנימית לשירותים?!).
ה API Facade הוא רכיב שיושב בין ה Client לשירותים, ומסתיר את פרטיהם הלא מעניינים. הוא מציג ל Client סדרת API פשוטים, כאשר מאחורי כל אחד מהם יש flow = סדרת קריאות ל APIs של שירותים שונים.
באופן זה, שינויים ב APIs של השירותים (פיצול, איחוד, או שינויים קלים בחתימה) לא משפיעים על ה Client ולא דורשים את עדכונו.
תפקיד זה הוא Facade קלאסי.
עוד בעיה שצצה היא עניין ה Latency:
כאשר ה Client ביצע יותר ויותר קריאות (כי יש יותר ויותר שירותים) עלויות ה latency של הרשת גברו:
מקור: הבלוג של נטפליקס |
בעזרת הצבת API Facade שמקבל קריאה יחידה, ואז מבצע סדרה של קריאות בתוך הרשת הפנימית (שהן זולות בהרבה: פחות מ 10ms באמזון, ופחות מ 1ms ב Data Center רגיל) - ניתן לקצר באמת את ה Latency של הרשת שעל ה Client "לשלם":
מקור: הבלוג של נטפליקס |
חשוב לממש את ה API Facade כך, שהוא לא יגרע מהמקביליות שהייתה קיימת קודם לכן ב Client - אחרת הוא יכול לגרום ליותר נזק מתועלת. לצורך כך ממשים את ה API Facade בעזרת שפה/כלים שקל לבצע בהם מקביליות וסנכרון שיהיו גם קלים לתחזוקה, וגם יעילים מבחינת ביצועים. נטפליקס בחרה למשימה ב Closure (תכנות פונקציונלי) או Groovy עם מודל ראקטיבי (RX observables). אחרים בחרו בשפת Go או סתם #C או ג'אווה.
תפקיד זה, של ניהול קישוריות בצורה יעילה, היא תפקיד קלאסי של Gateway.
וריאציה אחרת של תפקיד Gateway היא כאשר ה API Facade יודע לתרגם את פרוטוקול ה Client האחיד (נאמר HTTP) לפרוטוקולים שונים של השירותים השונים (נאמר SOAP, ODATA, ו RPC) - וכך לסייע בקישוריות.
עניין אחרון שבו ה API Facade מסייע הוא עניין של העשרת התקשורת. למשל: אתם רוצים שכל ה clients יבצעו SSO (קיצור של Single Sign-On, דוגמת SAML 2.0) או Audit על הגישה לשירותים שלכם. מה יותר נוח מלעשות זאת במקום אחד מרכזי (מאשר בכל אחד מהשירותים, ואז להתמודד עם פערי-גרסאות)?
כנקודת גישה מרכזית, ה API Facade יכול לרכז פעולות אלו ואחרות. זהו תפקיד קלאסי של Proxy.
סכנה!
בנקודה זו כדאי לעצור ולציין מכשלה שהולכת ומסתמנת בדפוס העיצוב של ה API Facade והיא הפיתוי להפוך את ה API Facade ל"כלבוייניק" של כל מה שקשור לקישוריות, או אבטחה, או סנכרון בין השירותים.
עצה חוזרת ונשנה היא להשאיר את ה API Facade רזה ומינימלי ככל האפשר - כדי שלא יסתבך.
מדוע?
משום שה API Facade הוא ריכוז של תלויות:
- הוא תלוי בכל ה Services אותם הוא מסתיר
- הוא תלוי בכל ה Clients אותם הוא משרת
אם עושים למישהו Deploy תכוף מאוד, כדאי מאוד שהוא יהיה פשוט, אמין, ולא "ייתפס באמצע פיתוח" שיעכב את ה deploy. מצד כזה בדיוק יכול להיות לרכיב שמטפל גם בתרגום פרוטוקולים (Gateway), וגם בסיפוק שירותי אבטחה (Proxy).
לכן, ה Best Practice הוא לבצע extraction של כל לוגיקה אפשרית מה API Facade לשירותי-עזר שיספקו שירותים שכאלה (תרגום, SSO, וכו'), בעוד ה API Facade הוא רק נקודת החיבור של שירותי העזר האלו ל API של ה Client.
התוצאה: ה API Facade נשאר "easily deployable" - הוא מתמחה רק בהחזקת תלויות רבות ובקריאה לרצפים (flows) של APIs של שירותים עסקיים, בעוד שירותי העזר שלו הם בעלי הלוגיקה, ולהם ניתן לבצע עדכון / deploy - רק ע"פ הצורך ובקצב שנוח לעשות כן.
כך בערך זה נראה:
איך תקשרו את זה לצוות שלכם? את העובדה שאתם רוצים שה API Facade יהיה רזה ומינימליסטי, ולא יכלול שום דבר אחר? שאתם לא רוצים "ליפול בפח" של יצירת רכיב מורכב שתלוי כמעט-בכולם, ובאופן זה הוא מערער את יציבות המערכת?
נכון! ע"י כך שתקראו לו "API Facade", ולא "API Gateway" או "API Proxy".
מכיוון ש "Facade", בהגדרה, הוא "שלד ועצמות", רזה ומינימליסטי, בעוד "Gateway" או "Proxy" - הם לא.
הנה עוד המלצה לאופן שיצמצם את התלויות שה-API Facade נושא עליו:
לבצע "partitioning" של ה API Facade לכך שיהיו כמה API Facades במערכת - אחד לכל Client.
נטפליקס גילו שהקמת צוות ייעודי לתחזוקת ה API Facade יצרה צוואר-בקבוק וחוסר תקשורת מול צוותי ה client - הלקוח העיקרי של התוצר. הם העבירו את האחריות לקוד ה API Facade לצוות ה Client - כאשר יש API Facade לכל Client שקיים. התוצאות היו חיוביות - וגם חברות אחרות אימצו גישה זו.
מאוחר יותר, נטפליקס איחדו את התשתית של כל ה API Facades לתשתית אחידה - עבור יעילות ואחידות גבוהות יותר. עדיין כל צוות אחראי להגדרת ה flows שלו (flow = קריאה נכנסת מה Client, שמתתרגמת לסדרת קריאות מול השירותים השונים).
סיכום
בתבנית ה API
התבנית נראית כמו חלק טבעי במחזור החיים של ארכיטקטורה: ארכיטקטורת Micro-Services הופכת לפופולרית, ואיתה צצות כמה בעיות חדשות. מה עושים? מתארים Best Practices, או דפוסים - שמציעים פתרונות לבעיות הללו, ומתקשרים אותם כ"דפוסי עיצוב".
עם הזמן, הפתרונות המוצלחים שביניהם יהפכו לחלק מהגדרת הארכיטקטורה של MSA, ויפנו את מקומם ל Best Practices חדשים...
API Facade הוא דפוס שעוזר להתמודד עם בעיה ספציפית שנוצרת בעקבות ריבוי השירותים של Micro-Services Architecture, והצורך של ה Client לתקשר עם כל "הסבך הזה" (יש כאלו שהחלו לצייר את השירותים כ"ענן שירותים"). API-Facade מקל על הבעיה, אך גם יוצר פיתוי מסוים ליצור "גוש מונוליטי של פונקציונליות, מלא תלויות" ממנו כדאי להיזהר...
שיהיה בהצלחה!
----
מקורות:
- הפוסט "המדובר" בבלוג של נטפליקס: http://techblog.netflix.com/2013/01/optimizing-netflix-api.html
- תיאור דפוס העיצוב ב microservices.io (כרגע, דפוס העיצוב היחידי באתר): http://microservices.io/patterns/apigateway.html
- Microservices are SOLID, פוסט שמזכיר את הנושא כחלק מקשת יותר רחבה: http://www.mattstine.com/2014/06/30/microservices-are-solid
- סיפור על מימוש API Gateway שהסתבך: http://microsoftintegration.guru/2014/11/17/part-3-api-gateway-worked-went-wrong/
- סיפור על לקחים מהסיפור הקודם, שהצליחו טוב יותר בפעם השניה: http://microsoftintegration.guru/2014/11/17/part-4-api-gateway-cloud/