Kiến thức cơ bản về HTTP Proxy, Cân bằng tải, Bộ đệm và bộ nhớ Cache trên server Nginx

6 năm trước

 

Kiến thức cơ bản về HTTP Proxy, Cân bằng tải, Bộ đệm và bộ nhớ Cache trên server Nginx 

Mở đầu

Trong hướng dẫn này, ta sẽ thảo luận về khả năng hoạt động như một reverse proxy server cho phép Nginx chuyển các yêu cầu yêu cầu cho các backend http server xử lí. Nginx thường được thiết lập như một giải pháp reverse proxy để giúp mở rộng ra cơ sở hạ tầng và chuyển các yêu cầu đến các server khác mà bản thân chúng không được thiết kế để xử lý lưu lượng truy cập trên client lớn.

Xuyên suốt bài viết, ta sẽ đề cập đến cách làm thế nào để mở rộng hệ thống bằng cách sử dụng khả năng cân bằng tải (built-in load balancing) của Nginx, bên cạnh đó là ý niệm về buffering và caching để cải thiện hiệu suất của proxy server cho khách hàng.

 

Sơ lược về Proxy

Nếu bạn đã chỉ được sử dụng các máy chủ web trong quá khứ với các cấu hình đơn giản và duy nhất, bạn có thể tự hỏi tại sao bạn sẽ cần phải proxy( ủy quyền) các yêu cầu truy cập web.

Một lý do để thực hiện proxy các server khác với Nginx là khả năng mở rộng quy mô ra cơ sở hạ tầng. Nginx được xây dựng để xử lý nhiều kết nối đồng thời cùng một lúc. Điều này làm cho nó lý tưởng cho là điểm liên lạc chung cho client. Các server có thể chuyển các yêu cầu đối với bất kỳ số lượng backend server nào đến nó để xử lý phần lớn công việc, sau đó phản hồi các truy cập từ bất cứ đâu trên cơ sở hạ tầng của bạn. Thiết kế này cũng cung cấp cho bạn sự linh hoạt và dễ dàng hơn trong việc bổ sung thêm backend server để mở rộng hệ thống cũng như gỡ nó xuống khi cần thiết để bảo trì.

Một trường hợp về lợi ích mà http proxy có thể mag lại là khi sử dụng một application server có thể không được xây dựng để xử lý yêu cầu trực tiếp từ client. Nhiều framework bao gồm các web server, nhưng hầu hết chúng không có khả năng xử lí mạnh mẽ như các server được thiết kế cho lưu lượng sử dụng cao như Nginx. Việc đặt Nginx trước những server này có thể dẫn đến trải nghiệm tốt hơn cho người dùng và tăng cường an ninh.

Proxying trong Nginx được thực hiện bằng cách hướng yêu cầu đến Nginx server và gởi nó đến các server khác để xử lí. Kết quả của yêu cầu được truyền lại cho Nginx, sau đó chuyển tiếp thông tin cho client. Các máy chủ khác trong trường hợp này có thể là remote machine, local server, hoặc thậm chí cả các virtual server được xây dựng bên trong Nginx. Các server được kết nối với Nginx như trên được gọi là upstream server.

Nginx có thể ủy quyền yêu cầu đến các server liên kết với nó bằng giao thức http(s),FastCGI, SCGI, và uwsgi hoặc các memcached protocol thông qua bộ chỉ thị cho từng loại proxy. Trong hướng dẫn này, ta sẽ tập trung vào các giao thức http. Ví dụ Nginx chịu trách nhiệm truyền yêu cầu và "nắn" các thành phần tin nhắn vào một định dạng mà các upstream server có thể hiểu.

 

Tái cấu trúc một HTTP Proxy Pass cơ bản

Loại proxy cơ bản và trực tiếp nhất là chuyển yêu cầu đến một server đơn có thể liên lạc với proxy server qua giao thức http được biết đến như một "proxy pass" chung được xử lí bằng một lệnh tên là proxy_pass .

Lệnh proxy_pass được tìm thấy chủ yếu trong các giao tiếp với thuộc tính vị trí (location context). nó cũng hiệu quả trong các khối if bên trong location context và trong thuộc tính limit_except. Khi một yêu cầu khớp với địa chỉ trong proxy_pass nhất định, yêu cầu đó sẽ được điều hướng đến URL trong location context đó.

