5. HTTP Caching (ok)

https://viblo.asia/p/http-caching-6BAMYknzvnjz

Xem thêm bài viết https://viblo.asia/p/tim-hieu-ve-http-caching-djeZ1BRJlWz

Trong các ứng dụng web hiện nay chắc hẳn các bạn đã khá quen thuộc với từ khóa Cache. Chúng ta có cache lại kết quả của các câu truy vấn (queries) để trả về kết quả nhanh hơn, ngoài ra chúng ta có thể cache lại các views để render nhanh hơn,...

Bài viết hôm nay tôi muốn giới thiệu với các bạn về HTTP Caching, về định nghĩa, cơ chế hoạt động, tại sao nên sử dụng và cách config server để thực hiện việc cache này.

Vậy chúng ta phải làm gì trong trường hợp này? Dù chúng ta đã rất cố gắng refactor queries phía Server để trả về kết quả nhanh nhất tuy nhiên để tốc độ ngon hơn nữa thì đây chính là lúc cần sự trợ giúp thêm từ phía Client. Các file tĩnh như các file hình ảnh (image), các file stylesheet (CSS), các file javascript (JS), các đoạn clip,... dù đã lưu trên Server nhưng chúng ta đã có sự trợ giúp của HTTP Caching như một cầu nối giao tiếp để Client cũng có thể lưu lại những file tĩnh đó trên máy của người dùng. Làm được như vậy sẽ giảm tải khá nhiều cho Server trong việc xử lý các request đến các file tĩnh này và trả về cho Client.

HTTP Caching là gì?

Trong nội dung bài viết này tôi xin phép không giới thiệu về giao thức HTTP cũng như các tham số của HTTP Header.

Kỹ thuật HTTP Caching chính là việc bạn chuyển một bản copy các tài nguyên tĩnh phía Server xuống lưu ở dưới Client. Về cơ bản, người dùng sẽ cảm nhận thấy một độ trễ rất thấp khi yêu cầu các tài nguyên tĩnh này từ phía Server, lưu lượng truyền đi ít hơn, số request đến Server ít hơn, do vậy Server sẽ nhàn hơn để dùng sức của mình vào những việc khác.

  1. Client yêu cầu file index.html

  2. Server làm công việc đi tìm kiếm xem file index.html ở đâu

  3. Server tìm thấy và trả về cho Client

  4. Client download file và hiển thị cho người dùng.

Giả sử index.html là một file tĩnh rất ít khi thay đổi thì điều bất cập xảy ra ở đây đó là mỗi lần Client yêu cầu truy cập file này, Server đều phải lục lọi tìm kiếm rồi bắt buộc Client phải download thì mới sử dụng được. Việc làm này đang làm lãng phí thời gian của Server và thời gian của người sử dụng.

Tại sao cần sử dụng HTTP Caching?

Các lợi ích chính mà HTTP Caching mang lại có thể kể ra như sau:

  1. Giúp cho ứng dụng Web load nhanh hơn (giảm thời gian trễ).

  2. Giảm băng thông sử dụng.

  3. Giảm số lần truy cập lên Server.

HTTP Caching hoạt động như thế nào?

Tất cả các hệ thống cache đều sử dụng chung các nguyên tắc để xác định thời điểm cung cấp đối tượng (tất nhiên là các đối tượng đó đã được lưu trong Cache). Trường hợp đối tượng chưa được lưu trong Cache thì sẽ phát sinh yêu cầu đến Server để lấy thông tin của đối tượng đó.

Một số nguyên tắc được thiết lập trong giao thức HTTP (1.0, 1.1) và một số nguyên tắc khác sẽ được thiết lập bởi những người quản trị cache (hiểu nôm na có thể là người dùng Browser có thể đặt các setting cho Cache thông qua các trình duyệt như Safari, Firefox, Chrome,...)

