The Best Request Is No Request
Hơn một thập kỷ qua, tối ưu hóa hiệu năng web được kiểm soát bởi một quy tắc không thể bàn cãi: "The best request is no request". Đây là một quy tắc rất đơn giản và dễ hiểu. Nhưng mọi thứ đã thay đổi kể từ khi HTTP/2 ra đời. Được thiết kế cho ứng dụng web hiện đại, HTTP/2 hiệu quả hơn trong việc ...
Hơn một thập kỷ qua, tối ưu hóa hiệu năng web được kiểm soát bởi một quy tắc không thể bàn cãi: "The best request is no request". Đây là một quy tắc rất đơn giản và dễ hiểu. Nhưng mọi thứ đã thay đổi kể từ khi HTTP/2 ra đời. Được thiết kế cho ứng dụng web hiện đại, HTTP/2 hiệu quả hơn trong việc đáp ứng một lượng lớn request so với người tiền nhiệm. Câu hỏi đặt ra là: liệu quy tắc giảm thiểu request vẫn còn nguyên giá trị?
Điều gì đã được thay đổi với HTTP/2
Để hiểu khác biệt giữa HTTP/2 và HTTP1, ta cần biết về HTTP/1. HTTP được xây dựng dựa trên TCP. Dù TCP có khả năng truyền tải dữ liệu tin cậy, nhưng cách HTTP/1 tận dụng TCP lại không thực sự hiệu quả. Mỗi một lần request tài nguyên đều cần một kết nối TCP mới. Và với mỗi kết nối TCP lại cần đồng bộ hóa giữa client và server, dẫn đến việc delay khi trình duyệt bắt đầu khởi tạo một kết nối (connection).
Những cập nhật trong HTTP/1.1 nhằm mục đích khắc phục những hạn chế này. Client giờ đây có thể sử dụng một kết nối TCP cho nhiều resource, nhưng vẫn phải download tuần tự.
Bên cạnh đó, hầu hết trình duyệt giới hạn số kết nối TCP song song được mở cho mỗi tên miền ở con số khá thấp. Ngay cả tối ưu như thế, HTTP/1.1 vẫn không phù hợp với số lượng resource của website ngày này. Bởi vậy mới nói "The best request is no request" (request tối ưu nhất là không có request nào cả). Kết nối TCP tốn chi phí và thời gian. Đó chính là lý do chúng ta sử dụng những kỹ thuật tối ưu tài nguyên như image sprites ... để tránh tạo kết nối mới và tái sử dụng những tài nguyên hiện có.
HTTP/2 về cơ bản khác hẳn HTTP/1.1. HTTP/2 sử dụng một kết nối TCP duy nhất và cho phép nhiều tài nguyên được download song song hơn người tiền nhiệm. Tưởng tượng kết nối TCP này như một đường hầm mà dữ liệu được gửi đi thông qua các làn. Ở client, tất cả package được lắp ráp lại thành nguyên gốc.
Tất cả các kết nối sử dụng cùng một luồng, vậy nên chúng cùng chia sẻ băng thông. Tùy thuộc vào số lượng tài nguyên có thể dẫn đến việc delay do kết nối có băng thông thấp.
Điều này cũng có nghĩa là việc ưu tiên tài nguyên không dễ thực hiện như ở HTTP/1.1 (thứ tự tài nguyên trong document ảnh hưởng đến việc cái nào sẽ được download truowccs). Với HTTP/2, mọi thứ xảy ra vào cùng thời điểm. Trong spec của HTTP/2 có thông tin về việc ưu tiên luồng, nhưng tại thời điểm bài viết, việc kiểm soát thứ tự ưu tiên vẫn nằm ngoài tầm với của developer.
The best request is no request: cherry-picking
Vậy phải làm gì khi chưa thể kiểm soát việc ưu tiên tài nguyên? Về việc lãng phí băng thông? Hãy nhớ lại nguyên tắc đầu tiên của tối ưu hóa performance: the best request is no request. Chúng ta sẽ giải thích lại nguyên tắc này.
Cụ thể, hãy xem một trang web điển hình (trong ví dụ là từ Dynatrace). Ảnh chụp màn hình dưới đây cho thấy một phần tài liệu trực tuyến bao gồm nhiều thành phần khác nhau: main nav, footer, breadcrumb, sidebar và chủ đề chính.
Một page khác trên cùng trang web, chúng ta có những thành phần khác như tiêu đề quảng cáo, social media, galleries ... Mỗi thành phần được định nghĩa bởi markup và stylesheet.
Trong HTTP/1.1, chúng ta thường sẽ kết hợp tất cả style sheet vào một file CSS. Một kết nối TCP để truyền tất cả CSS cần thiết, thậm chí cho những page mà người dùng không thấy được, hệ quả có thể là một file CSS khổng lồ.
Vấn đề trở nên phức tạp khi một trang web sử dụng thư viện như Bootstrap, với kích cỡ tầm 300kB. Số lượng CSS thực tế mà mỗi page cần, trong một số trường hợp, thậm chí chưa đến 10% tổng kích cỡ được tải về:
Có những tool như UnCSS được tạo ra để loại bỏ phần style dư thừa.
Trong ví dụ trên CSS được xây dựng riêng cho công ty, phù hợp nhu cầu hơn so với Bootstrap. Tổng kích cỡ CSS là khoảng 80kB. CSS thực tế được dùng trên page là 8.1kB. Để thấy rằng dù file CSS đã được dựng riêng cho website nhưng page này vẫn chỉ dùng khoảng 10% trong số style đó.
HTTP/2 cho phép chúng ta chọn file mà muồn truyền tải. Bản thân request cũng không tốn kém như trong HTTP/1.1, vậy nên ta có thể sử dụng nhiều element link, trỏ trực tiếp đến element được dùng trên mỗi page cụ thể:
<link rel="stylesheet" href="/css/base.css"> <link rel="stylesheet" href="/css/typography.css"> <link rel="stylesheet" href="/css/layout.css"> <link rel="stylesheet" href="/css/navbar.css"> <link rel="stylesheet" href="/css/article.css"> <link rel="stylesheet" href="/css/footer.css"> <link rel="stylesheet" href="/css/sidebar.css"> <link rel="stylesheet" href="/css/breadcrumbs.css">
Tất nhiên là file ảnh và javascript cũng tương tự. Bằng cách này bạn chỉ truyền cái bạn cần. Download một file CSS lớn
Chỉ gọi những file cần thiết, và download song song.
Ảnh thứ nhất cho thấy thời gian để trình duyệt tải về là 700ms. Ảnh thứ hai hiện thị thời gian tải về một file CSS trong số 8 file của page, nội dung tải xuống gần như ngay lập tức.
Dù có vẻ không thực sự ấn tượng vì nó chỉ tải về 1 file. Nhưng vì 8 file CSS được download song song, cho nên chúng ta vẫn tiết kiệm được rất nhiều thời gian khi so sánh với cách tiếp cận cũ.
Khi chạy cùng một page trên ở webpagetest.org, chúng ta có thể thấy mẫu tương tự. File gộp main.css bắt đầu download sau 1.5s và mất 1.3s để download, thời gian để First Meaningful Paint là khoảng 3.5 s (dòng màu xanh)
Khi chúng ta chia nhỏ file CSS, mỗi file bắt đầu được download sau 1.5s (dòng màu vàng) và mất 315-375 ms để hoàn thành. Kết quả là giảm được thời gian First Meaningful Paint được hơn 1s (dòng màu xanh):
Điều thú vị là thứ được coi là anti-pattern của performance trong HTTP/1.1 - sử dụng nhiều tham chiếu đến tài nguyên - lại trở thành thực hành tốt nhất trong kỷ nguyên HTTP/2. Thêm vào đó, quy tắc vẫn giữ nguyên. Ý nghĩa thay đổi một xíu.
The best request is no request: bỏ những file và đoạn mã mà người dùng của bạn không cần!
The best request is no request: caching and versioning
Chúng ta đã biết cách tối ưu lần truy cập đầu tiên đến trang web. Gói tài nguyên được chia nhỏ thành nhiêu file và client chỉ nhận những gì thực sự cần để hiện thị trang web đó. Điều này trao cho chúng ta cơ hội để nhìn lại một số thứ mà người ta có khuynh hướng bỏ quên khi tối ưu performance: các lần truy cập tiếp theo.
Trong lần truy cập tiếp theo, chúng ta muốn tránh việc tải lại asset không cần thiết. HTTP headers như Cache-Control cho phép ta lưu file trên ổ đĩa người dùng trong một khoảng thời gian xác định. Một vài server CDN mặc định là vài phút. Một số là vài giờ hoặc thậm chí là vài ngày. Ý tưởng là trong suốt phiên đó, người dùng không nên phải tải lại những thứ đã download trước đó. Chẳng hạn Cache-Control header dưới đây sẽ cấu hình lưu file khoảng 600s.
Cache-Control: public, max-age=600
Chúng ta có thể tận dụng Cache-Control một cách chặt chẽ hơn. Trong lần tối ưu hóa đầu tiên, chúng ta quyết định cherry-pick tài nguyên và thận trọng với những gì chúng ta gửi cho client, vậy nên hãy lưu những tài nguyên này trên máy trong một thời gian dài:
Cache-Control: public, max-age=31536000
Thời gian ở trên là một năm. Điểm lợi trong việc cấu hình như này là tài nguyên sẽ được lưu trữ lâu hơn. Ảnh chụp màn hình dưới đây hiển thị biểu đồ thác nước của lần truy cập đầu tiên. Mọi tài nguyên của file HTML đều được request:
Với việc set đúng Cache-Control header, lần truy cập tiếp theo sẽ tốn ít request hơn. Bạn có thể lưu trữ tài nguyên trên client trong một thời gian dài. Tuy nhiên, HTML thì nên ngắn gọn trong hầu hết trường hợp. Thông thường, file HTML chứa thông tin tài nguyên cần download. Nếu bạn muốn thay đổi tài nguyên thì cần phải cập nhật lại các đường link tham chiếu chúng trong file HTML. Những CDN phổ biến không cache HTML quá 6 phút, nhưng bạn có thể quyết định điều gì là phù hợp với ứng dụng của mình.
Và lại một lần nữa, the best request is no request: lưu file ở client lâu nhất có thể, và đừng request chúng lại một lần nữa.
Lời cuối cùng
HTTP/2 được thiết kế nhằm khắc phục những hạn chế của HTTP/1. Điều tồi tệ với performance không phải là gọi một số lượng lớn request nữa, mà là tải về những dữ liệu không cần thiết.
Để khai thác được hết tiềm năng của HTTP/2, chúng ta phải xem xét từng trường hợp cụ thể. Một cấu hình tối ưu hóa có thể tốt cho trang web này nhưng lại tệ cho trang khác. Quy tắc vàng cho việc tối ưu performance vẫn được giữ nguyên: the best request is no request. Chỉ truyền những thứ mà người dùng cần. Vậy thôi.
Dịch từ: http://alistapart.com/article/the-best-request-is-no-request-revisited