Dưới đây là một ví dụ:

# server contextlocation /match/here {
proxy_passhttp://example.com;
}. . .

Ở snippet cấu hình trên, không URI nào được thêm vào dưới  server trong phần định nghĩa của proxy_pass. Để khớp với mẫu này, URI được yêu cầu bởi client sẽ được chuyển đến upstream server as-is.

Ví dụ, khi một yêu cầu đến /match/here/please được xử lí, URI sẽ được gửi đến server chứa example.com ở địa chỉ http://example.com/match/here/please.

 

Một cách triển khai khác như sau:

# server contextlocation /match/here {
proxy_passhttp://example.com/new/prefix;
}. . .

Ở ví dụ trên,  proxy server được gắn với một URI segment (/new/prefix). Khi một URI được ghi trong proxy_pass , phần yêu cầu khớp với location context  được thay thế bằng URI.

Ví dụ, một yêu cầu đến /match/here/please trên Nginx server sẽ được chuyển đến upstream server ở địa chỉ http://example.com/new/prefix/please/match/here sẽ được thay thế bằng /new/prefix

Đôi khi, kiểu thay thế này không thể thực hiện được. Khi đó, URI ở phần dưới của proxy_pass sẽ bị bỏ qua và cả URI từ client hay URI được xử lí bởi các lệnh khác cũng được chuyển tới upstream server.

Chẳng hạn, khi vị trí được khớp bằng việc sử dụng những phép toán thông thường, Nginx không thể xác định phần nào của URI khớp với biểu thức, do đó, nó gửi các URI gốc của client. Một ví dụ khác là khi một lệnh rewrite được sử dụng trong cùng một vị trí, khiến URI của client được viết lại, nhưng vẫn được xử lý trong cùng một khối. Trong trường hợp này, URI được viết lại sẽ được thông qua.

Quá trình xử lí các Header của Nginx

Một điều không dễ thấy ngay là sự quan trọng cảu việc truyền nhiều thông tin hơn hơn chỉ là URI ,nếu bạn mong đợi upstream server xử lý các yêu cầu đúng cách. Các yêu cầu đến từ Nginx thay mặt cho client sẽ khác một yêu cầu trực tiếp từ client. Một phần quan trọng của việc này là các header  đi cùng với yêu cầu.

Khi Nginx ủy quyền một yêu cầu, nó tự động thay đổi header của yêu cầu nhận được từ client:

  • Nginx không xử lí các header rỗng. Vì không có gì để chuyển đến các server, việc xử lí những yêu cầu này chỉ làm chậm quá trình xử lí và tốn tài nguyên.
  • Nginx xem tất cả các header chứa dấu "_" là không hợp lệ và sẽ loại bỏ chúng. Nếu muốn Nginx chuyển các yêu cầu này đến các server, bạn cần đặt đặt kích hoạt thuộc tính underscores_in_headers
  • Header "Host" sẽ đưuọc viết lại thành giá trị của $proxy_host. Nó sẽ là điạ chỉ IP hoặc tên của host cùng số hiệu cổng của upstream server như trong proxy_pass
  • Header "Connection" sẽ được chuyển thành "close". Nó được dùng để ra hiệu cho kết nối riêng biệt giữa hai party . Ở đây, Nginx đặt giá trị cho nó là "close" để báo cho upstream server rằng kết nối sẽ được ngắt khi yêu cầu gốc được phản hồi.

Vấn đề đầu tiên có thể suy ra từ trên là bất kỳ header nào bạn không muốn thông qua nên được nhận giá trị rỗng. Các header với các giá trị rỗng hoàn toàn bị loại bỏ..

Tiếp theo là nếu ứng dụng backend của bạn sẽ những header không đúng chuẩn, bạn phải chắc chắn chúng không có dấu gạch dưới. Nếu bạn cần header sử dụng một dấu gạch dưới, bạn có thể thiết lập giá trị của underscores_in_headers thành "on" (có giá trị hoặc với giao thức http hoặc với các giá trị mặc định cho việc kết hợp địa chỉ IP và cổng của server). Nếu bạn không làm điều này, Nginx sẽ gắn cờ không hợp lệ cho các yêu cầu của bạn, và chúng sẽ không được xử lí.

