מבוא ל-HTTP/2

פרוטוקול HTTP/2 יהפוך את האפליקציות שלנו למהירות, פשוטות וחזקות יותר – שילוב נדיר – בכך שהוא יאפשר לנו לבטל רבים מהפתרונות לעקוף HTTP/1.1 שבוצעו בעבר באפליקציות שלנו, ולטפל בבעיות האלה בשכבת התעבורה עצמה. יותר מזה, פותחות בפנינו גם כמה הזדמנויות חדשות לגמרי לבצע אופטימיזציה של האפליקציות שלנו ולשפר את הביצועים!

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

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

למה לא HTTP/1.2?

כדי להשיג את יעדי הביצועים שהוגדרו על ידי קבוצת העבודה של HTTP, HTTP/2 מציג שכבה חדשה של פריים בינארית, שלא תואמת לאחור לשרתים ולקוחות HTTP/1.x קודמים, ולכן ההגדלה העיקרית של גרסת הפרוטוקול ל-HTTP/2.

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

היסטוריה קצרה של SPDY ו-HTTP/2

SPDY היה פרוטוקול ניסיוני, שפותח ב-Google והכרזנו עליו באמצע 2009. מטרתו העיקרית הייתה לנסות לקצר את זמן האחזור לטעינת דפי אינטרנט, על ידי טיפול בכמה ממגבלות הביצועים הידועות של HTTP/1.1. באופן ספציפי, יעדי הפרויקט שצוינו הוגדרו כך:

  • כדאי לנסות לצמצם את זמן הטעינה של דפים (PLT) ב-50%.
  • להימנע מהצורך לבצע שינויים בתוכן על ידי מחברי האתר.
  • צמצום מורכבות הפריסה ומניעת שינויים בתשתית הרשת.
  • מפתחים את הפרוטוקול החדש בשותפות עם קהילת הקוד הפתוח.
  • אספו נתוני ביצועים אמיתיים כדי (in) לאמת את הפרוטוקול הניסיוני.

זמן קצר לאחר ההודעה הראשונית, מייק בלש ורובטו פאון, שניהם מהנדסי תוכנה ב-Google, שיתפו את התוצאות הראשונות, את התיעוד ואת קוד המקור שלהם ליישום הניסיוני של פרוטוקול SPDY החדש:

עד עכשיו בדקנו SPDY רק בתנאי מעבדה. התוצאות הראשוניות מעודדות מאוד: כשאנחנו מורידים את 25 האתרים המובילים באמצעות סימולציה של חיבורי רשת ביתית, אנחנו רואים שיפור משמעותי בביצועים – דפים שנטענים עד 55% מהר יותר. (בלוג Chromium)

בתחילת שנת 2012, הפרוטוקול הניסיוני החדש נתמך ב-Chrome, ב-Firefox וב-Opera, ומספר גדל והולך של אתרים – גדולים (למשל Google, Twitter, Facebook) וגם קטנים, פרסו את SPDY בתשתית שלהם. למעשה, SPDY היה צפוי להפוך לסטנדרט בפועל, בעקבות אימוץ הולך וגדל בתחום.

בעקבות המגמה הזו, קבוצת העבודה של HTTP (HTTP-WG) התחילה במאמץ חדש להפיק את הלקחים שנלמדו מ-SPDY, לבנות אותם ולשפר אותם, ולספק תקן "HTTP/2" רשמי. נכתב אמנה חדשה, התקבלה קריאה פתוחה להצעות HTTP/2, ולאחר דיונים רבים בקבוצת העבודה, אומץ מפרט SPDY כנקודת התחלה לפרוטוקול HTTP/2 החדש.

