
Go 1.27 מנקה אוטומטית גופי תשובות HTTP/1 שלא נקראו לטובת שימוש חוזר מהיר יותר בחיבורים
Go 1.27 משפר את שימוש חוזר בחיבורי HTTP/1, על ידי ניקוי אוטומטי של גופי תשובות שלא נקראו. בנצ'מרק מול index.golang.org מראה שאותה תוכנה רצה כמעט פי חמש מהר יותר בהשוואה ל-Go 1.26.
גופי תשובות שלא נקראו שברו שימוש חוזר בחיבורי HTTP/1
החבילה net/http של Go תומכת ב-HTTP/1.0, HTTP/1.1 ו-HTTP/2, אשר פועלים מעל TCP, וכן ב-HTTPS בכתובות רגילות מעל TLS. יצירת חיבור חדש יקרה: לחיבור TCP נדרש זמן נסיעה מלא אחד (RTT), לחיבור TLS 1.2 נדרשים 2 RTT, ורק לאחר 3 RTT בסך הכל ניתן לשלוח את בקשת ה-HTTP הראשונה. השהיה חד-כיוונית בין אזורים מגיעה לעיתים קרובות ל-50 מילישניות או יותר, ולכן יצירת חיבור עלולה להיות יקרה בהרבה מאשר מסירת תשובה קטנה.
לכן לקוחות ושרתי HTTP מנסים לעשות שימוש חוזר בחיבורים קיימים. ב-HTTP/1.1 חיבורים הם קבועים כברירת מחדל, אך תשובות נשלחות זו אחר זו על אותו תם של בתים. משמעות הדבר היא ש-net/http ימצא את תחילת התשובה הבאה רק כאשר הגוף הנוכחי ייקרא עד סופו, כפי שנקבע על ידי כותרת Content-Length או Transfer-Encoding.
בפוסט הבלוג של Go, ניקולאס הוסיין ודמיאן נילי מציינים טעות נפוצה: פונקציה שקוראת ל-http.Get, מבצעת defer של resp.Body.Close ומחזירה רק את קוד הסטטוס, ולא קוראת את הגוף בכלל. עם Go 1.26, אם נשאר גוף שלא נקרא בעת Close, net/http סוגר את החיבור ופותח חיבור חדש לבקשה הבאה, ובכך עדיין משלם את עלות ההקמה. הבעיה נוגעת גם לתוכניות שכן קוראות את הגוף, אך לא עד הסוף. לדוגמה, encoding/json.Decoder עוצר את הקריאה בסגירת הסוגריים של אובייקט ה-JSON, ולכן שרת שמסיים תשובה ב-newline משאיר את הבת הזה לא נקרא והחיבור כבר לא ניתן לשימוש חוזר. ל-HTTP/2 אין בעיה זו, כי הוא ממקספל מסגרות על חיבור אחד ולא שולח תשובות זו אחר זו באופן קפדני.
Go 1.27 מנקה גופים אוטומטית
החל מ-Go 1.27, net/http.Transport מטפל בזה אוטומטית. הוא מנקה א-סינכרונית גוף תשובת HTTP/1 שלא נקרא עד 50 מילישניות או 256 קילובייט, לפי הסף שמושג ראשון. אם הניקוי מסתיים לפני שליחת הבקשה הבאה, החיבור נשמר לשימוש חוזר; אחרת נוצר חיבור חדש, והישן נכנס למאגר חיבורים לא פעילים ועשוי לשמש שוב לבקשה עתידית. היישום לא מבצע ניקוי כלל כאשר לתשובה יש את כותרת Connection: close, כי במקרה זה הניקוי היה עבודה מייתרת.
המחברים מציינים שהתנהגות זו עשויה להשתנות בגרסאות עתידיות של Go, כאשר שיפורים אפשריים נוספים ייחשפו.
תוצאות הבנצ'מרק והמלצה
השיפור משמעותי. תוכנית בדיקה שקוראת לפונקציית בדיקת סטטוס פשוטה 100 פעמים מול index.golang.org, אתר מפוזר עולמית שתומך ב-TLS 1.3, דורשת כ-710 מילישניות עם Go 1.26, ורק כ-150 מילישניות עם Go 1.27, כמעט פי חמש מהר יותר.
עם ההתנהגות החדשה, צוות Go ממליץ למפתחים לסגור גופי תשובות HTTP פשוט כשהם כבר לא נחוצים, באמצעות התבנית המוכרת defer resp.Body.Close(). טיפים לניקוי גופים שנכתבו ביד, שרבים מהמפתחים השתמשו בהם במשך שנים, כגון ניקוי גופים באמצעות io.Copy, הגבלת בתים שנקראו או תחרות עם timeout לניקוי, כבר לא נחוצים. המחברים מציגים זאת כחלק מפילוסופיית הפשטות של Go: שהקוד יישאר נקי ופשוט, והחלק המורכב יטופל על ידי net/http במקומכם.
מקורות: Hacker News
SiTech — פיתוח אתרים בכוח ה-AI
אנחנו בונים אתרים מהירים ומודרניים ומשלבים AI בתהליכי עבודה אמיתיים. יש לכם פרויקט או שאלה? נשמח לעזור.