Header "Host" là thành phần quan trọng nhất của quá trình proxy. Mặc định giá trị của nó sẽ là $proxy_host, chứa tên miền hoặc địa chỉ IP kèm với cổng truy cập được ghi trong proxy_pass. Giá trị này được chọn mặc định vì nó là địa chỉ duy nhất Nginx có thể chắc chắn rằng upstream server sẽ phản hồi đến (vì nó được lấy trực tiếp từ các thông tin kết nối).

Các giá trị thường gặp của Host:

  • $proxy_host: Đặt giá trị cho "Host" header là tên miền hoặc địa chỉ IP cùng với cổng trong proxy_pass
  • $http_host: Đặt "Host" header thành "Host" header của yêu cầu của client. Header được gửi bởi client luôn luôn hợp lệ và được Nginx coi như các biến. Biến này bắt đầu với tiền tố $http_ , theo sau là tên header ở định dạng chữ viết thường , với các dấu "/" được thay bằng dấu "_". Mặc dù biến $http_host hoạt động trong hầu hết thời điểm, khi yêu cầu của không có một "Host" header hợp lệ, việc chuyển yêu cầu có thể bị chặn.
  • $host: Biến này tham chiêu đến tên host của yêu cầu gốc, "Host" header của yêu cầu từ client , hoặc tên server khớp với yêu cầu.

Trong hầu hêt trường hợp, "Host" header sẽ được đặt là biến $host vì nó linh hoạt và thường cung cấp cho các upstream server một "Host" header chính xác.

 

Đặt giá trị hoặc or Reset Header

Để thay đổi header cho kết nối proxy, ta có thể sử dụng lệnh proxy_set_header. Ví dụ, nếu muốn thay đổi "Host" header như đã đề cập ở trên, và thêm một số header thông thường với các yêu cầu proxy khác, ta có thể dùng đoạn mã sau:

# server contextlocation /match/here {
proxy_set_header HOST $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_passhttp://example.com/new/prefix;
}. . .

Yêu cầu trên đặt "Host" header là biến $host, chứa thông tin về host gốc được yêu cầu. X-Forwarded-Proto cung cấp thông tin cơ bản cho các upstream server về các yêu cầu của client ( khi nó là yêu cầu http hoặc https).

Header X-Real-IP là địa chỉ IP của client để proxy server ra quyết định xử lí hoặc ghi log thế nào với yêu cầu. Header X-Forwarded-For là một danh sách chứa địa chỉ IP của tất cả server mà client từng được ủy nhiệm. Ở ví dụ trên, ta đã đặt nó cho biến $proxy_add_x_forwarded_for. Biến này nhận giá trị gốc của header X-Forwarded-For được thu thập từ client và thêm địa chỉ IP của Nginx server vào cuối.

Ta có thể chuyển lệnh proxy_set_header ra bên ngoài phạm vi của server hay http, cho phép nó hoạt động với nhiều địa chỉ:

# server contextproxy_set_header HOST $host;proxy_set_header X-Forwarded-Proto $scheme;proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;location /match/here {
proxy_passhttp://example.com/new/prefix;
}location /different/match {
proxy_passhttp://example.com;
}
 

Định nghĩa một Upstream Context để Cân bằng tải cho Kết nối Proxy

Trong ví dụ trước, chúng tôi biết làm thế nào để thực hiện một http proxy đơn giản đến một backend server duy nhất. Nginx cho phép chúng ta dễ dàng mở rộng cấu hình này ra bằng cách xác định toàn bộ backend server mà ta có thể chuyển yêu cầu tới.

Với lệnh upstream , ta có thể định nghĩa một tập hợp các server, với giả sử tất cả các server này đều có khả năng xử lí các yêu cầu. Điều này cho phép mở rộng cơ sở hạ tầng mà không gặp quá nhiều khó khăn.

Dưới đây là một ví dụ đơn giản:

# http contextupstreambackend_hosts {
server host1.example.com;
server host2.example.com;
server host3.example.com;
}server {
listen80;
server_name example.com;
location /proxy-me {
proxy_passhttp://backend_hosts;
}}

