Cách thức mà Multiplexing thay đổi HTTP API của bạn - QuânSysAd's Blog

15 tháng 10 2019

Cách thức mà Multiplexing thay đổi HTTP API của bạn

Cách thức mà Multiplexing thay đổi HTTP API của bạn
Khi tôi lần đầu học về SPDY, tôi đã rất thích nó vì một số lý do, nhưng dẫn đầu các lý do đó là các ảnh hưởng tiềm năng lên API mà sử dụng HTTP.
Trong khi HTTP rất tốt cho việc xây dựng API bởi một số lý do, một trong các vấn đề mà nhiều API gặp khó khăn là một vấn đề cốt lõi, đó là hầu hết các API hữu ích phải là các API đủ nhỏ, đủ chi tiết, vì điều đó nền bạn chỉ trả lại được một số thông tin mà client cần, nhưng điều đó dẫn tới client cần tạo rất nhiều request để lấy được mọi thứ chúng cần.
Ví dụ, nếu API của bạn là về widgets, bạn có lẽ muốn đưa ra URL cho từng widget, để clients có thể chỉ cần access widgets họ quan tâm.
GET /widget/5382894223 HTTP/1.1
Accept: application/widget+json
Host: api.widgets.org
Accept-Encoding: gzip, deflate
User-Agent: BobsWidgetClient/1.5
Tuy nhiên, trong HTTP/1, các request là rất đắt đỏ, và API được thiết kế như vậy sẽ nhanh chóng đi vào vấn đề thực tế, nếu client cần tìm khoảng 40 widgets cụ thể, thì 40 requests cần phải được tạo ra. Bên cạnh các overhead (phần thông tin tiêu để) của việc truyền tất cả các request đó (mỗi request khoảng 150 byte, như trên đây là hầu hết khoảng 6K), client sẽ khó khăn khi quyết định làm sao để truyền chúng đi:
Một là chúng có thể sử dụng 1 connection cho tất cả 40 request. Nói một cách thực tế, điều đó có nghĩa rằng thời gian tối thiểu để xử lý tất cả các requests là 40 round trip giữa client và server (cộng thêm một ít thời gian để thiết lập kết nối). Ngay cả khi sử dụng HTTP pipelining (mà thường sẽ khó khăn), bất cứ delay nào trong việc tạo ra chỉ một responses (phản hồi) sẽ block tất cả những responses đằng sau nó.
Hai là có thể sử dụng một kết nối mỗi request, như vậy là không có cái nào block cái khác. Tuy nhiên, điều đó có nghĩ rằng client sẽ sử dụng 40 connection, mà sẽ tốn thời gian để open, tiêu tốn tài nguyên, và sẽ rất thường xuyên gây ra vấn đề TCP congestion, điều này sẽ tăng thêm mà không giảm đi một lượng lớn thời gian trễ và sự không hiệu quả do phải truyền lại thông tin.
Ba là có thể sử dụng số lượng connection ít hơn để san tải, làm giảm (chứ không triệt tiêu) nguy cơ congestion mà không yêu cầu thêm requests phải được hoàn thành theo tuần tự. Đây là sự đáp ứng về vị trí một cách hiệu quả, sẽ không cần tạo ra 40 round trip, nhưng sẽ cần tối thiểu 40 chia cho số lượng connection được sử dụng. Như vậy, thời gian trễ của một phản hồi sẽ vẫn block các phản hồi đằng sau đó trong cùng connection - có nghĩa rằng performance sẽ biến động dựa vào số lượng các nhân tố. Và sự biến động trong performancethông thường sẽ tồi tệ hơn consistent delay (trễ nhất quản, trễ đều).
Một vài API sẽ cố gắng tránh các tùy chọn trên bằng cách cung cấp URL mà clients có thể GET hoặc POST một “batch” request tới:
GET /widgets/5382894223,35223231,534232313,5231332435 HTTP/1.1
Accept: application/widgets+json
Host: api.widgets.org
Accept-Encoding: gzip, deflate
User-Agent: BobsWidgetClient/1.5
Tuy nhiên, cách này sẽ có một số vấn đề. Cả service và client đều cần phải hiểu endpoint mới, và định dạng list-based mới, làm bloating (phình to) API, đặc biệt nếu có nhiều loại hoặc tài nguyên mà cần xử lý tương tự nhau. Hơn nữa, cách tiếp cận này ảnh hưởng nghiêm trong tới sự hiệu quả của cache, tạo ra thêm tải cho server và tạo ra trễ client-perceived (client nhận ra được).
Nó cũng đã là một nhân tố dẫn tới query languages như GraphQL được phát triển, nếu bạn có thể mô tả một cách chính sác những gì bạn muốn tới server theo một định dạng hiệu quả, nó có thể phản hồi chỉ với những thông tin nào bạn muốn.
Sự lựa chọn khó khăn này cũng gặp phải bởi trình duyệt Web khi sử dụng HTTP/1 để request tất cả các tài nguyên khác nhau của Web page, và dẫm tới thiết kế của SPDY, mà xử lý các input như HTTP/2. HTTP/2 về cơ bản khác với HTTP/1 ở vài điểm, nhưng multiplexing - là khả năng có nhiều request và responses cùng lúc (in flight) trong một connection là đặc điểm lớn nhất.
Multiplexing kết hợp từng requests/response và giao tiếp bằng stream ID, và sử dụng cái đó (stream ID) để đảm bảo rằng đoạn dữ liệu của từng cặp không bị lẫn lộn khi được truyền gửi, bất kể chúng được interleaved. Nó có nghĩa rằng bạn có thể chỉ cần dùng một connection mà không bị mất hiệu năng do Head of Line Blocking Bị chặn đầu
Phần trên có lẽ không phải là mới với hầu hết bạn đọc ở đây, nhưng tôi nghi ngờ sự liên đới của thiết kế API không hoàn toàn rõ ràng. Có tin cho rằng, HTTP/2 (và sắp tới là HTTP/3) cho phép bạn khai báo một số lượng lớn các request theo cách rất nhỏ gọn.
Để đưa ra một ví dụ, hãy xem script sau, sẽ tạo ra 40 HTTP/2 request đồng thời với nghttp2:
#!/bin/bash