בשנים הבאות, SPDY ו-HTTP/2 המשיכו להתפתח במקביל, ו-SPDY פעל כסניף ניסיוני ששימש לבדיקת תכונות והצעות חדשות לתקן HTTP/2. מה שנראה טוב על הנייר עלול לא לעבוד בפועל ולהיפך, ו-SPDY הציע מסלול לבדיקה ולהערכה של כל הצעה לפני הכללתה בתקן HTTP/2. בסופו של דבר, התהליך הזה נמשך שלוש שנים ויצר יותר מתריסר טיוטות ביניים:

  • מרץ 2012: קריאה להצעות עבור HTTP/2
  • נובמבר 2012: טיוטה ראשונה של HTTP/2 (על סמך SPDY)
  • אוגוסט 2014: פורסמו טיוטה 17 HTTP/2 וטיוטה 12 של HPACK
  • אוגוסט 2014: קריאה אחרונה של קבוצת העבודה ל-HTTP/2
  • פברואר 2015: טיוטות HTTP/2 ו-HPACK שאושרו על ידי IESG
  • מאי 2015: פורסמו RFC 7540 (HTTP/2) ו-RFC 7541 (HPACK)

בתחילת 2015, ארגון IESG בדק ואישר את תקן HTTP/2 החדש לפרסום. זמן קצר לאחר מכן, צוות Google Chrome הכריז על לוח הזמנים שלו להוצאה משימוש של התוספים SPDY ו-NPN ל-TLS:

השינויים העיקריים ב-HTTP/2 מ-HTTP/1.1 מתמקדים בביצועים משופרים. כמה מהתכונות העיקריות, כמו ריבוב, דחיסת כותרות, תעדוף ומשא ומתן על פרוטוקול התפתחו מעבודה שהתבצעה בפרוטוקול פתוח קודם אבל לא סטנדרטי בשם SPDY. Chrome תומך ב-SPDY מאז Chrome 6, אבל מכיוון שרוב היתרונות קיימים ב-HTTP/2, הגיע הזמן להפסיק את התמיכה. אנחנו מתכננים להסיר את התמיכה ב-SPDY בתחילת 2016, ולהסיר באותו זמן גם את התמיכה בתוסף TLS שנקרא NPN לטובת ALPN ב-Chrome. אנחנו ממליצים מאוד למפתחי שרתים לעבור ל-HTTP/2 ול-ALPN.

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

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

עיצוב ויעדים טכניים

גרסאות קודמות של פרוטוקול HTTP תוכננו באופן מכוון לצורך הטמעה פשוטה: HTTP/0.9 היה פרוטוקול של שורה אחת לאתחול ה-World Wide Web; HTTP/1.0 תיעד את התוספים הפופולריים ל-HTTP/0.9 בתקן מידע; HTTP/1.1 הציג תקן IETF רשמי; ראו היסטוריה קצרה של HTTP. לכן, HTTP/0.9-1.x סיפק בדיוק את מה שהוא אמור לעשות: HTTP הוא אחד מהפרוטוקולים הנפוצים ביותר לאפליקציות באינטרנט.

לצערנו, גם לפשטות ההטמעה יש השפעה על ביצועי האפליקציה: לקוחות HTTP/1.x צריכים להשתמש במספר חיבורים כדי להשיג בו-זמניות ולצמצם את זמן האחזור; HTTP/1.x לא דוחס כותרות של בקשות ותשובות, וגורם לתנועה מיותרת ברשת; HTTP/1.x לא מאפשר תעדוף יעיל של משאבים, וכתוצאה מכך השימוש לא טוב בחיבור ה-TCP הבסיסי, וכן הלאה.

המגבלות האלה לא היו חמורות, אבל ככל שאפליקציות האינטרנט המשיכו להתרחב בהיקף, במורכבות ובחשיבות שלהן בחיי היום-יום שלנו, הן יצרו נטל הולך וגדל גם על המפתחים וגם על המשתמשים באינטרנט, וזה הפער המדויק ש-HTTP/2 נועד לטפל בו:

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

הפרוטוקול שנוצר ידידותי יותר לרשת, כי אפשר להשתמש בפחות חיבורי TCP בהשוואה ל-HTTP/1.x. המשמעות היא פחות תחרות עם תהליכים אחרים וחיבורים לטווח ארוך יותר, מה שמוביל לניצול טוב יותר של קיבולת הרשת הזמינה. לבסוף, HTTP/2 מאפשר גם עיבוד יעיל יותר של הודעות באמצעות מסגור הודעות בינארי. (Hypertext Transfer Protocol גרסה 2, טיוטה 17)