Một số nguyên tắc chung có thể được liệt kê ra như sau:

  1. Nếu trong Response Header từ Server có thông tin không cho phép lưu cache, Browser sẽ không được quyền lưu.

  2. Nếu đối tượng trong request có yêu cầu xác thực hay bảo mật (VD: HTTPS), nó sẽ không được phép lưu trong shared cache.

  3. Nếu một đối tượng được gọi là "mới" được phép lưu cache ở Browser mà không cần xác thực với Server thì nó cần phải có thông tin về khoảng thời gian tồn tại.

  4. Nếu một đối tượng được gọi là "cũ", Server sẽ nhận được yêu cầu xác thực đối tượng và báo cho Client biết nó còn giá trị sử dụng hay không. Trong trường hợp này ClientServer chỉ trao đổi với nhau thông qua việc truyền và gửi message để confirm nên lưu lượng sẽ giảm đi rất nhiều so với việc gửi cả một đối tượng có dung lượng xác định.

Như vậy các bạn có thể thấy thay bằng việc Client cứ phải download những file hàng trăm KB hay đơn vị MB với mỗi request lên Server thì giờ đây chỉ bằng việc trao đổi với nhau vài message với dung lượng cực kỳ nhỏ, Client đã có thông tin mà mình cần, đồng thời Server cũng đỡ cực hơn trong việc xử lý request từ Client. Message mà tôi nói đến chính là những tham số mà HTTP Header hỗ trợ. Bây giờ chúng ta sẽ cùng xem chi tiết đó là những tham số gì và nó hoạt động ra sao.

Các giải pháp HTTP Caching

Sau đây tôi xin giới thiệu với các bạn một số giải pháp thực hiện HTTP Caching sử dụng các tham số của HTTP Header. Các bạn nên lưu ý rằng các tham số này chỉ mang tính chất truyền thông điệp giữa Client và Server, đồng thời cũng là thông tin đối sánh để Client biết lấy dữ liệu từ Cache hay lại phải request lên Server để lấy dữ liệu mà mình cần. Cache mà tôi nói đến ở đây chính là một vùng lưu trữ nào đó trên ổ cứng của máy bạn mà Client (hay chính xác là Browser) sử dụng để lưu trữ các file tĩnh từ phía Server trả về.

Tham số Last-Modified

Khi lần đầu tiên Client request một file (VD: logo.png) từ Server, file này chưa hề được lưu trong Cache ở Client, Server sẽ trả về cho Client file đó kèm theo tham số Last-Modified trên Response Headers để xác định thời điểm cuối cùng file đó được sửa đổi.

Nhìn vào hình ảnh trên ta có thể thấy file được chỉnh sửa lần cuối cùng vào Thứ 2, ngày 1/6/2015, lúc 2 giờ 57 phút, Status Code200 OK. Sau đó Client sẽ lưu lại file này vào Cache và đồng thời lưu luôn thông tin ngày thay đổi cuối cùng của file.

Tiếp theo, Client lại thực hiện request một lần nữa lên Server. Trước hết hãy cùng xem qua sơ đồ hoạt động ví dụ sau:

  1. Client gửi yêu cầu lấy file logo kèm theo điều kiện ngày thay đổi của file đó phải chính xác là Thứ 2, ngày 1/6/2015, lúc 2 giờ 57 phút (tham số If-Modified-Since trong Request Headers).

  2. Server đi tìm file logo và kiểm tra xem ngày thay đổi có đúng với thông tin Client gửi lên không.

  3. Nếu Server check OK, Client sẽ nhận được Status Code304 Not Modified.

  4. Client không phải download file logo đó một lần nữa từ Server mà chui thẳng vào Cache để lấy file đó ra dùng.

Như vậy câu hỏi đặt ra là: Nếu If-Modified-Since trong Request HeadersLast-Modified trong Response Headers trước đó không trùng nhau thì sao?

Câu trả lời hết sức đơn giản, Client sẽ download file mới, lưu Cache đồng thời lưu thông tin ngày thay đổi mới. Quy trình sẽ lại lặp lại từ bước đầu tiên tôi nói phía trên.

  • Ưu điểm:

    • Nếu file không có sự thay đổi, Server chỉ phải gửi về status 304 với thông báo Not Modified mà không cần phải gửi lại toàn bộ nội dung file cho Client.

  • Nhược điểm:

    • Vẫn phải kết nối đến Server để nhận thông điệp.

    • Server vẫn phải xử lý kiểm tra thời gian thay đổi file.

    • Nếu có phát sinh sai lệch thời gian trên Server thì nội dung file vẫn được gửi xuống Client download mặc dù file đó không hề thay đổi.

