יום שני, 21 בינואר 2013

ביצועים של אפליקציות ווב: מבוא לצד האפל

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

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

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

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


פשט

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



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

הערה: כמובן שבעת שעוסקים ברמה של קישור בין מערכות, ב High Level - זו רמת הפשטה נאותה.


מהתרשים לעיל, ניתן להסיק על חלוקת העבודה כמשהו בנוסח הבא:




האנטומיה של קריאת HTTP

בואו ניזכר קצת בפרוטוקולי התקשורת המעורבים:
פרוטוקול HTTP הוא פרוטוקול אפליקטיבי - הוא שולח נוסח טקסט מוסכם מראש המתאר התנהגות אפליקטיבית על גבי פרוטוקול TCP.
פרוטוקול TCP - הוא פרוטוקול לניהול תקשורת (Transmission Control Protocol) שמנהל Connection לשליחת רצף של הודעות, דואג שכולן יגיעו ושיגיעו בסדר הנכון.
מי שעושה באמת את העבודה הוא פרוטוקול (IP (Internet Protocol שאחראי לשליחת הודעות, ללא הבטחת סדר וללא אמינות.

כל קריאה של פרוטוקול IP היא בעצם הודעה שעוברת ממחשב אחד (לקוח) למחשב אחר (שרת). למרות שנדמה מהתרשים לעיל שהייתה בעצם הודעה אחת ותשובה אחת בין הלקוח לשרת - בפועל, ברמת ה IP, היו הרבה קריאות ותשובות
.
הנה תיאור מפורט / מדויק יותר של מה שמתרחש בפועל:

תרשים 1: קריאת HTTP לשרת כפי שהיא מתבצעת בפועל


DNS Lookup
כשאנחנו מקלידים כתובת (למשל http://www.ynet.co.il) יש קודם כל ללמוד מה כתובת ה IP של שרת היעד לפני שאנו יכולים לשלוח לו הודעות. כתובות אלו הן דינמיות ועשויות להשתנות לאורך הזמן. תהליך ההפיכה של הכתובת הטקסטואלית (hostname) לכתובת IP היא תהליך שנקרא DNS Lookup. בפועל הדפדפן שלנו פונה לשרת (Domain Name Servers (DNS, שהוא קורא לסדרה של שרתים אחרים, כל אחד בתורו, בכדי לפרש את הכתובת. תחילה קוראים לשרת ה ROOT כדי ללמוד על כתובת ה IP של שרת ה "il" (מפרשים את כתובת ה hostname מהסוף להתחלה). בשלב הבא עלינו לפנות לשרת "il" וללמוד ממנו את כתובת ה IP של שרת ה "co" (קיצור של commercial - הרי ynet הוא אתר מסחרי) ורק אז מקבלים את הכתובת ה IP של השרת של YNET בכבודו ובעצמו.

תהליך ה DNS Lookup לוקח בממוצע 130ms (עבור כל השרתים שבדרך). התקשורת נעשית באמצעות פרוטוקול UDP שהוא מהיר יותר מ TCP - אך איננו אמין. הסטטיסטיקה אומרת שבכ 5% מתהליכי ה DNS Lookup הודעה כלשהי הולכת לאיבוד. על הדפדפן לזהות מצב זה (ע"י קביעת timeout) ולשלוח הודעה מחדש - מה שיכול לגרום לתהליך ה DNS Lookup להתעכב זמן רב (עד כשתיים-שלוש שניות). כל זאת לפני שעוד בכלל ביצענו קריאה לשרת שלנו.


Establish TCP Connection
יצירה של TCP Connection כוללת תהליך שנקרא "לחיצת יד משולשת" שבמהלכו מתבצעות 3 קריאות של פרוטוקול IP רק כדי להסכים על ה connection. רק לאחר 3 קריאות אלו ניתן בעצם לשלוח את הודעת ה HTTP. בתקשורת מאובטחת (קרי HTTPS) יצירת ה connection מערבת "לחיצת ידיים" ארוכה בהרבה.

תיאור סכמטי של ה Three Way Handshake

HTTP Request and Response
רק לאחר שהוקם ה TCP Connection ניתן לשלוח הודעה אפליקטיבית (HTTP) לשרת. השרת מבצע את הקוד הנדרש (קורא לבסיס הנתונים או מבצע לוגיקה) ומחזיר את התשובה. זמן זה מסומן בתרשים 1 כ Server Time. יש לזכור שהשרת משקיע עבודה גם ביצירת ה TCP Connection ושליחת ההודעה עצמה - מה שמצוין בתרשים 1 כצבע הירוק ברקע.
ה HTTP Request הוא לרוב טקסט קצר למדי (כמה שורות בודדות) בעוד שהתשובה היא לרוב ארוכה למדי (קובץ HTML או קובץ JPG למשל) - ולכן אורכת זמן רב יותר.
נתון מעניין הוא ש "אינטרנט מהיר יותר" -כמעט ולא משפר גלישה רגילה באינטרנט. האינטרנט המהיר מאיץ רק את החלק של ה "HTTP Response" וכפי שניתן לראות בתרשים 1 - זהו חלק קטן מהזמן הכולל.


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


Rendering Time on the Browser
כאשר דף ה HTML מגיע לדפדפן, על הדפדפן לפענח את הפורמט (ולפעמים לתקן בו טעויות), לתרגם אותו למבנה שנקרא (DOM (Document Object Model ואז לרנדר ממנו את תצוגת הדף. תהליך דומה קורא לקבצי CSS ולקבצי javaScript. מנועי הרינדור של הדפדפנים השתפרו פלאים בשנים האחרונות - אך עדיין שלב זה גוזל זמן רב. זמני פעולה זו תלויים בחומרה של מחשב הלקוח, מנוע הרינדור של הדפדפן - וכמובן בדרך בה נכתב ה HTML / CSS / JavaScript.


Latency

התיאור שהשתמשנו בו עד עכשיו, של תקשורת IP או TCP כתקשורת של נקודה לנקודה, לקוח לשרת, איננו מדויק.
האינטרנט איננו רשת אחת, אלא מערך של רשתות שמחוברות בניהן בנתבים (Routers). מקור השם Internet הוא קיצור של inter-connected networks. בפועל כל הודעת IP מדלגת מרשת לרשת עד שהיא מגיעה לרשת של מחשב היעד - ושם היא מנותבת מקומית.
כל הודעה מנותבת באופן פרטני כך שייתכן שהודעות עוקבות ישלחו בכלל במסלולים שונים.

לקוח שולח הודעת IP, המנותבת על פני מספר רשתות עד שהיא מגיעה ליעד. התשובה (הודעת IP נוספת) מנותבת בדרך קצת אחרת.

הנה הרצה של פקודת "trace route", באמצעותה אני בוחן את הדרך שהודעת IP עוברת מהמחשב האישי שלי עד לשרת עליו מאוכסן הבלוג:


כפי שאתם יכולים לראות היו 17 תחנות בדרך. התחנה הראשונה, אגב, היא הראוטר הביתי שלי (הרשת הביתית היא עוד רשת). כל תחנה בדרך מוסיפה כמה מילי-שניות ובסה"כ זמן ה Round Trip, כלומר הודעת IP הלוך וחזור, היא בסביבות ה 100ms. זהו זמן דיי טוב שמאפיין את שרתי גוגל מחוץ לארה"ב (blogger שייך לגוגל).

לאתרים ללא "נוכחות" מקומית (כמו netflix או rottentomatoes) זמן ה (Round Trip (RTT הוא כ 200-300ms בד"כ, כלומר חצי מזה לכל כיוון. בשעות העומס, המספרים גבוהים יותר.

RTT של 250ms אינו נשמע מטריד - הרי זה זמן שבן-אנוש כמעט לא יכול להבחין בו. הבעיה היא כמובן במספר ה roundrtips הנדרשים על מנת לטעון דף אינטרנט מודרני. כיום, דף אינטרנט ממוצע מורכב מ 82 קבצים ו 1MB של מידע.
בצורה הנאיבית ביותר מדובר ב 82 x יצירת TCP connections ו 82 x בקשות HTTP (כאשר HTTP הוא פרוטוקול סינכרוני ולא ניתן להוציא בקשה חדשה לפני שהתשובה מהבקשה הקודמת חזרה) = 164 המתנות של רבע שנייה (כ 40 שניות) לא כולל DNS Lookup, זמן שרת, זמן רינדור בדפדפן וללא זמן הורדת הקבצים (= Bandwidth אינסופי).

מניסיון החיים, ברור שזה לא המצב בפועל. יש הרבה אופטימזציות ברמות השונות שמשפרות את נתוני הבסיס הבעייתיים. הנה כמה אופטימיזציות חשובות:
  • פרוטוקול HTTP 1.1 הוסיף מצב של keep-alive בו ניתן לבקש לא לסגור ולעשות שימוש חוזר ב TCP connection שנוצר. השיפור: כ 100%
  • דפדפנים החלו לפתוח מספר Connections מקבילים לשרת על מנת להאיץ את הורדת הקבצים. זה החל ב 2-3 connections לשרת והיום מגיע ל 6 ואף 8 connections לשרת. השיפור: עד 800%
  • רשתות Content Delivery Networks של שרתים הפזורים ברחבי העולם מחזיקים עותקים מקומיים של שרתים ששילמו על השירות - וכך מקצרים את ה latency בצורה משמעותית. השיפור: עד 1000%. במקרי קיצון: אפילו יותר. ניתן לקרוא על CDN בפוסט תקשורת: מיהו התוכי האמיתי של האינטרנט המהיר?
  • לדפדפנים יש cache בו הם שומרים קבצים אחרונים שנטענו מקומית על המחשב וכך חוסכים את כל הסעיפים הנ"ל.  ה cache יעיל החל מהגישה השנייה לאתר. השיפור: אדיר.
ויש עוד...
עדיין - יש הרבה שאתרים / אפליקציות ווב יכולות לעשות על מנת לשפר את הביצועים שלהם ולנצל טוב יותר את התשתיות הקיימות.

ל Latency יש השפעה רבה בהרבה על ביצועי אתרי אינטרנט מאשר ה Bandwidth.
הקשר בין זמן טעינה של דף אינטרנט כתלות ב RTT


לרוע המזל, שיפור של Bandwith הוא זול יחסית (יותר כבלים) בעוד שיפור ב Latency הוא יקר הרבה יותר: כיום התעבורה למרחקים נעשית במהירות האור, ונתיבי הכבלים מוגבלים מתוואי השטח. לדוגמה: את הכבלים התת-ימיים בין ארה״ב לאוסטרליה ניתן לקצר בכ 40% באורך, וכך לשפר את ה Latency בכ 40% - אך לצורך כך יש למתוח אותם בקו ישר, מה שיגרום לעלויות אסטרונומיות. לא פלא שספקי האינטרנט מתמקדים בפרסום ה Bandwidth שלהם ולא כ"כ ב Latency (אם כי גם הוא משתפר עם השנים).


ההיבט המובילי

מכשירי מובייל, מלבד מעבד חלש יותר ו bandwidth נמוך יותר, סובלים גם מ Latency גבוה יותר. כאשר עובדים ברשת 3G, על התקשורת לעבור כמה תאים סלולריים ותחנות ממסר לפני שהם מתחברים לרשת האינטרנט מה שיכול להוסיף כ 200ms ל RTT.
כפי שאתם אמורים כבר להבין, המשמעות היא כבדה מאוד - ועל כן אפליקציות מובייל משקיעות הרבה יותר באופטימיזציות, לעתים עד האסקטרים.

הבשורה הטובה היא שרשתות דור רביעי (LTE) לא רק משפרות משמעותית את ה Bandwidth אלא גם את ה Latency - החשוב יותר. מדובר על תוספת של כ 50ms ל RTT במקום כ 200ms - שיפור גדול מאוד.


סיכום

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


שיהיה בהצלחה!



3 תגובות:

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

    השבמחק
  2. קורס מזורז בתקשורת נתונים ;)
    אחלה פוסט, תודה ליאור!

    השבמחק
  3. תמיד נהנה לקרוא מאמרים שלך!
    יהיה טוב להזכיר כחלק מהאופטימיזציות של הדפדפנים ישנם גם dns caching ו-DNS Prefetching שנשמר בדפדפנים היום בהיסטוריה.
    תודה רבה ליאור

    השבמחק