חשוב לציין ש-HTTP/2 מרחיב את תקני HTTP הקודמים, ולא מחליף אותם. הסמנטיקה של האפליקציות ב-HTTP זהה, ולא בוצעו שינויים בפונקציונליות או במושגי הליבה שמוצעים, כמו methods של HTTP, קודי סטטוס, מזהי URI ושדות של כותרות. השינויים האלה לא נכללים במאמץ של HTTP/2. עם זאת, למרות שה-API ברמה הגבוהה נשאר ללא שינוי, חשוב להבין איך השינויים ברמה הנמוכה מטפלים במגבלות הביצועים של הפרוטוקולים הקודמים. נערוך סיור קצר בשכבת הפריים הבינארית ובתכונות שלה.

שכבת פריים בינארית

הליבה של כל שיפורי הביצועים של HTTP/2 היא שכבת הפריים הבינארית החדשה, שקובעת את אופן הכמויות של הודעות ה-HTTP וההעברה שלהן בין הלקוח לשרת.

שכבת פריים בינארית של HTTP/2

ה'שכבה' מתייחסת לאפשרות עיצוב שמציגה מנגנון קידוד חדש שעבר אופטימיזציה בין ממשק השקע לבין ה-API הגבוה יותר של HTTP שחשוף לאפליקציות שלנו: הסמנטיקה של HTTP, כמו פעלים, methods וכותרות, לא מושפעת, אבל אופן הקידוד שלהם בזמן ההעברה שונה. בניגוד לפרוטוקול HTTP/1.x של טקסט ללא הצפנה שמופרד באמצעות שורה חדשה, כל התקשורת מסוג HTTP/2 מפוצלת להודעות ולמסגרות קטנות יותר, שכל אחת מהן מקודדת בפורמט בינארי.

כתוצאה מכך, גם הלקוח וגם השרת חייבים להשתמש במנגנון הקידוד הבינארי החדש כדי להבין זה את זה: לקוח HTTP/1.x לא יבין שרת HTTP/2 בלבד ולהפך. למרבה המזל, האפליקציות שלנו לא מודעים לכל השינויים האלה, כי הלקוח והשרת מבצעים את כל פעולות הפריים הנדרשות מטעמנו.

צ'אטים, הודעות ומסגרות

השימוש במנגנון החדש לשמירה על המשתתפים בפריים משנה את אופן חילופי הנתונים בין הלקוח לשרת. כדי לתאר את התהליך, נכיר את המונחים HTTP/2:

  • Stream: זרם דו-כיווני של בייטים בתוך חיבור קיים, שעשוי לכלול הודעה אחת או יותר.
  • הודעה: רצף מלא של פריימים שממופים להודעה או להודעה לוגית של בקשה לוגית.
  • Frame: יחידת התקשורת הקטנה ביותר ב-HTTP/2, כשכל אחת מהן מכילה כותרת מסגרת, שמזהה לכל הפחות את הסטרימינג שאליו שייכת המסגרת.

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

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

סטרימינג, הודעות ומסגרות של HTTP/2

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

ריבוי בקשות ותשובות

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

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

ריבוב של בקשות HTTP/2 ותגובות מתוך חיבור משותף

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

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

  • ניתן לשלב מספר בקשות במקביל בלי לחסום אף אחת מהן.
  • ניתן לשלב מספר תשובות במקביל בלי לחסום אף אחת מהן.
  • להשתמש בחיבור יחיד כדי לשלוח מספר בקשות ותגובות במקביל.
  • הסירו פתרונות זמניים מיותרים מסוג HTTP/1.x (ראו ביצוע אופטימיזציה ל-HTTP/1.x, כמו קבצים עם שרשורים, תמונות sprite ופיצול דומיינים).
  • קיצור זמני הטעינה של דפים באמצעות ביטול זמן אחזור מיותר ושיפור ניצול קיבולת הרשת הזמינה.
  • ועוד הרבה יותר...

