უკან დაბრუნება
Go 1.27 ავტომატურად აცლის წაუკითხავ HTTP/1 პასუხის body-ებს კავშირის უფრო სწრაფი ხელახალი გამოყენებისთვის
SiTech AI Team3 წთ. საკითხავი

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-ს ჰენდშეიკს ერთი სრული მიმოსვლის დრო (RTT) სჭირდება, TLS 1.2-ის ჰენდშეიკს 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-ს მომავალ ვერსიებში შეიძლება შეიცვალოს, როცა სხვა შესაძლო გაუმჯობესებები გამოაშკარავდება.

ბენჩმარკის შედეგები და რეკომენდაცია

გაუმჯობესება მნიშვნელოვანია. სატესტო პროგრამას, რომელიც მარტივ სტატუსის შემოწმების ფუნქციას 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

SSiTech

SiTech — AI-გაძლიერებული ვებ დეველოპმენტი

ვქმნით სწრაფ, თანამედროვე ვებსაიტებს და AI-ს ვაერთიანებთ ქართული ბიზნესებისთვის. გაქვთ პროექტი ან კითხვა? სიამოვნებით დაგეხმარებით.