• Thứ Tư, 08/09/2010 08:58 (GMT+7)

    Tăng tốc website (phần 2)

    Anh Khoa
    Tiếp nối "Tăng tốc website" (ID: A1003_88), kỳ này, chúng ta cùng điểm qua 7 phương pháp thuộc nhóm máy chủ (server) thường được lập trình viên khai thác để cải thiện tốc độ đáp ứng.

         >> Tăng tốc website

    1) Sử dụng mạng phân phối nội dung

    Về cơ bản, khoảng cách giữa người dùng - hay chính xác hơn là máy tính mà bạn đang sử dụng - và máy chủ dịch vụ web (web server) có ảnh hưởng đáng kể đến thời gian đáp ứng của các trang web lưu trữ trên web server đó. Việc triển khai nội dung trên nhiều máy chủ "phân tán" về mặt địa lý sẽ giúp tải (load) trang web nhanh hơn so với mong đợi của người dùng. Tuy nhiên, bạn đừng cố gắng thiết kế lại (redesign) ứng dụng web (trang web hay website) hiện có của mình để đưa vào một cấu trúc đã được định sẵn nào đó. Tùy vào ứng dụng, việc thay đổi cấu trúc có thể bao gồm các tác vụ "khó xơi" như đồng bộ các trạng thái phiên làm việc và tạo bản sao cho giao dịch dữ liệu từ các địa điểm máy chủ. Tuy nhiên, trong nhiều trường hợp, nỗ lực giảm khoảng cách giữa người dùng và nội dung (trên các web server) có thể bị trì hoãn, hay thậm chí phá sản, bởi bước cấu trúc lại ứng dụng/website này.

    Như từng đề cập ở phương pháp "Hạn chế yêu cầu HTTP", khoảng 80-90% quãng thời gian mà người dùng phải chờ thường dành cho việc tải về tất cả thành phần trong một trang web, mà chủ yếu là hình ảnh, stylesheet, script, nội dung Flash... Do đó, thay vì bắt đầu với tác vụ khó khăn là thiết kế lại cấu trúc của ứng dụng/website, tốt hơn bạn nên bắt đầu bằng việc phân tán các nội dung của mình. Giải pháp này không những giúp giảm đáng kể thời gian đáp ứng mà còn dễ dàng thực hiện hơn nhờ các mạng phân phối nội dung (Content Delivery Network - CDN).

    CDN là tập hợp các web server được triển khai ở nhiều địa điểm với mục tiêu cung cấp nội dung đến người dùng cuối hiệu quả hơn. Server được chọn để phân phối nội dung đến một (hay nhiều) người dùng cụ thể thường dựa trên phép đo khoảng cách mạng. Ví dụ, server với khoảng cách mạng ngắn nhất hay server với thời gian phản hồi nhanh nhất sẽ được ưu tiên chọn sử dụng (hay nói chính xác là phân phối nội dung cho người dùng yêu cầu).

    Vài doanh nghiệp lớn sở hữu CDN riêng, tuy nhiên sẽ hiệu quả hơn về chi phí nếu sử dụng dịch vụ CDN từ các nhà cung cấp như Akamai Technologies, Mirror Image Internet hay Limelight Networks. Với các website nhỏ và công ty mới thành lập, chi phí cho dịch vụ CDN có thể là rào cản, nhưng khi lượng khách hàng mục tiêu tăng cao và mở rộng ở phạm vi toàn cầu thì cần có CDN để mang đến khả năng phản hồi, đáp ứng nhanh cho website.

    2) Bổ sung khóa Expires hay Cache-Control

    Trước hết, bạn nhớ 2 nguyên tắc sau: Với các thành phần tĩnh, chọn chính sách "Never expire" bằng cách thiết lập khóa (header) Expires với thời gian là thời điểm “vô tận” trong tương lai. Còn với các thành phần động, sử dụng khóa Cache-Control phù hợp để giúp trình duyệt đáp ứng các yêu cầu (request) có điều kiện.

     

    Thực tế, thiết kế của các trang web ngày càng phong phú, đồng nghĩa với việc có nhiều script, stylesheet, hình ảnh và nội dung Flash hơn trong một trang. Người dùng đầu tiên viếng thăm trang web của bạn có thể phải thực hiện vài yêu cầu HTTP, tuy nhiên bằng cách sử dụng khóa Expires, bạn có thể làm cho các thành phần này có thể được lưu lại để tái sử dụng. Về cơ bản, việc này giúp tránh những yêu cầu HTTP không cần thiết đối với những lần xem trang web sau đó. Khóa Expires thường được sử dụng với hình ảnh, tuy nhiên thi thoảng vẫn được kết hợp với tất cả thành phần khác như script, stylesheet và Flash.

    Các trình duyệt (và cả cookie trình duyệt) sử dụng một bộ nhớ tạm (cache) để giảm số lượng và kích thước của các yêu cầu HTTP, giúp trang web tải nhanh hơn. Một web server sử dụng khóa Expires trong các phản hồi HTTP sẽ thông báo cho máy khách (client) biết một thành phần có thể được lưu lại với thời gian bao lâu trong bộ nhớ tạm.

    Ví dụ, bạn bổ sung dòng Expires: Fri, 31 Dec 2010 20:00:00 GMT vào trang web để thông báo cho trình duyệt biết phản hồi này sẽ không thay đổi từ thời điểm hiện tại cho đến ngày 31/12/2010. Nếu máy chủ của bạn là Apache, hãy sử dụng khóa ExpiresDefault để thiết lập thời hạn hiệu lực so với ngày hiện tại. Chẳng hạn, bạn thêm dòng ExpiresDefault "access plus 10 years" để thiết lập thời gian hết hạn là sau 10 năm nữa tính từ lần yêu cầu.

    Lưu ý, nếu bạn sử dụng một khóa Header với thời gian khá dài thì bạn phải thay đổi tên tập tin của các thành phần mỗi khi chúng thay đổi.

    3) Nén dữ liệu theo chuẩn Gzip

    Thời gian cần để gửi một yêu cầu và phản hồi HTTP qua mạng có thể giảm xuống đáng kể bằng các kỹ thuật xử lý đầu cuối. Thực tế là băng thông kết nối mạng của người dùng đầu cuối, nhà cung cấp dịch vụ internet, khoảng cách giữa các điểm trao đổi dữ liệu ngang hàng,… đều nằm ngoài tầm kiểm soát của nhóm phát triển dự án website. Tuy nhiên, còn có những yếu tố ảnh hưởng đến thời gian phản hồi của trang web. Thao tác nén dữ liệu sẽ giúp giảm thời gian phản hồi bằng cách giảm kích thước của phản hồi HTTP tương ứng.

    Bắt đầu với chuẩn HTTP/1.1, các máy khách web thông báo hỗ trợ tính năng nén dữ liệu với từ khóa Accept-Encoding trong yêu cầu HTTP.

    Accept-Encoding: gzip, deflate

    Nếu web server thấy khóa này trong yêu cầu, nó có thể nén nội dung/dữ liệu phản hồi bằng cách sử dụng 1 trong các phương thức được liệt kê bởi máy khách. Web server thông báo cho máy khách biết thông qua khoá Content-Encoding trong phản hồi này.

    Content-Encoding: gzip

    Gzip có thể xem là phương thức nén thông dụng và hiệu quả nhất, được phát triển bởi dự án nguồn mở GNU và được chuẩn hóa bởi RFC 1952. Ngoài ra, còn có phương thức ít thông dụng và ít hiệu quả hơn là deflate.

    Phương pháp nén Gzip thường giảm kích thước của phản hồi xuống khoảng 70%. Khoảng 90% lưu lượng Internet hiện nay đi qua các trình duyệt có hỗ trợ Gzip. Nếu bạn sử dụng Apache, việc cấu hình gzip tùy thuộc vào phiên bản Apache đang sử dụng: Apache 1.3 sử dụng mod_gzip, trong khi đó Apache 2.x sử dụng mod_deflate.

    Các máy chủ chọn những gì cần nén (theo phương thức Gzip) dựa trên loại tập tin, tuy nhiên thường là giới hạn trong những gì họ quyết định nén. Hầu hết website thường nén các tài liệu HTML, ngoài ra còn có stylesheet và script, tuy nhiên nhiều website lại bỏ qua cơ hội giảm kích thước nội dung này. Nhiều lập trình viên cũng cho rằng nén mọi phản hồi dạng văn bản bao gồm XML và JSON là rất cần thiết; tập tin hình ảnh và PDF đã được nén rồi nên cố gắng nén chúng không chỉ làm lãng phí hiệu năng CPU mà còn tiềm ẩn nguy cơ làm tăng kích thước tập tin.

    4) Cấu hình ETags

    ETags (Entity tags) là một cơ chế mà các web server và trình duyệt sử dụng để xác định xem thành phần trong cache của trình duyệt có đáp ứng với một nội dung trên máy chủ chính hay không. Trong trường hợp này, "entity" là cách viết khác đi của từ "component" (thành phần): hình ảnh, script, stylesheet... ETags được bổ sung nhằm cung cấp một cơ chế xác thực/xác nhận các thành phần một cách mềm dẻo hơn so với cách dựa trên thời gian chỉnh sửa cuối cùng. ETag là một chuỗi có mục đích duy nhất là xác định phiên bản cụ thể của một thành phần. Cấu trúc của ETag có phần gượng ép và máy chủ gốc chỉ định ETag của thành phần bằng cách sử dụng khóa phản hồi ETag.

    HTTP/1.1 200 OK

    Last-Modified: Tue, 12

    Dec 2006 03:03:59 GMT

    ETag: "10c24bc-4ab-457e1c1f"

    Content-Length: 12195

    Sau đó, nếu trình duyệt phải xác thực một thành phần thì trình duyệt đó sẽ sử dụng khóa If-None-Match để đẩy ETag về lại máy chủ gốc. Theo ví dụ ở trên, nếu ETags đúng, một mã trạng thái 304 sẽ được mở lại để giảm phản hồi xuống còn dung lượng 12195 byte.

    GET /i/yahoo.gif

    HTTP/1.1 Host: us.yimg.com

    If-Modified-Since: Tue, 12 Dec 2006 03:03:59 GMT

    If-None-Match: "10c24bc-4ab-457e1c1f"

    HTTP/1.1 304 Not Modified

    Vấn đề với ETags là chúng thường được xây dựng với những thuộc tính riêng biệt dành riêng cho một máy chủ cụ thể. ETags sẽ không ăn khớp (đúng) khi một trình duyệt lấy thành phần gốc từ một máy chủ và sau đó cố gắng xác thực thành phần này trên một máy chủ khác - thường xảy ra trên các website sử dụng nhiều server để đáp ứng các yêu cầu từ người dùng. Mặc định, cả Apache và IIS nhúng dữ liệu trong ETag, qua đó giúp giảm việc xác thực trên các webiste được phân tán trên nhiều server. Định dạng ETag cho Apache 1.3 và 2.x là inode-size-timestamp. Dù một tập tin được cung cấp có thể tồn tại trong cùng thư mục trên nhiều máy chủ, và có cùng kích thước tập tin, quyền hạn, mốc thời gian... tuy nhiên giá trị của tham số inode khác nhau trên các server. Trong khi đó, IIS 5.0 và 6.0 cũng gặp vấn đề tương tự với ETags. Định dạng cho ETags trên IIS là FiletimeStamp: ChangeNumber, trong đó ChangeNumber là bộ đếm được dùng để theo dõi những thay đổi cấu hình trên IIS. Điểm khác biệt là ChangeNumber giống nhau trên mọi máy chủ IIS đằng sau một website.

    Kết quả cuối cùng là ETags được tạo ra bởi Apache và IIS sẽ không tương ứng từ server này đến server khác. Nếu ETags không đúng, người dùng không nhận được phản hồi mang số hiệu 304 mà ETags thiết kế, thay vào đó là phản hồi thông thường mang số hiệu 200 đi kèm với tất cả dữ liệu của một thành phần.

    Nếu bạn chỉ lưu website trên 1 server thì sẽ không có vấn đề gì. Tuy nhiên, nếu bạn sử dụng nhiều server để lưu website và đang sử dụng Apache hay IIS với cấu hình ETags mặc định, thì người xem website của bạn sẽ nhận được các trang web chậm hơn, đồng thời các server sẽ phải làm việc ở tần suất cao hơn. Do đó, bạn sẽ cần sử dụng nhiều băng thông hơn và các proxy sẽ không lưu lại nội dung một cách hiệu quả. Thậm chí, nếu các thành phần của bạn có sử dụng khóa Expire với thời điểm trong tương lai xa thì một yêu cầu GET vẫn được thực hiện bất cứ khi nào người dùng ra lệnh tải lại (reload) hay làm tươi (refresh) trang web đang xem.

    Nếu không khai thác lợi thế của mô hình xác thực động mà ETags cung cấp thì tốt hơn là bạn nên gỡ bỏ tất cả ETags. Việc gỡ khóa ETags thậm chí còn giúp giảm kích thước của phần đầu HTTP (HTTP header) trong cả phản hồi và yêu cầu sau đó. Bạn có thể tham khảo cách gỡ ETags tại http://support.microsoft.com/?id=922733. Với Apache, việc gỡ bỏ ETags có thể được thực hiện đơn giản bằng cách bổ sung dòng sau vào tập tin cấu hình Apache: FileEtag none.

    5) Bổ sung hàm Flush

    Khi người dùng yêu cầu một trang web thì cần từ 200 đến 500 mili giây để server "ghép nối" các trang HTML riêng rẽ lại với nhau. Trong suốt thời gian chờ dữ liệu đến, trình duyệt sẽ ở trạng thái không tải (idle). Trong PHP bạn có hàm flush(). Hàm này cho phép bạn gửi phản hồi đã hoàn chỉnh phần nào đến trình duyệt, vì thế trình duyệt có thể bắt đầu xử lý các thành phần trong khi server đang bận chờ đợi phần còn lại của trang HTML. Lợi ích của việc này chủ yếu được nhìn thấy trên các hệ thống máy chủ bận rộn hay máy tính của người dùng cuối có cấu hình yếu.

    Nơi thích hợp để bổ sung hàm flush là ngay sau phần HEAD vì HTML cho phần mở đầu thường dễ tạo hơn và cho phép kết hợp mọi tập tin CSS và JavaScript cho trình duyệt để bắt đầu xử lý công việc một cách song song trong khi backend vẫn tiếp tục công việc.

    Ví dụ:

     ... <!-- css, js -->

    </head>

    <?php flush(); ?>

    <body>

    ... <!-- content -->

    6) Sử dụng GET cho các yêu cầu AJAX

    Nhóm phát triển Yahoo! Mail nhận thấy khi sử dụng phương thức XMLHttpRequest, lệnh POST được thực hiện trong trình duyệt như là 1 tiến trình gồm 2 bước: Gửi các phần header trước, sau đó gửi dữ liệu. Vì thế, tốt nhất là bạn nên sử dụng lệnh GET nhưng chỉ lấy một gói nội dung TCP để gửi đi (trừ khi bạn có nhiều cookie trình duyệt). Chiều dài (hay chính xác là dung lượng) tối đa của URL trong trình duyệt IE là 2K, vì thế nếu muốn gửi nhiều hơn 2K dữ liệu thì không thể sử dụng hàm GET. Một ứng dụng phụ, lệnh POST mà không gửi đi bất kỳ dữ liệu nào có cách hành xử giống lệnh GET.

    7) Tránh sử dụng đường dẫn ảnh trống

    Đường dẫn cho hình ảnh trong một trang web thường được khai báo dưới 2 dạng sau:

    * Với HTML thuần túy: <img src="">

    * JavaScript: var img = new Image(); img.src = "";

    Cả 2 cách khai báo trên đều mang lại cùng hiệu ứng: trình duyệt sẽ tạo ra một yêu cầu khác cho server. Theo đó, IE tạo một yêu cầu đến thư mục mà trang web được lưu trữ, trong khi đó Safari và Chrome gửi yêu cầu trực tiếp đến chính trang web đó. Firefox 3 và những phiên bản trước hoạt động tương tự Safari và Chrome, tuy nhiên phiên bản 3.5 nhận dạng được vấn đề này [xem chi tiết tại https://bugzilla.mozilla.org/show_bug.cgi?id=444931] và không bao giờ gửi yêu cầu. Opera không làm bất cứ điều gì khi một đường dẫn đến hình ảnh được để trống.

    Việc để trống đường dẫn đến các hình ảnh trong một trang web trước tiên sẽ làm cho máy chủ phải gửi phản hồi cho các yêu cầu không có thực, lãng phí thời gian đáp ứng của máy chủ. Thậm chí, trong nhiều trường hợp, việc bỏ trống đường dẫn ảnh có thể gây ra sự phá hoại dữ liệu, vì dù yêu cầu hiển thị hình ảnh không trả về kết quả là một ảnh nhưng tất cả thông tin trong phần header đều được đọc và chấp nhận bởi trình duyệt, trong đó có mọi cookie. Do vậy, trong khi mọi thành phần rỗng đều được bỏ đi, các thành phần nguy hại trong cookie và header có thể phát huy tác dụng (tham khảo thêm tại địa chỉ www.nczonline.net/blog/2009/11/30/empty-image-src-can-destroy-your-site/).

    Tham khảo: Yahoo! Developer Network

     

    Từ khóa: Anh Khoa
    ID: A1007_120