מבוא ל-HTTP/2

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

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

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

מדוע לא HTTP/1.2?

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

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

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

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

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

זמן קצר לאחר ההכרזה הראשונית, מייק בלשה ורוברטו פאון, שניהם מהנדסי תוכנה ב-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: פורסמו HTTP/2 draft-17 ו-HPACK draft-12
  • אוגוסט 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 מאז גרסה 6 של Chrome, אבל מכיוון שרוב היתרונות קיימים ב-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 היה פרוטוקול בשורה אחת לאתחול האינטרנט ברחבי העולם; 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 זהה, ולא בוצעו שינויים בפונקציונליות או במושגים המרכזיים כמו שיטות HTTP, קודי סטטוס, מזהי URI ושדות הכותרת. השינויים האלה לא היו כלולים בכלל במאמץ HTTP/2. עם זאת, ה-API ברמה הגבוהה נשאר ללא שינוי, אבל חשוב להבין איך השינויים ברמה הנמוכה מטפלים במגבלות הביצועים של הפרוטוקולים הקודמים. נצא לסיור קצר בנושא שכבת הפריים הבינארית והתכונות שלה.

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

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

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

ה "שכבה" מתייחסת לבחירת עיצוב שמאפשרת להציג מנגנון קידוד חדש שעבר אופטימיזציה בין ממשק ה-socket לממשק ה-API הגבוה יותר של HTTP שחשוף לאפליקציות שלנו: הסמנטיקה של HTTP, כמו פעלים, שיטות וכותרות, לא מושפעת, אבל אופן הקידוד שלהם בזמן ההעברה שונה. בשונה מפרוטוקול 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, כמו קבצים משורשרים, sprites של תמונות ופיצול דומיינים).
  • קיצור זמני הטעינה של דפים על ידי ביטול זמן האחזור המיותר ושיפור הניצול של קיבולת הרשת הזמינה.
  • ועוד הרבה יותר...

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

תעדוף השידור

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

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

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

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

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

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

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

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

  1. השידור A ו-B לא מציינים תלות הורה ואומרים שהם תלויים ב "זרם הבסיס" המשתמע. ל-A יש משקל של 12 ול-B יש משקל של 4. כך, על סמך משקל יחסי: מקור ב' צריך לקבל שליש מהמשאבים שמוקצים לשידור א'.
  2. שידור ד' תלוי במקור הנתונים הבסיסי (root), וה-C תלוי בשידור ד'. לכן, D אמורה לקבל הקצאה מלאה של המשאבים לפני שפת C. השקלולים הם לא רציפים כי התלות ב-C מעידה על העדפה טובה יותר.
  3. שידור ד' אמור לקבל את ההקצאה המלאה של המשאבים לפני השידור ג'. לעומת זאת, מקור ב' אמור להקצות את כל המשאבים לפני השידור א', שליש מהמשאבים שמוקצים לשידור א'.
  4. זרם D אמור לקבל הקצאה מלאה של המשאבים לפני 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 פעיל ב-Firefox, Patrick McManus)

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

בקרת זרימה

בקרת זרימה היא מנגנון שמונע מהשולח להציף את המקבל בנתונים שהוא לא רוצה או יכול לעבד: המקבל יכול להיות עסוק, בעומס כבד או רק מוכן להקצות כמות קבועה של משאבים לזרם מסוים. לדוגמה, ייתכן שהלקוח ביקש זרם וידאו גדול עם עדיפות גבוהה, אבל המשתמש השהה את הסרטון ועכשיו הלקוח רוצה להשהות או לווסת את ההעברה מהשרת כדי למנוע אחזור ואגירה של נתונים מיותרים. לחלופין, לשרת proxy עשויים להיות חיבורים מהירים ב-downstream וחיבורים איטיים ב-upstream. בדומה לכך, הוא רוצה לווסת באיזו מהירות ה-downstream מספק נתונים בהתאם למהירות של 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), בלי שהלקוח יצטרך לבקש כל אחד מהם באופן מפורש.

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

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

למעשה, אם השתמשתם פעם ב-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 בייטים של תקורה בכל העברה, ולפעמים גם קילובייט (KB) במקרה שמשתמשים בקובצי cookie של HTTP. (למידע נוסף, ראו Measurement and Controlling Protocol Overhead). כדי לצמצם את התקורה ולשפר את הביצועים, HTTP/2 דוחס את המטא-נתונים של כותרת הבקשה והתגובה באמצעות פורמט דחיסת HPACK שמשתמש בשתי שיטות פשוטות אבל מתקדמות:

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

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

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

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

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

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

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

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

אפשר לקרוא פרטים מלאים על אלגוריתם הדחיסה HPACK במאמר IETF HPACK - Header Compression for HTTP/2.

קריאה נוספת