Trong ví dụ trên, ta đã thiết lập một upstream context là backend_hosts. Khi đã được xác định, tên này sẽ có sẵn để sử dụng trong phạm vi ủy quyền đi như thể nó là một tên miền thông thường. Như bạn có thể thấy, trong server block , ta có thể chuyển bất kỳ yêu cầu nào đến example.com/proxy-me/... tới pool được chỉ ra ở trên. Trong "pool" đó, một host được chọn bằng cách áp dụng một thuật toán có thể cấu hình. Theo mặc định, đây chỉ là một quá trình lựa chọn round-robin đơn giản (mỗi yêu cầu sẽ lần lượt được chuyển đến một máy chủ khác nhau).

Thay đổi thuật toán Cân bằng tải trên Upstream Server 

Bạn có thể thay đổi thuật toán cân bằng tải với các lệnh hoặc cờ sau:

  • (round robin): Thuật toán mặc định được sử dụng nếu không có thuật toán khác. Mỗi server trong upstream context được truyền các yêu cầu đến một cách lần lượt.
  • least_conn: Chỉ định rằng các kết nối mới luôn nên được chuyển đến các backend mà có số lượng các kết nối hoạt động ít nhất để đảm bảo cân bằng. Điều này đặc biệt hữu ích trong trường hợp kết nối vào backend có thể kéo dài một thời gian.
  • ip_hash: Thuật toán cân bằng này phân phối yêu cầu đến các máy chủ khác nhau dựa trên địa chỉ IP của client. Ba octet đầu tiên được sử dụng như một chìa khóa để quyết định trên máy chủ để xử lý các yêu cầu. Kết quả là client có xu hướng được phục vụ bởi mỗi máy chủ một lần, có thể hỗ trợ cho việc luân phiên hệ thống.
  • hash: Thuật toán cân bằng này được sử dụng chủ yếu với các ủy quyền sử dụng bộ nhớ đệm. Các máy chủ được chia dựa trên giá trị của một hash key ngẫu nhiên. Hash key này có thể là văn bản, biến, hoặc kết hợp cả hai. Đây là phương pháp cân bằng duy nhất yêu cầu người dùng cung cấp dữ liệu, chính là hash key được sử dụng.

Sau khi thay đổi thuật toán, khối upstream thu được sẽ như sau:

# http contextupstreambackend_hosts {
least_conn;
server host1.example.com;
server host2.example.com;
server host3.example.com;
}. . .

Ở ví dụ trên, server sẽ được lựa chọn ưu tiên theo số lượng kết nối đang có.

Với phương pháp hash, bạn sẽ cần cung cấp key như sau:

# http contextupstreambackend_hosts {
hash$remote_addr$remote_port consistent;
server host1.example.com;
server host2.example.com;
server host3.example.com;
}. . .

Đặt giá trị Weight cho Server để Cân bằng tải

Trong phần mô tả các backend server, mặc định các server sẽ có "tải trọng" như nhau, nghĩa là giả sử rằng mỗi server có thể xử lí lượng tải như nhau. Tuy nhiên bạn có thể thay đổi tải trọng của server trong phần mô tả của nó như sau:

# http contextupstreambackend_hosts {
server host1.example.com weight=3;
server host2.example.com;
server host3.example.com;
}. . .

Ở ví dụ trên, host1.example.com sẽ phải nhận tải trọng gấp 3 lần 2 server còn lại. Mặc định, mỗi server có tải trọng là 1.

 

Dùng Bộ đệm để Giải phóng Backend Server

 

Một vấn đề với proxy liên quan đến nhiều người sử dụng là tác động lên hiệu suất của việc thêm một máy chủ bổ sung cho hệ thống. Trong hầu hết các trường hợp, điều này có thể được giảm nhẹ chủ yếu bằng cách tận dụng khả năng đệm và bộ nhớ cache của Nginx.

Khi proxy đến một  server khác, tốc độ của hai kết nối sau sẽ ảnh hưởng đến trải nghiệm của người dùng:

  • Kết nối từ client đến Nginx proxy.
  • Kết nối từ Nginx proxy đến backend( upstream ) server.

Nếu không có buffer, dữ liệu được gửi từ máy chủ proxy và ngay lập tức bắt đầu được chuyển đến cho client. Nếu giả định client có tốc độ cao ,bộ đệm có thể được tắt để dữ liệu được chuyển đến client càng sớm càng tốt. Với bộ đệm, Nginx proxy sẽ tạm thời lưu trữ phản hồi của backend và sau đó đưa dữ liệu này đển client. Nếu client chậm, điều này cho phép máy chủ Nginx để đóng kết nối đến backend sớm hơn. Sau đó nó có thể xử lý phân phối dữ liệu cho client ở bất cứ tốc độ nào có thể.

Nginx mặc định có một thiết kế bộ đệm vì client thường có tốc độ kết nối rất khác nhau. Chúng ta có thể điều chỉnh hành vi đệm với các chỉ dẫn sau. Đây có thể được thiết lập trong môi trường http, server, hoặc vị trí. Điều quan trọng là cần lưu ý rằng các chỉ thị kích thước được cấu hình theo mỗi yêu cầu, do đó tăng chúng vượt nhu cầu của bạn có thể ảnh hưởng đến hiệu suất của bạn khi có quá nhiều yêu cầu:

  • proxy_buffering: Lệnh này điều khiển bộ đệm cho buffering cho context và các context con của nó. Mặc định, giá trị của nó là "0".
  • proxy_buffers: Lệnh này giới hạn số lượng ( tham số đầu) và kích cỡ (tham số sau) của bộ đệm cho một phản hồi proxy. Mặc định sẽ có 8 buffers với kích thước bằng một trang nhớ( memory page) ( 4k hoặc 8k). Tăng số lượng buffer cho phép buffer nhiều thông tin hơn.
  • proxy_buffer_size: Phần khởi tạo của phản hồi từ backend server, chứa các header được buffer một cách riêng biệt với các phản hồi khác. Lệnh này đặt kích cỡ cho các phản hồi này. Mặc định nó có cùng giá trị với proxy_buffers, nhưng vì được dung cho các thông tin về header, nó có thể nhận giá trị nhỏ hơn.
  • proxy_busy_buffers_size: Đặt kích cỡ lớn nhất của buffer có thể được đánh dấu là "client-ready" và do đó đang bận. Khi một client chỉ có thể đọc dữ liệu từ một bộ đệm tại một thời điểm, các bộ đệm sẽ được đặt ở một hàng đợi và sẽ được xử lí lần lượt. Lệnh này kiểm soát số buffer tối đa được phục vụ cùng lúc.
  • proxy_max_temp_file_size: Giá trị lớn nhất của một file tạm thời được lưu trên ổ đĩa với mỗi yêu cầu. Chúng được tạo khi phản hồi từ upstream quá lớn để được lưu trong buffer.
  • proxy_temp_file_write_size: Lượng dữ liệu Nginx sẽ ghi đến file ở trên trong một thời điểm.
  • proxy_temp_path: Đường dẫn đến thư mục hoặc vùng nhớ để lưu file đó.

Dễ thấy rằng Nginx cung cấp nhiều lệnh để điều khiển hành vi của bộ đệm. Trong hầu hết trường hợp, bạn không cần quan tâm đến đa số các lệnh ở đây nhưng chúng có thể có ích nếu bạn muốn tinh chỉnh để tối đa hiệu suất. Hai lệnh cần lưu ý là proxy_buffers và proxy_buffer_size .

Ví dụ về việc tăng các proxy buffer được sử dụng cho mỗi yêu cầu upstream trong khi giảm kích cỡ của bộ đệm lưu các header:

# server contextproxy_bufferingon;proxy_buffer_size1k;proxy_buffers244k;proxy_busy_buffers_size8k;proxy_max_temp_file_size2048m;proxy_temp_file_write_size32k;location / {
proxy_passhttp://example.com;
}

Bình thường, nếu bạn có các client có khả năng xử lí nhanh và muốn ngay lập tức phục vụ chúng ,hãy tắt bộ đệm để giảm thời gian xử lí. Nginx thực tế vẫn sử dụng bộ đệm khi upstream có tốc độ nhanh hơn client, nhưng nó sẽ ngay lập tức chuyển dữ liệu đến client thay vì đợi buffer. Nếu client có tốc độ xử lí chậm, việc này có thể khiến kết nối của upstream sẽ được mở cho đến khi client nhận được dữ liệu. Khi bộ đệm tắt, chỉ bộ đệm được định nghĩa bởi proxy_buffer_size được sử dụng:

# server contextproxy_bufferingoff;proxy_buffer_size4k;location / {
proxy_passhttp://example.com;
}

Tính khả dụng cao (High availability) (tùy chọn)

Quá trình xử lí proxy của Nginx sẽ có hiệu suất cao hơn với việc thêm vào các bộ cân bằng tải.

Một thiết lập high availability (HA) là một hệ thống không có một điểm lỗi nào, và các bộ cân bằng tải là bắt buộc đẻ có được điều đó. Với việc có nhiều hơn một cân bằng tải, bạn sẽ tránh được thời gian chết khi một trong số chúng gặp vấn đề hoặc cần bảo trì.

Đay là một lược đồ cho một hệ thống high availability:

HA Setup

Trong ví dụ này, bạn có nhiều bộ cân bằng tải (một trong 2 được kích hoạt) phía sau một Floating IP có thể kết nối các server với nhau. Yêu cầu từ client request được truyền từ Floating IP đến bộ cân bằng tải đang hoạt động, sau đó là các backend server.

 

Cấu hình Proxy Caching để Giảm thời gian Phản hồi

Trong khi bộ đệm giúp ta giải phóng backend server để xử lí nhiều yêu cầu hơn, Nginx cũng cung cấp thêm một thành phần cache ở backend server để loại bỏ bớt các yêu cầu đến các server ấy.

Cấu hình Proxy Cache

Để thiết lập một bộ nhớ cache,ta có thể sử dụng lệnh proxy_cache_path. Lệnh này tạo ra một vùng nhớ trên các server để lưu lại dữ liệu với các yêu cầu. Lệnh proxy_cache_path phải được đặt trong http context.

Một ví dụ cho lệnh trên là:

# http contextproxy_cache_path /var/lib/nginx/cache levels=1:2 keys_zone=backcache:8m max_size=50m;proxy_cache_key"$scheme$request_method$host$request_uri$is_args$args";proxy_cache_valid20030210m;proxy_cache_valid4041m;

Với proxy_cache_path , ta đã tạo một thư mục trên hệ thống file dành cho việc lưu cache, ở đây là /var/lib/nginx/cache .Nếu thư mục này không tồn tại, ta cần tạo ra nó và cấp đủ các quyền với lệnh:

sudo mkdir -p /var/lib/nginx/cache
sudo chown www-data /var/lib/nginx/cache
sudo chmod 700 /var/lib/nginx/cache

Thông số levels= xác định cách tổ chức cache. Nginx sẽ tạo một cache key bằng cách băm giá trị của key bên dưới đây. "levels" ra lệnh tạo ra một thư mục kí tự đơn (là giá trị cuối cùng của mã băm) với một thư mục con 2 kí tự (là hai kí tự tiếp theo từ phía cuối của mã băm). Những thư mục này sẽ giúp Nginx nhanh chóng tìm ra thư mục chứa key..

keys_zone= cho biết tên của vùng cache, mà ta sẽ gọi là backcache. Đây cũng là lệnh đặt dung lượng cho vùng lưu các metadata, ở đây ta sẽ lưu 8 MB key và với mỗi megabyte, Nginx có thể lưu trữ khoảng 8000 entry. max_size đặt giá trị cho vùng nhớ cache tổng cộng.

Một thông số khác được sử dụng là proxy_cache_key. Nó đưuọc dùng để đặt key được dùng để lưu cache, chính là key được xử dụng để kiểm tra xem một yêu cầu có thể được phục vụ từ bộ nhớ cache hay không.

proxy_cache_valid là lệnh có thể nhận nhiều giá trị khác nhau. Nó cho phép ta cấu hình để dữ liệu sau mỗi khoảng thời gian bao lâu dựa vào mã trạng thái. Ở đây ta sẽ lưu các truy cập thành công vào cache trong 10 phút, và vô hiệu cache bằng thông điệp 404 mỗi phút.