Tham số Etag

Cơ chế hoạt động của tham số Etag cũng gần gần tương tự với Last-Modified chỉ khác ở điểm Etag có giá trị là một chuỗi ký tự đại diện cho file.

Etag có thể là một chuỗi Hash hay Footprint. Trong nội dung bài viết này tôi xin phép không đi sâu vào việc Footprint được sinh ra thế nào hay theo thuật toán nào, ta chỉ hiểu đơn giản là với mỗi file, Server sẽ sinh ra 1 chuỗi ký tự riêng và duy nhất để đại diện cho file đó (mà chúng ta gọi là tham số Etag). Nếu bạn thay đổi nội dung file thì Etag chắc chắn sẽ thay đổi giá trị.

  1. Client yêu cầu file logo từ Server, và gửi kèm theo tham số If-None-Match (chính là giá trị của Etag) trong Request Headers đã lưu trước đó (có thể hiểu là từ lần request đầu tiên).

  2. Server tìm kiếm file logo và kiểm tra mã Etag.

  3. Nếu Server check OK, Client sẽ nhận được Status Code304 Not Modified.

  4. Client không phải download file logo đó một lần nữa từ Server mà chui thẳng vào Cache để lấy file đó ra dùng.

Ta có thể nhận thấy ưu và nhược điểm của tham số Etag này cũng gần tương tự với tham số Last-Modified, ta vẫn cứ phải chui lên Server để kiểm tra xem file có sự thay đổi hay không.

Tham số Expires

Để khắc phục nhược điểm của 2 tham số trên, chúng ta hãy cùng tiếp tục tìm hiểu về tham số Expires. Để tránh việc request một file nào đó từ lần thứ 2 trở đi cứ phải chui lên Server hỏi và kiểm tra, với lần đầu tiên Client request, Server sẽ trả về nội dung file kèm theo thông tin Expires trong Response Headers.

Thông tin trong tham số Expires chính là thời điểm hết hạn của file đó, sau thời điểm này nếu Client vẫn muốn sử dụng file thì phải lên Server để download lại cũng như cập nhật thông tin Expires mới, còn nếu chưa hết hạn thì Client sẽ chỉ việc lấy thông tin từ Cache để sử dụng mà không cần hỏi lại Server.

  1. Client cần request file logo, tuy nhiên kiểm tra thấy thời hạn hết hiệu lực chưa đến.

  2. Client tự lấy file đó từ Cache mà không cần lên Server hỏi.

  • Ưu điểm của cách làm này đó là chúng ta hạn chế được việc Client sẽ phải chui lên để hỏi Server, khi đã làm được như vậy thì Server sẽ rảnh hơn để dành sức và tài nguyên phục vụ những thao tác khác. Ngoài ra thì tham số Expires cũng đã được hỗ trợ từ chuẩn HTTP 1.0 nên nó được sử dụng khá rộng rãi hiện nay.

  • Nhược điểm hay câu hỏi đặt ra cho cách làm này đó là nhỡ đâu trong thời điểm file đó vẫn chưa hết hạn (tức là Client vẫn đương nhiên lấy ở Cache) thì phía Server đã thực hiện thay đổi file đó. Như vậy trong trường hợp này Client sẽ không nhận được thông tin file mới ít nhất cho đến khi qua thời điểm hết hạn của file.

Có một giải pháp đã được đưa ra để giải quyết nhược điểm của cách làm này đó là đánh version cho file. Các bạn có thể thấy trong một số ứng dụng họ có đánh version cho các assets (js, css) dựa trên sự thay đổi của các file đó. Version có thể là một thành phần trong tên file (VD: A.123456.js) hoặc có thể ở dạng query string (VD: a.js?v=123456).

Cách đánh version như vậy sẽ giải quyết được nhược điểm của phương pháp này ở chỗ khi Server cập nhật file mới với version mới, Client sẽ download file mới kèm theo Expires của file mới và từ đó làm việc với file mới chứ không phải file cũ đã được Cache.

Tham số Cache-Control

Cách dùng tham số Cache-Control về cơ bản lại giống với Expires tuy nhiên cách cấu hình uyển chuyển và linh hoạt hơn. Thay vì việc đặt ra thời gian cụ thể hết hạn thì tham số Cache-Control này có thể khai báo thay cho một thông báo kiểu như: "File này sẽ hết hạn sử dụng trong vòng 1 tháng tính từ thời điểm hiện tại". Cụ thể thì tham số này hỗ trợ cấu hình các thông tin như sau:

  • max-age=[seconds]: định nghĩa khoảng thời gian tối đa mà file được cache (tính từ thời điểm request file), tính bằng đơn vị giây, một số thông tin chuyển đổi cơ bản như sau:

    • 1 ngày = 86400 giây

    • 1 tuần = 604800 giây

    • 1 tháng = 2629000 giây

    • 1 năm = 31636000 giây

  • s-maxage=[seconds]: tương tự như max-age nhưng thông số này chỉ được sử dụng cho shared caches (VD: Proxy caches).

  • public: cho phép file được cache bởi các Proxy và các server trung gian.

  • private: file có giá trị khác nhau cho các Client khác nhau, Browser có thể cache nhưng Proxy thì không.

  • no-cache: không được phép cache file này, thường được sử dụng cho những trang kết quả tìm kiếm, mặc dù URL là giống nhau nhưng nội dung có thể thay đổi khác nhau.

  • no-store: chỉ thị cho Client biết không được phép tạo một bản copy của file và lưu trữ dưới bất kỳ hình thức nào.

  • must-revalidate: chỉ thị bắt buộc phải validate lại với Server khi sử dụng file đã được cache.

  • proxy-revalidate: tương tự như must-revalidate nhưng chỉ áp dụng cho các Proxy caches.

Ví dụ sử dụng:

Cache-Control: max-age=3600, must-revalidate

Cách config server hoạt động với HTTP Caching

Phần này tôi xin giới thiệu cách config HTTP Caching hoạt động với Server Apache (hệ điều hành UBUNTU). Cách config với Nginx về cơ bản cũng tương tự (các bạn vui lòng Google để biết thêm chi tiết).

Điều kiện cần đầu tiên đó là 2 module: mod_headersmod_expires của Apache phải được enable. Các bạn cần làm theo các bước như sau:

  • Bước 1: kiểm tra xem 2 mod đó đã được enable chưa

	apachectl -t -D DUMP_MODULES
  • Bước 2: enable 2 mod nói ở trên (nếu kiểm tra fail ở bước 1), sau đó nhớ restart server

    sudo a2enmod headers
	sudo a2enmod expires

Sau khi đã có 2 mod cần thiết chúng ta tiến hành cấu hình cho HTTP Caching hoạt động như sau:

  • Sử dụng Expires Header:

    <IfModule mod_expires.c>
        ExpiresActive On
        ExpiresByType image/jpg "access 1 year"
        ExpiresByType image/jpeg "access 1 year"
        ExpiresByType image/gif "access 1 year"
        ExpiresByType image/png "access 1 year"
        ExpiresByType text/css "access 1 month"
        ExpiresByType text/html "access 1 month"
        ExpiresByType application/x-javascript "access 1 month"
        ExpiresByType application/javascript "access 1 month"
        ExpiresByType text/javascript "access 1 month"
        ExpiresByType image/x-icon "access 1 year"
        ExpiresDefault "access 1 month"
    </IfModule>
  • Sử dụng Cache-Control Header:

    <IfModule mod_headers.c>
        <FilesMatch "\.(ico|jpg|jpeg|png|gif|css)$">
        	Header set Cache-Control "max-age=2678400, public"
        </FilesMatch>
        <FilesMatch "\.(html|htm)$">
        	Header set Cache-Control "max-age=7200, private, must-revalidate"
        </FilesMatch>
        <FilesMatch "\.(pdf)$">
        	Header set Cache-Control "max-age=86400, public"
        </FilesMatch>
        <FilesMatch "\.js$">
        	Header set Cache-Control "max-age=2678400, private"
        </FilesMatch>
    </IfModule>

Có một số chú ý như sau:

Kết luận

  • Trong các cách sử dụng tham số cho HTTP Headers để thực hiện HTTP Caching có thể chia làm 2 loại:

    • Strong Caching Header: ExpiresCache-Control.

    • Weak Caching Header: EtagLast-Modified.

  • Ứng dụng của bạn chỉ nên sử dụng một Strong Caching Header và một Weak Caching Header vì cơ chế tương tự nhau, nếu sử dụng cả hai cùng một lúc sẽ dư thừa.

  • Nếu đồng thời sử dụng ExpiresCache-Control thì Cache-Control sẽ được ưu tiên hơn.

  • Nên sử dụng Cache-Control hơn là Expires vì tính linh động của nó.

  • Nên sử dụng Last-Modified hơn là Etag vì có thể Etag sẽ gặp vấn đề khi config với Apache2.

  • Với các file tĩnh ít khi thay đổi thì nên đặt thời gian còn hiệu lực là khoảng 1 tháng đến 1 năm.

  • Nên sử dụng thêm việc đánh version cho các file tĩnh để khắc phục nhược điểm của Expires cũng như Cache-Control.

Hy vọng các bạn tìm thấy thông tin hữu ích qua bài viết này (bow)

Tham khảo

Các Web Developer Đã Sử Dụng "Cache-Control" Như Thế Nào

Hầu hết các web developer có kinh nghiệm đều nắm được các chỉ thị phổ biến trong HTTP header như Status Code, Content Type, Cookie. Tuy nhiên nếu được hỏi về Cache-Control thì không nhiều có thể trả lời ngay được chỉ thị này dùng để làm gì. Nếu bạn là một trong số này thì bài viết này sẽ giúp bạn có được câu trả lời cho lần tiếp theo nếu như được hỏi cũng như áp dụng vào công việc thực tế.

Cache-Control Directive Dùng Làm Gì

Đầu tiên đối với những bạn dev nào ít kiên nhẫn thì câu trả lời nhanh gọn cho câu hỏi trên đó là sử dụng chỉ thị Cache-Control trong header phục vụ hai mục đích chính:

  • Tối ưu tốc độ tải trang

  • Tăng tính bảo mật

Với các bạn dev nào có tính tò mò, ham tìm tòi học hỏi (rất tốt tôi tin tưởng mãnh liệt rằng các bạn sẽ có một sự nghiệp đầy triển vọng!) thì hãy kiên nhẫn kéo xuống đọc thêm một xíu nữa để có thể hiểu được chi tiết hơn về câu trả lời ở trên đây.

Tối Ưu Tốc Độ Tải Trang

Tốc dộ tải trang là một trong những điểm quan trọng khi phát triển ứng dụng website. Theo một khảo sát gần đây thì một nửa số người dùng internet hy vọng nội dung trang web được tải về trong vòng 2 giây hoặc ít hơn. Điều này có nghĩa nếu như website của bạn không quá phổ biến, thì việc yêu cầu người dùng đợi hơn 2 giây để nội dung trang được tải về có thể khiến website gặp nguy cơ mất đi một nửa lượng truy cập.

Vậy thì câu hỏi đặt ra tiếp theo ở đây đó là Cache-Control giúp cải thiện tốc độ tải trang web như thế nào?

Đầu tiên chúng ta lưu ý Cache-Control thường được sử dụng trong response header (hay header trả về từ server) và chức năng của nó là để kiểm soát việc lưu trữ dữ liệu của trang trong bộ nhớ đệm của trình duyệt (hay cache).

Ví dụ trong một trang web bạn có một ảnh banner với kích thước khá lớn và bạn muốn trình duyệt lưu trữ ảnh này vào bộ nhớ cache của nó để lần sau khi người dùng truy cập trang thì trình duyệt chỉ cần lấy ảnh từ cache thay vì phải tải về ảnh này từ server thêm một lần nữa.

Để làm điều này bạn sẽ cần gán giá trị Cache-Control trong response header trả về cho các yêu cầu tải hình ảnh gửi tới server:

Cache-Control: max-age=31536000

31536000 là thời gian (với đơn vị là tính giây) mà dữ liệu được sẽ được lưu trữ trong bộ nhớ đệm của trình duyệt. Trong trường hợp bạn gán giá trị max-age cho ảnh banner trên là 1 năm, tuy nhiên vài ngày sau đó thì ảnh này được cập nhật và bạn muốn trình duyệt không sử dụng ảnh đã lưu trong cache thì bạn có thể chọn cách làm như sau:

  • Sử dụng một URL khác cho banner. Ví dụ banner cũ là http://my-site.vn/banner.jpg thì bạn có thể đổi lại thành http://my-site.vn/banner-new.jpg

  • Trường hợp bạn không muốn đổi URL thì bạn có thể gắn thêm query string cho URL ví dụ: http://my-site.vn/banner.jpg?time=11112017

  • Và một số cách khác ...

Bạn cũng cần lưu ý thêm rằng một khi đã gán giá trị cho max-age thì trình duyệt sẽ không gửi yêu cầu tới server để lấy lại dữ liệu nữa cho tới khi dữ liệu này có tuổi thọ bằng thời gian quy định bởi max-age.

Trên đây chúng ta đã sử dụng Cache-control để tối ưu tốc độ tải trang cho các lần truy cập thứ 2 trở đi, vậy thì lần truy cập đầu tiên thì có thể dùng chỉ thị này để tăng tốc độ tải trang nhanh hơn được không? Câu trả lời là có!

Thông thường khi phát triển web thì chúng ta sẽ cần sử dụng các thử viện bên ngoài ví dụ như AngularJS, jQuery, Twitter Bootstrap... Nếu như bạn sử dụng các thư viện này từ máy chủ CDN thay vì từ máy chủ của bạn sẽ giúp tăng tốc độ của trang. Lý do giải thích ở đây là bởi vì các thư viện này được dùng phổ biến bởi nhiều trang khác nhau và do đó có khả năng cao là trình duyệt đã lưu lại chúng trong bộ nhớ cache khi người dùng truy cập các trang web khác trước khi đến với trang của bạn. Và các server CDN này cũng sử dụng Cache-Control để lưu trữ dữ liệu trong cache của trình duyệt.

Tăng Tính Bảo Mật Của Trang

Một công dụng khác của Cache-Control đó là giúp tăng tính bảo mật của trang web. Ví dụ trường hợp bạn xây dựng một ứng dụng web có tính năng thanh toán trực tuyến và người dùng cần nhập thông tin về phương thức thanh toán của họ (ví dụ như thẻ ngân hàng) để có thể thực hiện việc mua hàng và thanh toán online.

Trong trường hợp người dùng sử dụng một máy tính ở nơi công cộng và truy cập trang chứa thông tin phương thức thanh toán của họ. Xem xong rồi thì người dùng này bấm vào logout để đăng xuất. Lúc này nếu như có một người khác tiếp theo sử dụng máy tính trên và họ bấm nút quay lại (biểu tượng dấu mũi tên quay lại ở phía góc trái thanh công cụ của trình duyệt) thì bạn cần đảm bảo rằng trình duyệt không hiển thị thông tin thanh toán của người dùng đã logout ngay trước đó.

Để làm việc này thì trong response header trả về cho truy cập tới trang chứa thông tin phương thức thanh toán của người dùng, bạn sẽ gán giá trị cho Cache-Control như sau:

Cache-Control: no-cache, no-store, must-revalidate

Khi đó trình duyệt sẽ không lưu dữ liệu của trang này trong cache và khi người kế tiếp bấn vào nút quay lại để về trang trước đó thì trình duỵệt sẽ gửi lại yêu cầu tới server để lấy thông tin. Tất nhiên do người dùng trước đã đăng xuất server của bạn sẽ yêu cầu người dùng tiếp theo đó nhập thông tin tài khoản của họ (nếu có) để vào được trang.

Kết Luận

Mặc dù ít được biến đến hơn so với các chỉ thị header phổ biến khác tuy nhiên Cache-Control lại đóng một vài trò vô cùng quan trọng trong lập trình ứng dụng web đặc biệt là vấn đề bảo mật. Ở bài viết này chúng ta đã tìm hiểu về hai trong số các công dụng phổ biến của chỉ thị Cache-Control trong giao thức HTTP. Hy vọng bạn sẽ có thể sử dụng những kiến thức này để cải thiện tốt hơn các ứng dụng đã có hoặc áp dụng chúng một cách triệt trong quá trình phát triển các ứng dụng web các lần tới.

Tìm hiểu về http caching

0. Mở đầu

Trong các ứng dụng web, caching là một vấn đề cực kỳ quan trọng để tối ưu hóa performance. Có rất nhiều cách để caching -Page Caching -Action Caching -Fragment Caching -Russian Doll Caching -Managing dependencies -Low-Level Caching -SQL Caching

Nhưng hôm nay mình xin giới thiệu về http caching

Thực ra nếu làm web, với client là browser (chrome, firefox)... thì các bạn có thể bỏ qua bài viết này, hoặc đọc để hiểu rõ hơn. Vì browser đã tự có cơ chế caching này rồi, còn nếu làm ứng dụng APP (Android, IOS), có thể sẽ có bạn không để ý về http caching, dẫn tới việc mất thời gian đi tìm các giải pháp phức tạp.

1. Tại sao dùng http caching

Các trình duyệt xây dựng trong nó hệ thống cache. Mỗi khi ta nhấn nút Back trên trình duyệt thì nó đọc lại cho ta trang ta vừa xem trong cache của nó. Đối tượng cache có thể là các file tạm thời trên đĩa hoặc có thể là trong bộ nhớ trong.

Tư tưởng là khi request 1 file từ host, trước hết ta sẽ load về HTTP header của file. Nếu là lần đầu hoặc, HTTP header thay đổi so với lần trước thì sẽ lấy file từ host về.

Ngược lại, sẽ lấy luôn file trên local.

Dưới đây là một gói HTTP header thông thường

HTTP/1.1 200 OK

Date: Fri, 08 Nov 2013 04:41:40 GMT

Server: Apache/2.2.15 (CentOS)

Last-Modified: Sun, 11 Aug 2013 02:41:48 GMT

Etag: "1c253d-6c2-4e3a2f30b418d"

Accept-Ranges: bytes

Content-Length: 1730

Cache-Control: max-age=1209600

Expires: Fri, 22 Nov 2013 04:41:40 GMT

Keep-Alive: timeout=15, max=100

Connection: Keep-Alive

Content-Type: text/javascript

2. Last-Modified

Ta sẽ so sánh thời gian sửa đổi cuối cùng để xác định xem file này đã cập nhật nội dung nào mới chưa, nếu có thì tải về:

B1: Client —-Last-modified——–> Server

B2: Server: Lục file đó, kiểm tra Last-modified có khớp vs Last-modified của client không, nếu khớp, tới B4.

B3: Nếu không khớp (tức là đã có cập nhật mới), gửi HTTP response chứa file yêu cầu về cho client. Tới B5.

B4: Gửi HTTP response chứa Status Code: 304 Not Modified.

Bước 5: Kết thúc.

3. Etag (Entity Tags)

Khuyết điểm khi dùng Last-Modified:

  • Múi giờ và thời gian của Server phải trùng với của client.

  • Vẫn yêu cầu xử lý trên server.

Do đó, có một cách khác là dùng Etag, nó là một chuỗi duy nhất được sinh ra bằng hash hoặc footprint, tức là mỗi lần có thay đổi là Etag cũng sẽ bị đổi theo.

B1: Client —–Etag—-> Server

B2: Server kiểm tra xem Etag có khớp không, nếu khớp chuyển tới B4.

B3: Nếu không khớp (có cập nhật mới), gửi HTTP response chứa file yêu cầu về cho client. Tới B5.

B4: Gửi HTTP response chứa Status Code: 304 Not Modified.

Bước 5: Kết thúc.

4. Expires

Khuyết điểm của Etag: Vẫn phải kết nối tới server để kiểm tra trong mỗi request.

Ta cần tìm một cách nào đó để browser tự biết khi nào cần tải về. Expires trong HTTP Header cũng giống như khi ta đặt expire time cho session. Browser sẽ tự biết khi nào file đó hết hạn để tài về.

B1: Browser: check Experies, nếu còn trong Expiration thì chuyển tới bước 4.

B2: Nếu đã hết hạn (expired) thì gửi request tới server yêu cầu gửi file về.

B3: Server —–file requested —–> Client. Tới bước 4.

Bước 4: Kết thúc.

