
Go 1.27 ավտոմատ ջնջում է չկարդացված HTTP/1 պատասխանի body-ները ավելի արագ կրկնակի օգտագործման համար
Go 1.27-ը բարելավում է HTTP/1 կապի կրկնակի օգտագործումը՝ ավտոմատ ջնջելով չկարդացված պատասխանի body-ները: index.golang.org-ի դեմ չափումը ցույց է տալիս, որ նույն ծրագիրը Go 1.26-ի հետ համեմատած գրեթե հինգ անգամ արագ է աշխատում:
Չկարդացված պատասխանի body-ները խանգարում էին HTTP/1 կապի կրկնակի օգտագործմանը
Go-ի net/http փաթեթն աջակցում է HTTP/1.0-ին, HTTP/1.1-ին և HTTP/2-ին, որոնք աշխատում են TCP-ով, իսկ HTTPS հասցեների դեպքում՝ սովորական TLS-ով: Նոր կապի հաստատումն թանկ է: TCP-ի handshake-ի համար պահանջվում է մեկ ամբողջ երթ ժամանակը (RTT), TLS 1.2-ի handshake-ին՝ 2 RTT, և միայն 3 RTT-ից հետո կարելի է առաջին HTTP հարցումն ուղարկել: Տարածաշրջանների միջև մոնոկողմանի ուշացումը հաճախ հասնում է կամ գերազանցում է 50 միլիվայրկյանը, ուստի կապի հաստատումը կարող է շատ թանկ լինել, քան փոքր պատասխանի տրամադրումը:
Այդ պատճառով HTTP հաճախորդներն ու սերվերները փորձում են նորից օգտագործել առկա կապերը: HTTP/1.1-ում կապերը լռելյայն մշտական են, թեև պատասխանները ուղարկվում են մեկ ու նույն բայթային հոսքում իրար հետևից: Դա նշանակում է, որ net/http-ն հաջորդ պատասխանի սկիզբը միայն այդ դեպքում կտեսնի, երբ ընթացիկ body-ն ամբողջությամբ կընթերցվի, որը որոշում են Content-Length կամ Transfer-Encoding վերնագրերը:
Go-ի բլոգ գրումուներում Նիկոլաս Հուսինին և Դեմիեն Նիլին նշում են տարածված սխալը: ֆունկցիան, որը կանչում է http.Get, defer-ով կատարում է resp.Body.Close և վերադարձնում է միայն կարգավիճակի կոդը, body-ն ամբողջությամբ չի կարդում: Go 1.26-ի դեպքում, եթե Close-ից հետո մնած է չկարդացված body, net/http-ն կապը թողնում է և հաջորդ հարցման համար բացում նորը, ինչպես էլ է վերցնում հաստատման ծախսը: Խնդիրը վերաբերում է նաև այն ծրագրերին, որոնք body-ն կարդում են, բայց մինչև վերջ թողնում են փոքր մասը: Օրինակ՝ encoding/json.Decoder-ը կտրում է կարդալը JSON օբյեկտի փակման բրակետում, ուստի սերվերը, որը պատասխանն ավարտում է newline-ով, այդ բայթը թողնում է չկարդացված, և կապը այլևս չի կարող օգտագործվել: HTTP/2-ն այս խնդիրը չունի, քանի որ այն մուլտիպլեքսավորում է կադրերը մեկ կապում և պատասխանները խիստ հաջորդական չի ուղարկում:
Go 1.27 body-ներն ավտոմատ ջնջում է
Go 1.27-ից սկսած net/http.Transport-ն դա ավտոմատ է լուծում: այն ասինխրոն ջնջում է չկարդացված HTTP/1 պատասխանի body-ն 50 միլիվայրկյան կամ 256 կիլոբայթ սահմանում՝ կախված նրանից, թե որ սահմանը հասնում է առաջինը: Եթե ջնջումն ավարտվում է հաջորդ հարցումն ուղարկելուց առաջ, կապը նորից օգտագործվում է: Հակառակ դեպքում ստեղծվում է նոր կապ, իսկ հինը հեռանում է անօգուտ կապերի պուլ և կարող է ապագա հարցման համար էլ օգտագործվել: Իմպլեմենտացիան ընդհանրապես չի ջնջում, երբ պատասխանը ունի Connection: close վերնագիր, քանի որ այդ դեպքում ջնջումը դառնային դատարկ աշխատանք:
Հեղինակները նշում են, որ այս պահվածքը կարող է փոխվել Go-ի ապագա տարեթվերում, երբ հայտնի դառնայն այլ հնավոր բարելավումներ:
Benchmark-ի արդյունքները և խորհուրդը
Բարելավումն էական է: Տեստային ծրագիրը, որը պարզ կարգավիճակի ստուգման ֆունկցիան 100 անգամ է կանչում index.golang.org-ին, աշխարհով մեկ տարածված, TLS 1.3-ն աջակցող կայքի համար, Go 1.26-ի վրա պահանջում է մոտ 710 միլիվայրկյան, իսկ Go 1.27-ի վրա՝ միայն մոտ 150 միլիվայրկյան, գրեթե հինգ անգամ արագ:
Նոր վարքագծով Go-ի թիմը խորհուրդ է տալիս մշակողներին HTTP պատասխանի body-ները պարզապես փակել, երբ դրանք այլևս պետք չեն, հայտնի defer resp.Body.Close() շաբլոնն օգտագործելով: Ձեռքով գրված էջի հավաքման մեթոդները, որոնց շատ մշակող տարիներ է օգտագործել, օրինակ՝ body-ների ջնջումը io.Copy-ով, ջնջված բայթերի սահմանափակումը կամ ջնջման timeout-ի հետ մրցությունը, այլևս պետք չէ: Հեղինակները դա համարում են Go-ի պարզության փիլիսոփայության մասը: թող կոդը մաքուր և պարզ մնա, բարդ մասը լուծի ձեր փոխարեն net/http-ը:
Աղբյուրներ: Hacker News
SiTech — AI-ով հզորացված վեբ մշակում
Ստեղծում ենք արագ ու ժամանակակից կայքեր և AI-ը ներդնում իրական բիզնես գործընթացներում։ Ունե՞ք նախագիծ կամ հարց։ Ուրախ կլինենք օգնել։