שכבת הפריים הבינארית החדשה ב-HTTP/2 פותרת את בעיית החסימה של החלק העליון (head-of-line) שנמצאה ב-HTTP/1.x ומבטלת את הצורך בחיבורים מרובים כדי לאפשר עיבוד ושליחה מקבילים של בקשות ותגובות. כתוצאה מכך, הפריסה של האפליקציות שלנו מהירה, פשוטה וזולה יותר.

תעדוף של שידורים חיים

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

  • אפשר להקצות לכל שידור חי משקל של מספר שלם בין 1 ל-256.
  • לכל שידור יכולה להיות תלות מפורשת בסטרימינג אחר.

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

יחסי תלות ומשקלים של שידור HTTP/2

יחסי התלות של זרם ב-HTTP/2 מוצהרים באמצעות הפניה למזהה הייחודי של זרם אחר כהורה. אם לא כוללים את המזהה, נאמר שהזרם תלוי ב'זרם הבסיס'. הצהרה על תלות של מקור נתונים מציינת שאם אפשר, צריך להקצות משאבים לזרם ההורה לפני יחסי התלות שלו. במילים אחרות, "עליכם לעבד את תשובה ד' ולמסור אותה לפני תשובה ג'".

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

  1. סיכום כל המשקולות: 4 + 12 = 16
  2. צריך לחלק את כל המשקל של השידור במשקל הכולל: A = 12/16, B = 4/16

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

  1. זרם א' ו-B לא מציינים תלות הורה ונאמרים שהם תלויים ב'זרם השורש' המרומז; ל-A יש משקל 12 ול-B יש משקל 4. לכן, על סמך משקלים פרופורציונליים: מקור נתונים ב' צריך לקבל שליש מהמשאבים שהוקצו לשידור א'.
  2. שידור ד' תלוי במקור הנתונים ברמה הבסיסית (root); ג' תלוי ב-D. לכן, השדה D צריך לקבל את הקצאה מלאה של המשאבים לפני C. למשקלים אין השפעה כי התלות של ג' מייצגת העדפה חזקה יותר.
  3. שידור ד' צריך לקבל הקצאה מלאה של משאבים לפני ג'. ג' צריך לקבל הקצאה מלאה של המשאבים לפני שלב א' וב'. מקור ב' צריך לקבל שליש מהמשאבים שהוקצו לשידור א'.
  4. מקורות ד' צריכים לקבל הקצאה מלאה של משאבים לפני E ו-C; E ו-C צריכים לקבל הקצאה שוויונית לפני A ו-B; A ו-B צריכים לקבל הקצאה פרופורציונלית בהתאם למשקל שלהם.

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

חיבור אחד לכל מקור

כשקיים מנגנון בינארי חדש לפריים, HTTP/2 לא צריך יותר חיבורי TCP מרובים במקביל לשידורי Multiplex. כל שידור מחולק לפריים רבים, שאפשר לשלב ביניהם ולתעדף אותם. כתוצאה מכך, כל חיבורי HTTP/2 הם קבועים, ונדרש רק חיבור אחד לכל מקור, ולכן יש מספר יתרונות לביצועים.

גם ל-SPDY וגם ל-HTTP/2, התכונה הקטלנית היא ריבוב שרירותי בערוץ יחיד עם בקרת צפיפות. זה מדהים אותי כמה זה חשוב ועד כמה זה עובד. אחד המדדים הנהדרים בעניין הזה הוא החלק של החיבורים שנוצרו עם טרנזקציית HTTP אחת בלבד (וכך אנחנו מוציאים את כל התקורה על העסקה הזו). בשביל HTTP/1, 74% מהחיבורים הפעילים שלנו כוללים רק טרנזקציה אחת – חיבורים קבועים לא שימושיים בדיוק כמו שכולנו רוצים. אבל ב-HTTP/2 המספר צונח ל-25%. זה יתרון אדיר לצמצום התקורה. (HTTP/2 הוא Live ב-Firefox, פטריק מקמנוס)

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

בקרת זרימה

בקרת זרימה היא מנגנון שמונע מהשולח להציף את המקבל בנתונים שהוא לא רוצה או לא יכול לעבד: יכול להיות שהוא עסוק, בעומס כבד, או שהוא מוכן להקצות כמות קבועה של משאבים לשידור מסוים בלבד. לדוגמה, יכול להיות שהלקוח ביקש סטרימינג גדול של וידאו עם עדיפות גבוהה, אבל המשתמש השהה את הסרטון ועכשיו הלקוח רוצה להשהות או לווסת את ההעברה שלו מהשרת כדי למנוע אחזור ואגירה של נתונים מיותרים. לחלופין, לשרת Proxy יש חיבורים מהירים ב-downstream וב-upstream והוא רוצה לשלוט על המהירות שבה הוא מספק נתונים בהתאם למהירות ה-upstream לשלוט בשימוש במשאבים וכן הלאה.

האם הדרישות שלמעלה מזכירות לכם את הבקרה על תהליכי TCP? הן אמורות להיות זהות, כי הבעיה בפועל זהה (ראו בקרת זרימה). עם זאת, מכיוון ששידורי ה-HTTP/2 עוברים ריבוי עיבודים בתוך חיבור TCP יחיד, ולכן הבקרה של זרימת TCP לא מפורטת מספיק ולא מספקת את ממשקי ה-API הנדרשים ברמת האפליקציה כדי לווסת את השליחה של שידורים נפרדים. כדי לפתור את הבעיה, HTTP/2 מספק קבוצה של אבני בניין פשוטות שמאפשרות ללקוח ולשרת להטמיע בקרת זרימה משל עצמם ברמת הסטרימינג וברמת החיבור:

  • בקרת הזרימה היא כיוונית. כל מקלט יכול לבחור להגדיר כל גודל חלון שהוא רוצה לכל שידור ולכל החיבור.
  • בקרת הזרימה מבוססת על האשראי. כל מקלט מפרסם את החיבור הראשוני שלו ואת חלון הבקרה על הזרימה בסטרימינג (בבייטים), והוא מוקטן בכל פעם שהשולח פולט פריים של DATA וגדל באמצעות מסגרת WINDOW_UPDATE שהנמען שולח.
  • לא ניתן להשבית את בקרת הזרימה. כשנוצר חיבור HTTP/2, המסגרות SETTINGS של הלקוח והשרת מחליפים אותן, שקובעים את הגדלים של החלונות של בקרת הזרימה בשני הכיוונים. ערך ברירת המחדל של החלון של בקרת הזרימה מוגדר ל-65,535 בייטים, אבל המקבל יכול להגדיר גודל חלון מקסימלי גדול (2^31-1 בייטים) ולשמור עליו על ידי שליחת מסגרת WINDOW_UPDATE בכל פעם שמתקבלים נתונים.
  • בקרת הזרימה היא שלב אחרי שלב, ולא מקצה לקצה. כלומר, מתווך יכול להשתמש בו כדי לשלוט בשימוש במשאבים ולהטמיע מנגנונים להקצאת משאבים על סמך קריטריונים והיוריסטיקה שלו.

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

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

דחיפת שרת

תכונה חדשה ועוצמתית נוספת של HTTP/2 היא היכולת של השרת לשלוח מספר תגובות לבקשת לקוח אחת. כלומר, בנוסף לתגובה לבקשה המקורית, השרת יכול לדחוף משאבים נוספים ללקוח (איור 12-5), בלי שהלקוח יצטרך לבקש כל אחד מהם באופן מפורש.

השרת יוזם שידורים חדשים (הבטחה) למשאבי דחיפה

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

למעשה, אם אי פעם הוספתם ל-CSS, JavaScript או כל נכס אחר באמצעות URI של נתונים (ראו הטמעת משאבים), כבר יש לכם ניסיון מעשי עם דחיפת שרת. על ידי הוספה ידנית של המשאב למסמך, אנחנו למעשה דוחפים את המשאב ללקוח בלי להמתין עד שהלקוח יבקש אותו. באמצעות HTTP/2 אפשר להשיג את אותן תוצאות, אבל עם יתרונות נוספים בביצועים. משאבי Push יכולים להיות:

  • נשמר במטמון על ידי הלקוח
  • שימוש חוזר בדפים שונים
  • צפייה בכמה משחקים בו-זמנית לצד משאבים אחרים
  • לפי תעדוף של השרת
  • נדחתה על ידי הלקוח

PUSH_PROMISE 101

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

אחרי שהלקוח מקבל פריים של PUSH_PROMISE, יש לו אפשרות לדחות את השידור (באמצעות פריים של RST_STREAM) אם הוא רוצה. (יכול להיות שזה יקרה כי המשאב כבר נמצא במטמון.) זהו שיפור חשוב לעומת HTTP/1.x. לעומת זאת, השימוש בהטבעה של משאב, שהיא 'אופטימיזציה' פופולרית ב-HTTP/1.x, מקביל ל'דחיפה מאולצת': הלקוח לא יכול לבטל את ההסכמה, לבטל אותו או לעבד את המשאב שהוזן בנפרד.

עם HTTP/2 הלקוח נשאר בשליטה מלאה על אופן השימוש בדחיפת השרת. הלקוח יכול להגביל את מספר השידורים שנדחפים בו-זמנית; לשנות את חלון בקרת הזרימה הראשוני כדי לקבוע כמה נתונים נדחפים כשהשידור נפתח לראשונה או להשבית לגמרי את דחיפת השרת. ההעדפות האלה מועברות באמצעות הפריימים SETTINGS בתחילת חיבור HTTP/2, ואפשר לעדכן אותן בכל שלב.

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

דחיסת כותרת

כל העברת HTTP כוללת קבוצה של כותרות שמתארות את המשאב שהועבר ואת המאפיינים שלו. ב-HTTP/1.x, המטא-נתונים האלה תמיד נשלחים כטקסט פשוט ומוסיפים 500-800 בייטים של תקורה לכל העברה, ולפעמים גם קילו-בייט יותר אם משתמשים בקובצי cookie של HTTP. (למידע נוסף, אפשר לקרוא את המאמר Measurement and Controlling Protocol). כדי לצמצם את התקורה הזו ולשפר את הביצועים, HTTP/2 דוחס את המטא-נתונים של כותרות הבקשות והתשובות באמצעות פורמט הדחיסה HPACK, שמשתמש בשתי שיטות פשוטות ועוצמתיות:

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

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

HPACK: דחיסת כותרות עבור HTTP/2

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

האבטחה והביצועים של HPACK

הגרסאות המוקדמות של HTTP/2 ו-SPDY השתמשו ב-zlib, עם מילון מותאם אישית, כדי לדחוס את כל כותרות ה-HTTP. כך הופחתו ב-85% עד 88% את הגודל של נתוני הכותרת שהועברו, ושיפור משמעותי בזמן האחזור של זמן הטעינה של הדף:

בקישור ה-DSL ברוחב הפס הנמוך יותר, שבו הקישור להעלאה הוא רק 375Kbps, דחיסת הכותרת בבקשה הובילה לשיפורים משמעותיים בזמן הטעינה של הדף באתרים מסוימים (במילים אחרות, אלה ששלחו מספר גדול של בקשות למשאבים). זיהינו ירידה של 45-1,142 אלפיות השנייה בזמן טעינת הדף, פשוט בגלל דחיסת הכותרת. (סקירה מפורטת של SPDY, chromium.org)

עם זאת, בקיץ 2012 פורסמה מתקפת אבטחה מסוג "CRIME" כנגד אלגוריתמים לדחיסה של TLS (אבטחת שכבת התעבורה) ו-SPDY, ועלולה לגרום לפריצה למחשבים. כתוצאה מכך, אלגוריתם הדחיסה zlib הוחלף ב-HPACK, שתוכנן במיוחד לטפל בבעיות אבטחה שזוהו, להיות יעיל ופשוט להטמעה נכונה, וכמובן לאפשר דחיסה טובה של מטא-נתונים של כותרות HTTP.

לפרטים מלאים על אלגוריתם הדחיסה HPACK, ראו IETF HPACK – דחיסת כותרת עבור HTTP/2.

קריאה נוספת