URLS=""
for i in {1..40}
do
    URLS+="https://localhost:8443/widgets/${i}/whatever "
done

nghttp -y --no-dep $URLS
Nếu bạn quan sát bằng Wireshark, bạn sẽ thấy 40 requests này đi ra trong một gói 1440 byte. Đây là các bit có liên quan của text dump (với một vài chi tiết lướt qua và dòng được xuống dòng)
No. 11  Time 0.002228  Source ::1  src port 56623  destination ::1
Protocol HTTP2  Length 1440   Information
Magic, SETTINGS[0],
HEADERS[1]: GET /widgets/1/whatever,
HEADERS[3]: GET /widgets/2/whatever,
HEADERS[5]: GET /widgets/3/whatever,
HEADERS[7]: GET /widgets/4/whatever,
...
HEADERS[75]: GET /widgets/38/whatever,
HEADERS[77]: GET /widgets/39/whatever,
HEADERS[79]: GET /widgets/40/whatever

Frame 11: 1440 bytes on wire (11520 bits),
    1440 bytes captured (11520 bits) on interface 0
Null/Loopback
Internet Protocol Version 6, Src: ::1, Dst: ::1
Transmission Control Protocol
    Source Port: 56623
    Destination Port: 8443
    [Stream index: 0]
    [TCP Segment Len: 1364]
    TCP payload (1364 bytes)
Hầu hết các requests độc lập là khoảng 23 bytes, vì thế bạn sẽ có thể nhét 400 request như vậy vào trong 10 packet đầu tiên (khởi tạo CWND phổ biến nhất) của connection trước bất kỳ các request nào khác cần chờ ACK. Không còn lo lắng về Head of Line blocking, congestion events do multiple connections, hoặc bloat API (làm phình API).
Các phản hồi (responses) cũng trở nên hiệu quả, khi mà server có một phần thông tin response đang có sẵn, nó có thể gửi phản hồi, trái ngược với việc chỉ còn bandwidth mới gửi được. Đặc biệt, nếu có cache giữa server và clients, nó có thể lấp đầy các bandwidth có sẵn trong khi server trả lời các requests chưa được cache - điền này sẽ làm cho hầu hết các tài nguyên có sẵn để phục vụ.
Điều này khá mạnh. Nói không quá, một cách khác để nghĩ về HTTP/2 là nó sẽ như một query language mới - ngôn ngữ này cho phép bạn encode một tập rất phức tạp các requests vào một lượng nhỏ data mà được tối ưu một cách triệt để để truyền đi, trong khi vẫn cho phép các thành phần HTTP tiêu chuẩn - cụ thể là cache để làm việc với các request riêng lẻ.
Bạn có thể nói rằng URL trong ví dụ của tôi là rất giống nhau, không thực tế trong nhiều trường hợp. Điều đó là đúng, nhưng bởi vì do đó là cách mà HPACK header compression hoạt động, nhưng điều đó không phải vấn đề quá lớn, 40 requests cho bất kỳ URL path nào có độ dài như vậy sẽ encode về cùng kích cỡ.
Đó là, tất nhiên, các lý do khác để sử dụng batch endpoints hoặc các query language cụ thể, đặc biệt, nếu một vài requests của bạn có ảnh hưởng phụ mà có thẻ nhận thấy trong request khác bạn đang tạo ra, vì thế thứ tự xử lý của chúng là quan trọng.
Đây cũng là những ngày đầu tiên của HTTP/2 client, trong khi nó được thiết lập tốt trên trình duyệt, nó cũng chỉ bắt đầu sẵn sàng và có thể sử dụng như là thư viện trong nhiều ngôn ngữ (hãy thử libcurl). Server bạn cần áp dụng cũng sẽ cần xem xét kỹ lưỡng để khai thác loại mẫu request này; nhiều người sẽ cần nghĩ lại trước khi chúng có thể xử lý 40 hoặc 400 requests đồng thời trên một connection một cách hiệu quả (nhưng có thể, caching có thể đóng góp rất lớn, như có thể coi các request khác thường như một pool, thay vì chúng tách biệt nhau).
Với tất cả những ghi chú được báo trước trên đây, HTTP/2 sẽ đặt vào các ý tưởng còn lại sự cần thiết phải tối thiểu hóa số lượng requests cho API, giao thức sẽ khiến chúng đủ “cheap” để không còn là vấn đề thực tế cần để ý. Hãy sử dụng và thiết kế HTTP API ở mức độ chi tiết cao để đáp ứng được nhu cầu của các clients.
Credit:

Không có nhận xét nào: