til icon indicating copy to clipboard operation
til copied to clipboard

HTTP Caching in REST APIs

Open xluffy opened this issue 3 years ago • 0 comments

Thông thường, trình duyệt sẽ coi như tất cả các GET request có thể cache, trong khi với POST request thì mặc định ko thể cache, nhưng ta có thể khiến POST request có thể cache bằng cách set header Expires hoặc Cache-Control. Đối với PUT và DELETE request thì không thể cache trong tất cả các trường hợp.

Lợi ích của dùng HTTP caching là

  • Tiết kiệm bandwidth
  • Giảm latency
  • Giảm workload từ server
  • Tránh network failure

Các HTTP header được sử dụng cho HTTP caching chia làm nhiều loại:

  • Cache-control header
  • Expires header
  • Validators (ETag (Entity Tag), Last-Modified)

Với Cache-ControlExpires header, nếu thoả điều kiện, client sẽ không cần gửi request lên server và server nếu ko phải nhận request thì cũng không cần phải xử lý. Điều này giúp tiết kiệm băng thông, không cần chờ response từ server và server cũng không phải tốn công xử lý. Vấn đề của 2 header này là chỉ hữu ích cho static content vì ta sẽ không biết khi nào dữ liệu thay đổi để cập nhật lại. Với REST API vẫn có thể áp dụng được nếu các dữ liệu ít thay đổi. Ví dụ list country, state trả về bởi server khi đăng ký.

Vấn đề của 2 loại header này là nếu cần cập nhật dữ liệu, ta không thể ra lệnh cho client xoá dữ liệu cache cũ và cập nhật mới đc. Có thể giải quyết bằng một cách đó là set s-maxage và mỗi lần có nhu cầu cập nhật dữ liệu, gọi API để xoá cache ở CDN hoặc yêu cầu proxy xoá cache.

Với Validators client vẫn phải gửi request lên server và server vẫn phải xử lý dữ liệu, tuy nhiên, nếu giá trị trong header trùng nhau hoặc thoả điều kiện, thì thay vì response data + HTTP code 200 về client, server sẽ chỉ trả về HTTP code 304 Not Modified không kèm dữ liệu, và client sẽ tự xử. Điều này giúp tiết kiệm băng thông (vì request trả về ko có dữ liệu), đồng thời do không có dữ liệu trả về nên cũng response nhanh hơn.

Ví dụ về Cache-Control: public, max-age=86400, s-maxage=86400 header

  • max-age tính bằng giây, là độ tuổi tối đa của cache, sau thời gian này, client sẽ cần làm mới lại data, ở ví dụ trên là 1 ngày.
  • s-maxage với s nghĩa là shared-cache ví dụ như proxy-cache hoặc CDN, tương tự max-age, đây cũng là thời gian tồn tại của cache tại shared-cache và sau thời gian này, data cũng cần được làm mới.
  • public thể hiện rằng tài nguyên này có thể cache được.

Ví dụ về Expires: Sun, 03 May 2025 23:02:37 GMT, với giá trị của header này là timestamp, thể hiện thời gian sẽ hết hạn của tài nguyên và sau thời gian này, client cũng cần làm mới dữ liệu.

Về độ ưu tiên, Cache-Control sẽ có độ ưu tiên cao hơn Expires.

Với ETag, client sẽ gửi một request dữ liệu lên server, server trả về dữ liệu kèm một header là ETag như sau:

HTTP/1.1 200 OK
ETag: "06da1bee0444eaaa5298b00c27636ccd"
Content-Type: application/json;charset=UTF-8
Content-Length: 52

ETag có thể là một giá trị băm hoặc finger của dữ liệu nhưng nói chung client không cần biết giá trị này được tính toán như thế nào (ETag có prefix là W/06da1bee0444eaaa5298b00c27636ccd gọi là weak validators).

Ở request tiếp theo, client cần truyền lên header If-None-Match: 06da1bee0444eaaa5298b00c27636ccd, với giá trị là giá trị của ETag ở request trước. Server sẽ tính toán và nếu dữ liệu không có gì thay đổi, server sẽ trả về HTTP/1.1 304 Not Modified và body rỗng.

Tương tự với Last-Modified, khi client lần đầu request dữ liệu, server sẽ trả kèm dữ liệu một header như sau:

HTTP/1.1 200 OK
Last-Modified: Fri, 04 Jun 2021 03:29:49 GMT
Content-Type: application/json;charset=UTF-8
Content-Length: 52

Ở request tiếp theo, client truyền header If-Modified-Since: Last-Modified, ví dụ If-Modified-Since: Fri, 04 Jun 2021 03:29:49 GMT, server nếu thấy dữ liệu không có thay đổi từ lần cuối cùng xử lý, server sẽ trả về HTTP/1.1 304 Not Modified và body rỗng.

Code Rails mình hoạ cho ETag và Last-Modified

def show
  @product = Product.find(params[:id])

  if stale?(last_modified: @product.updated_at.utc, etag: @product)
    respond_to do |wants|
      # ... normal response processing
    end
  end
end

xluffy avatar Jun 14 '21 11:06 xluffy