Giờ sau khi đã cấu hình vùng cache, ta cần chỉ cho Nginx nơi sử dụng cache:

# server contextlocation /proxy-me {
proxy_cache backcache;
proxy_cache_bypass$http_cache_control;
add_header X-Proxy-Cache $upstream_cache_status;
proxy_passhttp://backend;
}. . .

Sử dụng proxy_cache ta có thể chỉ đinh vùng cache backcache sẽ được sử dụng cho context này. Nginx sẽ kiểm tra entry hợp lệ trước khi chuyển nó tới backend.

proxy_cache_bypass được đặt là biến $http_cache_control. Nó sẽ chứa một bộ phận chỉ ra khi nào client được yêu cầu một phiên bản mới không qua cache của tài nguyên. Đặt giá trị cụ thể cho nó giúp Nginx xử lí chính xác các yêu cầu của client.

Ta cũng thêm vào một header là X-Proxy-Cache. Giá trị của header này là $upstream_cache_status. Cơ bản thì header cho phép ta biết khi nào yêu cầu cho một cache hit, cache miss, hay yêu cầu đi vòng qua mà không bị cache. Việc này rất có ích trong quá trình debug.

Chú ý về kết quả Cache

Bộ nhớ đệm có thể tăng hiệu suất của hệ thống. Tuy nhiên có vài điều cần lưu ý khi cấu hình một bộ nhớ cache để đạt hiệu quả cao nhất.

Đầu tiên, tất cả các dữ liệu liên quan đến người dùng không nên được cache, vì nó có thể làm lộ thông tin người dùng này đến người dùng khác.

Nếu trang web của bạn có một vài thành phần động, bạn cần có một tài khoản cho nó ở backend server. Cách xử lí chúng tùy thuộc vào ứng dụng hay server nào đang đảm nhận quá trình xử lí ở backend. Với các thành phần cá nhân, bạn nên đặt header Cache-Control thành "no-cache", "no-store", hoặc "private" tùy thuộc vào dữ liệu:

  • no-cache: Chỉ ra rằng yêu cầu sẽ không được phản hồi lại nếu không kiểm tra rằng không có thay đổi dữ liệu trên backend. Nó có thể được sử dụng với các dữ liệu động và quan trọng. Một ETag hashed metadata header sẽ được kiểm tra với mỗi yêu cầu và giá trị trước đó sẽ được phục vụ nếu trả về cùng một giá trị hash.
  • no-store: Dữ liệu nhận được sẽ không bao giờ được cache. Đây là lựa chọn an toàn nhất cho các dữ liệu cá nhân, và các dữ liệu đó sẽ được lấy trực tiếp từ backend mỗi lần truy cập.
  • private: Không vùng cache được chia sẻ giữa các client nào được phép cache dữ liệu này. Nó sẽ có ích trong trường hợp trình duyệt của người dùng có thể cache dữ liệu, nhưng proxy server sẽ không quan tâm và không thể truy cập vào nó.
  • public: Dữ liệu có thể được cache bất cứ lúc nào.

Một header có thể kiểm soát việc này là max-age, quyết định tài nguyên sẽ được cache sau khoảng thời gian bao nhiêu lâu.

Nếu backend cũng sử dụng Nginx, bạn có thể thêm vào một số thứ khác qua lệnh expires ,nó cho phép đặt giá trị của max-age cho Cache-Control:

location / {
expires60m;
}location /check-me {
expires -1;
}

Khối đầu tiên cho biêt dữ liệu sẽ được cache trong vòng một giờ. Khối thứ hai đặt giá trị cho Cache-Control header thành "no-cache". Để đặt các giá trị khác, bạn có thể dùng add_header như sau:

location /private {
expires -1;
add_header Cache-Control "no-store";
}
 

Tổng kết

Nginx đứng đầu trong danh sách các reverse proxy vì nó vừa có khả năng hoạt động như một web server và như một reverser proxy server với hiệu suất cao cùng nhiều lệnh hỗ trợ. Việc ủy quyền yêu cầu đến các server khác được thực hiện một cách trực tiếp và dễ dàng. Nginx bên cạnh đó cũng rất ling động, cho phép nhiều lệnh cấu hình phức tạp để đáp ứng yêu cầu nếu cần thiết.