Expires headers sử dụng tốt nhất cho caching các hình ảnh tĩnh (như Navigation bar và các button). Do đó ta có thể đặt một khoảng thời gian expired lớn.

5. Cache-Control

Rõ ràng việc sử dụng Expires khiến ta giảm được một lượng đáng kể công việc cho server nhưng thay vào đó, Browser vẫn phải thực hiện những phép tính thời gian và vẫn phải đòi hỏi sự đồng bộ giữa đồng hồ của Web server và của cache.

Mục đích của Cach-Control là để các nhà phát triển có thể kiểm soát tốt hơn nội dung trang web và giới hạn địa chỉ cho Expires.

Cache-Control header bao gồm:

  • max-age=[seconds]: gán thời gian tối đa trước khi file hết hạn. Giá trị này cũng tương tự như Expires và được tính bằng giây.

  • s-maxage=[seconds] : cũng tương tự như max-age nhưng chỉ áp dụng cho những cache được chia sẻ (ví dụ như proxy).

  • public: Đánh dấu rằng gói response này có thể cached. Thông thường người ta set thuộc tính public cho những thông tin không bảo mật và báo cho trình duyệt biết rằng có thể cache. Thông thường, nếu yêu cầu xác thực HTTP thì response sẽ tự động private.

  • private: Chỉ cho phép cache thông tin của một user nào đó vào trình duyệt của họ. Và những shared cache (như proxy) thì không được phép.

6. Các lưu ý để xây dựng một web site cache hiệu quả:

  • Sử dụng những URL duy nhất: Đây là nguyên tắc vàng trong caching. Nếu ta sử dụng một nội dung giống nhau cho trang khác nhau, những người dùng khác nhau hoặc những website khác nhau. Ta nên sử dụng các URL giống nhau. Đây là cách dễ dàng và hiệu quả nhất để trang web của ta có thể dễ dàng caching. Ví dụ, nếu ta sử dụng /index.html để chỉ tới một nội dung thì phải luôn luôn sử dụng nó.

  • Sử dụng một thư viện ảnh chung: như vậy những phần tử khác nhau sẽ dẫn đến những thư viện ảnh đó từ những vị trí khác nhau.

  • Cache lại những hình ảnh và những trang không thay đổi thường xuyên: Sử dụng header Cache-Control: max-age và set cho nó một giá trị thật rộng.

  • Nếu một resource (đặc biệt là một file để download) bị thay đổi, hãy đổi tên của nó: Với cách này, ta có thể giữ nó lâu hơn mà vẫn đảm bảo được phiên bản chính xác đang được cung cấp bằng cách mỗi lần cập nhật phiên bản mới, ta chỉ cần sửa đường dẫn tới nó một chút. Và trang web dẫn tới nó phải là duy nhất và thời gian quá hạn của trang web này phải ngắn.

  • Không thay đổi những file không cần thiết: Khi ta update web site, chỉ thay thế những file được sửa đổi, không nên update toàn bộ trang web vì những file không bị sửa đổi thì thuộc tính Last-Modified của nó sẽ thay đổi và trình duyệt sẽ phải cache lại nó.

  • Chỉ sử dụng cookies khi cần thiết: Cookies rất khó cache và không cần thiết trong nhiều trường hợp. Nếu bạn bắt buộc phải sử dụng cookie thì nên hạn chế dùng nó cho những trang web động.

  • Hạn chế sử dụng SSL: Vì những trang mã hóa không được cache lại, do đó ta chỉ sử dụng chúng trong những trường hợp bắt buộc và không nên sử dụng nhiều hình ảnh cho những trang SSL.

  • Kiểm tra trang web của bạn với: http://redbot.org/ Nó sẽ giúp bạn hiểu hơn những nội dung được trình bày bên trên để optimize trang web của bạn.

6. Kết luận

Với framework Ruby on rails mọi thứ đã sẵn có, chúng ta cũng không cần quan tâm đến điều này lắm. Tuy nhiên, nếu bạn làm APP và gặp phải vấn đề performance khi tải đi tải lại 1 resource (file pdf, img, video...) hãy nghĩ tới http caching.

Tài liệu tham khảo https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/http-caching

Hi vọng bài viết này hữu ích.

Last updated