Tuesday, December 31, 2013

Cấu hình mod caching trên nginx

A. Name-based virtual servers
Nginx sẽ quyết định server block nào sẽ xử lý khi một request tới. Chẳng hạn như ta có các dòng config sau:
server { listen       80; server_name  nginx.org  www.nginx.org; ... }
server {
 listen       80;
 server_name  nginx.net  www.nginx.net;
 ...
 }
server { listen       80; server_name  nginx.com  www.nginx.com; ... }
trong những cấu hình trên, nginx sẽ dựa vào trường server_name để để xác định request sẽ được chuyển cho server block nào xử lý. Nếu như trong Host header của request không có trùng với bất cứ một server_name nào thì nó sẽ được đưa cho default_server như sau:
server { listen       80  default_server; server_name  nginx.net  www.nginx.net; ... }
Nếu không có chỉ định trên, default_server sẽ là server block đầu tiên như trong ví dụ thứ 1 thì là :
server { listen       80; server_name  nginx.org  www.nginx.org; ... }
B. Làm thế nào để chặn các request không có Host name trùng với server_name
Ta có thể dùng một đoạn cấu hình ngắn như sau:
server { listen       80; server_name  ""; return       444; }
C. Cấu hình 
Chúng ta xem một đoạn cấu hình PHP đơn giản như sau:
server { listen        80; server_name   nginx.org  www.nginx.org; root          /data/www;
location / {
 index     index.html  index.php;
 }
location ~* .(gif|jpg|png)$ {
 expires   30d;
 }
location ~ .php$ { fastcgi_pass   localhost:9000; fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name; include        fastcgi_params; } }
trong ví dụ trên bất kể thứ tự ra sao, nginx sẽ xử lý request tới như sau:
+ location “/” phù hợp với mọi request nhưng nó là literal string location nên nó sẽ được sử dụng cuối cùng.
+ Sau đó nginx sẽ tìm các location với regular expression được liệt kê theo thứ tự trong file config, ứng với cái nào thì nginx sẽ dùng lại và dùng
các chỉ thị ở đó.
+ Nếu không có regular expression nào phù hợp nginx sẽ tìm literal string location nào đầu tiên.
Ngoài ra nginx không quan tâm đến các biến chuổi đi theo sau như:
/index.php?user=john&page=1
/index.php?page=1&user=john
Một vài ví dụ
+ Request /logo.gif ứng với literal location “/” và regular expression “.(gif|jpg|png)$” nên nó sẽ được xử lý bởi
location ~* .(gif|jpg|png)$ { expires   30d; }
Ở trên có khai báo
root /data/www nên request trên sẽ ứng với /data/www/logo.gif và nó sẽ được gửi trả lại cho client.
+ Request /index.php ứng với literal string locaiont “/” và regular expression “.(php)$” nên nó sẽ được xử lý bởi
location ~ .php$ { fastcgi_pass   localhost:9000; fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name; include        fastcgi_params; }
request trên sẽ được đưa qua FastCGI server ở localhost:9000. Chỉ định fastcgi_param sẽ gán SCRIPT_FILENAME thành “/data/www/index.php” và sau đó
FastCGI sẽ xử lý yêu cầu đó.
Xem thêm $document_root và $fastcgi_script_name trên trang wiki của nginx.
+ Request “/about.html” chỉ ứng với location “/” nên nginx tương tự trả về cho người dùng file“/data/www/about.html”
+ Request “/”. Ứng với location “/”, nó sẽ dựa vào chỉ thị index để tìm file index.php trong /data/www/. Nếu filenày tồn tại, request lúc này sẽ trở
thành “/index.php”, sau đó nginx sẽ tiếp tục quá trình tìm kiếm location phù hợp.
Ghi chú: literal string là một chuỗi đơn
2. Các thông số cơ bản của caching nginx
- Khai báo thư mục lưu cache (nằm trong phân đoạn http):
proxy_cache_path /tmp/nginx_cache levels=2 keys_zone=cache_one:500m inactive=1d max_size=2048m;
/tmp/nginx_cache là thư mục chứa cache, bạn có thể để thư mục này trên RAM nếu cần truy xuất nhanh và RAM dư giả. Sử dụng MD5 để lưu tên cache (key_cache) .
level=2 phân cấp thư mục lưu cache
key_zone=cache_one:500m tên của cache zone là cache_one
inactive=1d: thành phần cache nào không được truy cập trong vòng 1 ngày sẽ bị xóa.
Ngoài ra vì khi cache, nginx tạo ra rất nhiều file tạm và file cache nên bạn cần đặt tham số worker_rlimit_nofile cao cao một chút hoặc sử dụng lệnh ulimit.
- Cấu hình cache cho website ( phần này nằm trong phân đoạn server )
Đây là phần quan trọng nhất của việc cấu hình cache, mình xin giới thiệu sơ qua một số tham số quan trọng:
proxy_cache cache_one : chỉ định cache vào zone cache_one
proxy_cache_valid 200 15m : các request có file tồn tại (200) sẽ có giá trị cache trong vòng 15m.
proxy_cache_key $host$request_uri : chỉ định key cache, giúp nginx lần được cache của một URL ở đâu trong thư mục cache.
proxy_set_header X-Forwarded-For $remote_addr: dùng để thêm một dòng trong header trả về cho client.
proxy_ignore_headers : cái này để thông báo cho nginx bỏ qua những header nào của client. Trong các header của người dùng có thể có những trường để điều khiển cache (cache control) nên chúng ta cần báo cho nginxbiết bỏ qua những thông số này để việc cache được kiểm soát tối đa.
proxy_pass http://210.211.108.138:8081: cái này chắc ai cũng biết rồi.
Ngoài ra còn 2 tham số rất quan trọng nữa là proxy_cache_bypass để bypass cachevà proxy_cache_nocache để bỏ qua việc cache respond của server trả về.
Vậy là xong phần cơ bản, cũng khá đơn giản. Tuy nhiên để cấu hình cache động cho một website thì cần mất rất nhiều công sức để optimize lại dần dần, vì không phải site nào cũng giống site nào

I – Nginx
I.1 – Nginx là gì


Nginx (pronounced “Engine-X”) : Là sản phẩm mã nguồn mở cho web server . Là một reverse proxy cho các giao thức HTTP, SMTP, POP3 and IMAP . Nhằn nâng cao hiệu xuất xử lý khi sử dung lượng RAM thấp .  Được cấp phép bởi BSD chạy trên nên tảng Unix , Linux  và các biến thểBSD, Mac OS X, Solaris, AIX, HP-UX và Microsoft Windows.

I.2 – Tổng quan


Nginx có thể triển khai nội dung của các trang web động bằng cách sử lý FastCGI, SCGI  cho các scripts . Và có thể sử dụng như là một server cân bằng tải . Sau đó vấn đề C10K xuất hiện  nói cách khác để cho phép mỗi máy chủ web phải có khả năng xử lý 10.000 khách hàng cùng một lúc.  Cần phải phát triển một mạng lưới  I / O tốt hơn và công nghệ quản lý chủ đề đã được xuất hiện. Sự xuất hiện của NGinx không phải là kết quả của một nỗ lực để giải quyết vấn đề C10K (như là một vấn đề phổ biến) nhưng “vấn đề C10K” đã thành công trong việc đưa ra các  nỗ lực để nâng cao hiệu suất phát triển mạng máy chủ
Igor Sysoev phát triển nginx từ cách đây hơn 9 năm. Vào tháng 10/2004, phiên bản 0.1.0 được phát hành rộng rãi theo giấy phép BSD. Công dụng của nginx ngoài máy chủ web, còn có thể làm proxy nghịch cho Web và làm proxy email (SMTP/POP3/IMAP). Theo thống kê của Netcraft, trong số 1 triệu website lớn nhất thế giới, có 6,52% sử dụng nginx. Tại Nga, quê hương của nginx, có đến 46,9% sử dụng máy chủ này. Nginx chỉ đứng sau Apache và IIS (của Microsoft).
Nginx cung cấp gần như tất cả các chức năng máy chủ web:

I.3 – Cài đặt Nginx

#aptitude -y update 
#aptitude -y install mongodb redis-server git-core libpcre3 libpcre3-dev libssl-dev mysql-server mysql-client libmysqlclient16-dev libxml2-dev libxslt-dev imagemagick libmagick9-dev libcurl3-dev libreadline-dev memcached build-essential openssl libreadline6 libreadline6-dev curl git-core zlib1g zlib1g-dev libssl-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt-dev autoconf libc6-dev ncurses-dev automake libtool bison subversion openjdk-6-jre moni
#mkdir -p /var/tmp/nginx/{proxy,client,fcgi} 
#cd /usr/local/src 
#wget http://nginx.org/download/nginx-1.0.15.tar.gz 
#tar zxvf nginx-1.0.15.tar.gz 
#cd nginx-1.0.15
CFLAGS='-march=nocona' ./configure  \ --prefix=/opt/nginx \ --conf-path=/opt/nginx/conf/nginx.conf \ --error-log-path=/var/log/nginx/error.log \ --pid-path=/var/run/nginx.pid  \ --lock-path=/var/lock/nginx.lock \ --user=nobody \ --group=nogroup \ --with-http_stub_status_module \ --with-http_ssl_module \ --with-http_gzip_static_module \ --with-http_realip_module \ --http-log-path=/var/log/nginx/access.log \ --http-client-body-temp-path=/var/tmp/nginx/client/ \ --http-proxy-temp-path=/var/tmp/nginx/proxy/ \ --http-fastcgi-temp-path=/var/tmp/nginx/fcgi
#make 
#make install #ln -s /opt/nginx/sbin/nginx /usr/sbin

I.4 – Cấu hình Nginx

#vim /opt/nginx/conf/nginx.conf
 #user  nobody;
 worker_processes  8;
 events 
{ worker_connections  1024; accept_mutex on; # "on" if nginx worker_processes > 1 use epoll; # enable for Linux 2.6+ } 
http { include       mime.types; 
default_type  application/octet-stream;
log_format  main  '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; 

upstream backend-unicorn-pc {
 server localhost:5000;
 }
upstream backend-unicorn-sp {
 server localhost:5100;

sendfile        on;
 keepalive_timeout  30;
 server {
 listen       80;
 server_name  snap.framgia.com;
 access_log  /var/log/nginx/unicorn.access.log  main;
 client_max_body_size 5M;
 set $is_sphone 0;
if ($http_user_agent ~ Android) {
set $is_sphone 1;
}
if ($http_user_agent ~ iPhone) {
set $is_sphone 1;
}
if ($http_user_agent ~ iPod) {
set $is_sphone 1;
}
if ($http_user_agent ~ iPad) {
set $is_sphone 1;
}
location /uploads/ {
root /usr/local/rails_apps/snap/current/public;
break;
}
location / {
root /usr/local/rails_apps/snap/current/public;
if (-f $request_filename) { break; }
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
proxy_read_timeout 60;
proxy_redirect off;
if ($is_sphone = 1) {
proxy_pass http://backend-unicorn-sp;
}
if ($is_sphone != 1) {
proxy_pass http://backend-unicorn-pc;
}
}
error_page   500 502 503 504  /50x.html;
location = /50x.html {
root   html;
}
}

I.5 – Các thông số của NGinx

I.5.1 worker_processes

Với cấu hình mặc định, Nginx sẽ sử dụng một CPU để xử lý các tác vụ của mình. Tùy theo mức độ hoạt động của web server mà chúng ta có thể thay đổi lại thiết lập này. Ví dụ với các web server hay sử dụng về SSL, gzip thì ta nên đặt chỉ số của worker_processes này lên cao hơn. Nếu website của bạn có số lượng các tệp tin tĩnh nhiều, và dung lượng của chúng lớn hơn bộ nhớ RAM thì việc tăng worker_processes sẽ tối ưu băng thông đĩa của hệ thống.
Để xác định số cores của CPU của hệ thống ta có thể thực hiện lệnh
# cat /proc/cpuinfo | grep processor
 processor : 0
 processor : 1
 processor : 2
 processor : 3
Như ở trên, CPU của chúng ta có 4 cores. Để thay đổi mức sử dụng CPU của nginx ta sửa tệp tin cấu hình chính
vim /opt/nginx/conf/nginx.conf
 worker_processes  4;

I.5.2  worker_connections

Worker_connections sẽ cho biết số lượng connection mà CPU sẽ xử lý. Mặc định, số lượng connection này được thiết lập là 1024. Để xem về mức giới hạn sử dụng của hệ thống bạn có thể dụng lệnh ulimit. Con số thiết lập của worker_connections nên nhỏ hơn hoặc bằng giới hạn này! Nếu bạn đã điều chỉnh lại giá trị worker_processes giúp Nginx sử dụng nhiều cores để xử lý các tác vụ hơn thì có thể thêm dòng cấu hình sau để tăng số lượng clients lên cao nhất.
max_clients = worker_processes * worker_connections

I.5.3 Buffers

Một trong những cấu hình quan trọng để tối ưu Nginx là thiết đặt các giá trị buffer. Nếu bạn thiết lập bộ nhớ buffer quá nhỏ thì sẽ dễ dẫn tới tình trạng “thắt cỗ chai” khi web server của chúng ta tiếp nhận một lượng traffic lớn. Để thay đổi các giá trị buffer này, chúng ta có thể thêm vào các dòng cấu hình ở thẻ http của file cấu hình chính nginx.conf
client_body_buffer_size 8K;
client_header_buffer_size 1k;
client_max_body_size 2m;
large_client_header_buffers 2 1k;
Trong đó:
client_body_buffer_size: Thiết đặt giá trị kích thước của body mà client yêu cầu. Nếu kích thước được yêu cầu lớn hơn giá trị buffer thì sẽ được lưu vào temporary file.
client_header_buffer_size: Thiết đặt giá trị kích thước của header mà client yêu cầu. Thông thường thì kích thước này 1K là đủ.
client_max_body_size: Thiết đặt giá trị kích thước tối đa của body mà client có thể yêu cầu được, xác định bởi dòng Conent-Length trong header. Nếu kích thước body yêu cầu vượt giới hạn nãy thì client sẽ nhận được thông báo lỗi “Request Entity Too Large” (413).
large_client_header_buffers: Thiết đặt giá trị kích về số lượng và kích thước lớn nhất của buffer dùng để đọc các headers có kích thước lớn từ các request của client. Nếu client gửi một header quá lớn Nginx sẽ trả về lỗi “Request URL too large” (414)hoặc “Bad request” (400)nếu header của request quá dài.
Ngoài ra chúng ta cũng cần thiết đặt lại các giá trị timeout để tối ưu hiệu suất hoạt động của web server với các client :
client_body_timeout 10;
client_header_timeout 10;
keepalive_timeout 15;
send_timeout 10;
Trong đó:
client_body_timeout: Thiết đặt thời gian tải body của webpage từ client. Nếu quá thời gian này, client sẽ nhận thông báo trả về “Request time out” (408).
client_header_timeout: Thiết đặt thời gian tải title của webpage từ client. Nếu quá thời gian này, client sẽ nhận thông báo trả về “Request time out” (408).
keepalive_timeout: Thiết đặt thời gian sống của kết nối từ client, nếu quá thời gian này thì kết nối sẽ bị đóng.
send_timeout: Thiết đặt thời gian phản hồi dữ liệu giữa client và server, nếu quá thời gian này thì nginx sẽ tắt kết nối.

I.5.4 Nén các gói dữ liệu gửi đi bằng Gzip


Gzip sẽ giúp nén các dữ liệu trước khi chuyển chúng tới Client. Đây là một cách để tăng tốc độ tuy cập website của cúng ta. Trong thẻ http của file cấu hình chính nginx.conf ta có thể thêm
 gzip on;
 gzip_comp_level 2;
 gzip_min_length 1000;
 gzip_proxied expired no-cache no-store private auth;
 gzip_types text/plain application/xml;
 gzip_disable "MSIE [1-6]\.";

V.5.5 Cache nội dung các tệp tin tĩnh

Hầu hết các request từ client tới website của chúng ta để load các nôi dung như: hình ảnh, java script, css, flash,… Chúng ta nên thực hiện việc lưu cache lại các tệp tin có nội dung tĩnh này trên Nginx
 location ~* "\.(js|ico|gif|jpg|png|css|html|htm|swf|htc|xml|bm p|cur)$" {
 root /home/site/public_html;
 add_header Pragma "public";
 add_header Cache-Control "public";
 expires 3M;
 access_log off;
 log_not_found off;

I.5.6 Ẩn phiên bản của Nginx

Việc ẩn đi phiên bản của Nginx từ Server Header sẽ giúp hệ thống webserver của chúng ta được bảo mật tốt hơn. Để thực hiện điều này, trong thẻ http của của tệp tin cấu hình chính nginx.conf ta thêm vào dòng sau
server_tokens off;

I.5.7 Cấm các truy cập tới các tệp tin ẩn trên Nginx

Đôi khi trên các thư mục web chúng ta có lưu những tệp tin ẩn (bắt đầu với dấu chấm “.”) như .svn, .htaccess. Đây là các tệp tin không mang tính public đối với người dùng. Để ngăn chặn các truy xuất tới các tệp tin ẩn này ta có thể thêm vào đoạn cấu hình sau
 location ~ /\. {
 access_log off;
 log_not_found off;
 deny all;
 }

I.5.8Các thông số cơ bản của caching

 http {
 proxy_cache_path  /var/www/cache levels=2 keys_zone=my-cache:8m max_size=1000m inactive=600m;
 proxy_temp_path /var/www/cache/tmp;
 server {
 location / {
 proxy_pass http://example.net;
 proxy_cache my-cache;
 proxy_cache_valid  200 302  60m;
 proxy_cache_valid  404      1m;
 }
 }
 }
/vr/www/cache là thư mục chứa cache
level=2 phân cấp thư mục lưu cache
key_zone=cache_one:8m tên của cache zone là cache_one
inactive=1d: thành phần cache nào không được truy cập trong vòng 1 ngày sẽ bị xóa.
proxy_cache cache_one : chỉ định cache vào zone cache_one
proxy_cache_valid 200 15m : các request có file tồn tại (200) sẽ có giá trị cache trong vòng 15m.
proxy_cache_key $host$request_uri : chỉ định key cache, giúp nginx lần được cache của một URL ở đâu trong thư mục cache.
proxy_set_header X-Forwarded-For $remote_addr: dùng để thêm một dòng trong header trả về cho client.
proxy_ignore_headers : cái này để thông báo cho nginx bỏ qua những header nào của client. Trong các header của người dùng có thể có những trường để điều khiển cache (cache control) nên chúng ta cần báo cho nginx biết bỏ qua những thông số này để việc cache được kiểm soát tối đa.
proxy_read_timeout Default:  60s

II- Unicorn

II.1 – Unicorn là gì ?

Unicorn là server HTTP cho Ruby
Nginx gửi các request tới worker Unicorn thông qua Unix Domain Socket  or TCP . Mỗi server đều có một con số worker nhất định mà trong giờ “cao điểm ” có thể đáp ứng được đồng thời nhiều yêu cầu bằng cách sắp xếp hàng đợi . Unicorn biết rõ được các worker đang xử lý tiến trình nào , hay bao bao lâu mỗi worker sử lý một yêu cầu. Thay vì xếp chồng hàng đợi chồng chất Unicorn  sẽ hủy bỏ worker và ngay lập tức tạo 1 worker mới để phục vụ yêu cầu

II.2 – Cài đặt – Cấu hình

#gem install rails
#gem install unicorn
#cd /var/www/
#rails new unicorn
#cd  unicorn
#unicorn_rails
I, [2012-09-07T23:57:09.486924 #2523]  INFO -- : listening on addr=0.0.0.0:8080 fd=5
 I, [2012-09-07T23:57:09.487888 #2523]  INFO -- : worker=0 spawning...
 I, [2012-09-07T23:57:09.489175 #2523]  INFO -- : master process ready
 I, [2012-09-07T23:57:09.490946 #2525]  INFO -- : worker=0 spawned pid=2525
 I, [2012-09-07T23:57:09.491675 #2525]  INFO -- : Refreshing Gem list
 I, [2012-09-07T23:57:15.208629 #2525]  INFO -- : worker=0 ready
#mkdir /etc/unicorn
#cd /etc/unicorn/
#vim /etc/unicorn/unicorn2.conf
 RAILS_ROOT=/var/www/unicorn
 RAILS_ENV=production
#vim /etc/init.d/unicorn_2
#!/bin/sh
 set -e
 sig () {
 test -s "$PID" && kill -$1 `cat "$PID"`
 }
 oldsig () {
 test -s "$OLD_PID" && kill -$1 `cat "$OLD_PID"`
 }
 cmd () {
 case $1 in
 start)
 sig 0 && echo >&2 "Already running" && exit 0
 echo "Starting"
 $CMD
 ;;
 stop)
 sig QUIT && echo "Stopping" && exit 0
 echo >&2 "Not running"
 ;;
 force-stop)
 sig TERM && echo "Forcing a stop" && exit 0
 echo >&2 "Not running"
 ;;
 restart|reload)
 sig USR2 && sleep 5 && oldsig QUIT && echo "Killing old master" `cat $OLD_PID` && exit 0
 echo >&2 "Couldn't reload, starting '$CMD' instead"
 $CMD
 ;;
 upgrade)
 sig USR2 && echo Upgraded && exit 0
 echo >&2 "Couldn't upgrade, starting '$CMD' instead"
 $CMD
 ;;
 rotate)
 sig USR1 && echo rotated logs OK && exit 0
 echo >&2 "Couldn't rotate logs" && exit 1
 ;;
 *)
 echo >&2 "Usage: $0 <start|stop|restart|upgrade|rotate|force-stop>"
 exit 1
 ;;
 esac
 }
 setup () {
 echo -n "$RAILS_ROOT: "
 cd $RAILS_ROOT || exit 1
 export PID=$RAILS_ROOT/tmp/pids/unicorn.pid
 export OLD_PID="$PID.oldbin"
 CMD="unicorn_rails -c config/unicorn.rb -E $RAILS_ENV -D"
 }
 start_stop () {
 # either run the start/stop/reload/etc command for every config under /etc/unicorn
 # or just do it for a specific one
 # $1 contains the start/stop/etc command
 # $2 if it exists, should be the specific config we want to act on
 if [ $2 ]; then
 . $2
 setup
 cmd $1
 else
 for CONFIG in /etc/unicorn/unicorn2.conf; do
 # import the variables
 . $CONFIG
 setup
 # run the start/stop/etc command
 cmd $1
 done
 fi
 }
 ARGS="$1 $2"
 start_stop $ARGS
#chmod +x /etc/init.d/unicorn_2
#cd /var/www/unicorn/config
#vim unicorn.rb
app_path = "/var/www/unicorn"
listen 2008 # by default Unicorn listens on port 8080
 worker_processes 2 # this should be >= nr_cpus
 pid "#{app_path}/tmp/pids/unicorn2.pid"
 stderr_path "#{app_path}/log/unicorn2.log"
 stdout_path "#{app_path}/log/unicorn2.log"
 before_exec do |server|
 ENV['BUNDLE_GEMFILE'] = "/var/www/unicorn/Gemfile"
 end
before_fork do |server, worker|
 defined?(ActiveRecord::Base) and ActiveRecord::Base.connection.disconnect!
old_pid = "#{server.config[:pid]}.oldbin"
 if old_pid != server.pid
 begin
 sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
 Process.kill(sig, File.read(old_pid).to_i)
 rescue Errno::ENOENT, Errno::ESRCH
 end
 end
 end
###################require "redis"
 after_fork do |server, worker|
 if defined?(ActiveRecord::Base)
 ActiveRecord::Base.establish_connection
 end
if defined?(MultiDb::ConnectionProxy)
 begin
 # MultiDb::ConnectionProxy.sticky_slave = true
 MultiDb::ConnectionProxy.setup!
 rescue
 end
 end
if Rails.cache.respond_to?(:reset)
 Rails.cache.reset
 end
port = 5000 + worker.nr
child_pid = server.config[:pid].sub('.pid', ".#{port}.pid")
 system("echo #{Process.pid} > #{child_pid}")
end
Kiểm tra các kết nối
#netstat -natp

Active Internet connections (servers and established)
 Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
 tcp        0      0 127.0.0.1:27017         0.0.0.0:*               LISTEN      864/mongod
 tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      880/mysqld
 tcp        0      0 127.0.0.1:6379          0.0.0.0:*               LISTEN      989/redis-server
 tcp        0      0 127.0.0.1:11211         0.0.0.0:*               LISTEN      924/memcached
 tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      840/boa
 tcp        0      0 0.0.0.0:2001            0.0.0.0:*               LISTEN      939/monkey
 tcp        0      0 127.0.0.1:28017         0.0.0.0:*               LISTEN      864/mongod
 tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1748/sshd
 tcp        0      0 192.168.4.106:22        192.168.4.100:49216     ESTABLISHED 2001/sshd: kiloccnp
 tcp        0     52 192.168.4.106:22        192.168.4.100:49396     ESTABLISHED 2533/sshd: kiloccnp
 tcp6       0      0 :::22                   :::*                    LISTEN      1748/sshd
 tcp6       0      0 :::8000                 :::*                    LISTEN      999/webfsd

#/etc/init.d/unicorn_2 start
#netstat -natp

Active Internet connections (servers and established)
 Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
 tcp        0      0 127.0.0.1:27017         0.0.0.0:*               LISTEN      864/mongod
 tcp        0      0 127.0.0.1:3306          0.0.0.0:*               LISTEN      880/mysqld
 tcp        0      0 127.0.0.1:6379          0.0.0.0:*               LISTEN      989/redis-server
 tcp        0      0 127.0.0.1:11211         0.0.0.0:*               LISTEN      924/memcached
 tcp        0      0 0.0.0.0:80              0.0.0.0:*               LISTEN      840/boa
 tcp        0      0 0.0.0.0:2001            0.0.0.0:*               LISTEN      939/monkey
 tcp        0      0 127.0.0.1:28017         0.0.0.0:*               LISTEN      864/mongod
 tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN      1748/sshd
 tcp        0      0 0.0.0.0:2008            0.0.0.0:*               LISTEN      3069/unicorn.rb -E
 tcp        0      0 192.168.4.106:22        192.168.4.100:49216     ESTABLISHED 2001/sshd: kiloccnp
 tcp        0     52 192.168.4.106:22        192.168.4.100:49396     ESTABLISHED 2533/sshd: kiloccnp
 tcp6       0      0 :::22                   :::*                    LISTEN      1748/sshd
 tcp6       0      0 :::8000                 :::*                    LISTEN      999/webfsd
cd /var/www/unicorn/log
cat unicorn2.log


I, [2012-09-08T00:13:37.402200 #3069]  INFO -- : listening on addr=0.0.0.0:2008 fd=5
 I, [2012-09-08T00:13:37.402872 #3069]  INFO -- : worker=0 spawning...
 I, [2012-09-08T00:13:37.403439 #3069]  INFO -- : worker=1 spawning...
 I, [2012-09-08T00:13:37.403980 #3069]  INFO -- : master process ready
 I, [2012-09-08T00:13:37.409890 #3072]  INFO -- : worker=0 spawned pid=3072
 I, [2012-09-08T00:13:37.410073 #3072]  INFO -- : Refreshing Gem list
 I, [2012-09-08T00:13:37.412841 #3074]  INFO -- : worker=1 spawned pid=3074
 I, [2012-09-08T00:13:37.413020 #3074]  INFO -- : Refreshing Gem list
 I, [2012-09-08T00:13:42.680852 #3074]  INFO -- : worker=1 ready
 I, [2012-09-08T00:13:42.684405 #3072]  INFO -- : worker=0 ready

 Tối ưu Nginx:
1) Bố trí lại các tệp tin cấu hình:
Thông thường thì các tệp tin cấu hình của Nginx sẽ được lưu trữ trong thư mục “/etc/nginx”. Một cách tổ chức lại việc lưu trữ  tốt hơn theo phong cách của Apache như sau
## Tệp tin cấu hình chính ##
/etc/nginx/nginx.conf

## Tệp tin cấu hình các virtual host ##
/etc/nginx/sites-available/
/etc/nginx/sites-enabled/
 
## Các tệp tin cấu hình khác... ##
/etc/nginx/conf.d/
Phần tệp tin cấu hình virtual host sẽ có 2 thư mục chính:
  • sites-available: Chứa danh sách các file cấu hình khác nhau như: file cấu hình hoàn chỉnh, file cấu hình tạm thời, file cấu hình lỗi,…Lưu trữ những tệp tin cấu hình mà ta hiện có.
  • sites-enabled: Chứa danh sách các symbolic link trỏ tới các tệp tin cấu hình hoàn chỉnh, đã tối ưu ở thư mục sites-available.
nginx-php-fpm-config-1
Vì chúng ta tách biệt phần file cấu hình của các virtual host ra lưu trữ riêng nên ta cần phải include dòng địa chỉ thư mục lưu trữ các file cấu hình này vào file cấu hình chính. Ở file nginx.conf ta thêm vào
## Tệp tin cấu hình virtual host. ##
include /etc/nginx/sites-enabled/*;
 
## Các tệp tin cấu hình khác.../ ##
include /etc/nginx/conf.d/*;
Chú ý: Việc tối ưu lại cách sắp xếp này giúp chúng ta dễ dàng quản lý hệ thống webserver hơn chứ không có tác dụng tới hiệu suất tới hiệu suất.
2) Tối ưu worker_processes:
Với cấu hình mặc định, Nginx sẽ sử dụng một CPU để xử lý các tác vụ của mình. Tùy theo mức độ hoạt động của web server mà chúng ta có thể thay đổi lại thiết lập này. Ví dụ với các web server hay sử dụng về SSL, gzip thì ta nên đặt chỉ số của worker_processes này lên cao hơn. Nếu website của bạn có số lượng các tệp tin tĩnh nhiều, và dung lượng của chúng lớn hơn bộ nhớ RAM thì việc tăng worker_processes sẽ tối ưu băng thông đĩa của hệ thống.
Để xác định số cores của CPU của hệ thống ta có thể thực hiện lệnh
# cat /proc/cpuinfo | grep processor
[root@server ~]# cat /proc/cpuinfo | grep processor
processor    : 0
processor    : 1
processor    : 2
processor    : 3
Như ở trên, CPU của chúng ta có 4 cores. Để thay đổi mức sử dụng CPU của nginx ta sửa tệp tin cấu hình chính
# vi /etc/nginx/nginx.conf
Tại dòng số 3 ta thay đổi giá trị của worker_processes là 4.
nginx-php-fpm-config-2
3) Tối ưu worker_connections:
worker_connections sẽ cho biết số lượng connection mà CPU sẽ xử lý. Mặc định, số lượng connection này được thiết lập là 1024. Để xem về mức giới hạn sử dụng của hệ thống bạn có thể dụng lệnh ulimit
# ulimit –n
nginx-php-fpm-config-3
Con số thiết lập của worker_connections nên nhỏ hơn hoặc bằng giới hạn này!
Nếu bạn đã điều chỉnh lại giá trị worker_processes giúp Nginx sử dụng nhiều cores để xử lý các tác vụ hơn thì có thể thêm dòng cấu hình sau để tăng số lượng clients lên cao nhất
max_clients = worker_processes * worker_connections
4) Tối ưu buffers (bộ nhớ đệm):
Một trong những cấu hình quan trọng để tối ưu Nginx là thiết đặt các giá trị buffer. Nếu bạn thiết lập bộ nhớ buffer quá nhỏ thì sẽ dễ dẫn tới tình trạng “thắt cỗ chai” khi web server của chúng ta tiếp nhận một lượng traffic lớn. Để thay đổi các giá trị buffer này, chúng ta có thể thêm vào các dòng cấu hình ở thẻ http của file cấu hình chính nginx.conf
client_body_buffer_size 8K; 
client_header_buffer_size 1k; 
client_max_body_size 2m; 
large_client_header_buffers 2 1k;
Trong đó:
  • client_body_buffer_size: Thiết đặt giá trị kích thước của body mà client yêu cầu. Nếu kích thước được yêu cầu lớn hơn giá trị buffer thì sẽ được lưu vào temporary file.
  • client_header_buffer_size: Thiết đặt giá trị kích thước của header mà client yêu cầu. Thông thường thì kích thước này 1K là đủ.
  • client_max_body_size: Thiết đặt giá trị kích thước tối đa của body mà client có thể yêu cầu được, xác định bởi dòng Conent-Length trong header. Nếu kích thước body yêu cầu vượt giới hạn nãy thì client sẽ nhận được thông báo lỗi “Request Entity Too Large” (413).
  • large_client_header_buffers: Thiết đặt giá trị kích về số lượng và kích thước lớn nhất của buffer dùng để đọc các headers có kích thước lớn từ các request của client. Nếu client gửi một header quá lớn Nginx sẽ trả về lỗi “Request URL too large” (414) hoặc “Bad request” (400) nếu header của request quá dài.
Ngoài ra chúng ta cũng cần thiết đặt lại các giá trị timeout để tối ưu hiệu suất hoạt động của web server với các client
client_body_timeout     10; 
client_header_timeout   10; 
keepalive_timeout       15; 
send_timeout            10;
Trong đó:
  • client_body_timeout: Thiết đặt thời gian tải body của webpage từ client. Nếu quá thời gian này, client sẽ nhận thông báo trả về “Request time out” (408).
  • client_header_timeout: Thiết đặt thời gian tải title của webpage từ client. Nếu quá thời gian này, client sẽ nhận thông báo trả về “Request time out” (408).
  • keepalive_timeout: Thiết đặt thời gian sống của kết nối từ client, nếu quá thời gian này thì kết nối sẽ bị đóng.
  • send_timeout: Thiết đặt thời gian phản hồi dữ liệu giữa client và server, nếu quá thời gian này thì nginx sẽ tắt kết nối.
5) Tắt Access Logs:
Mặc định thì Nginx sẽ ghi lại các request lên một file trên đĩa cứng như là một việc ghi logs. Nêu bạn không sử dụng các access logs thì bạn có thể tắt tính năng này đi để giảm bớt thời gian nhập xuất. Để thực hiện điều này, trong thẻ server của file cấu hình chính nginx.conf bạn có thể đặt giá trị sau
access_logs off;
6) Nén các gói dữ liệu gửi đi bằng Gzip
Gzip sẽ giúp nén các dữ liệu trước khi chuyển chúng tới Client. Đây là một cách để tăng tốc độ tuy cập website của cúng ta. Trong thẻ http của file cấu hình chính nginx.conf  ta có thể thêm
gzip              on;
gzip_comp_level   2;
gzip_min_length   1000;
gzip_proxied      expired no-cache no-store private auth;
gzip_types        text/plain application/xml;
gzip_disable      "MSIE [1-6]\.";
7) Cache nội dung các tệp tin tĩnh:
Hầu hết các request từ client tới website của chúng ta để load các nôi dung như: hình ảnh, java script, css, flash,... Chúng ta nên thực hiện việc lưu cache lại các tệp tin có nội dung tĩnh này trên Nginx
location ~* "\.(js|ico|gif|jpg|png|css|html|htm|swf|htc|xml|bmp|cur)$" {
    root            /home/site/public_html;
    add_header      Pragma "public";
    add_header      Cache-Control "public";
    expires         3M;
    access_log      off;
    log_not_found   off;
}

8) Ẩn phiên bản của Nginx:
Việc ẩn đi phiên bản của Nginx từ Server Header sẽ giúp hệ thống webserver của chúng ta được bảo mật tốt hơn. Để thực hiện điều này, trong thẻ http của của tệp tin cấu hình chính nginx.conf ta thêm vào dòng sau
server_tokens off;
9) Thực thi các tệp tin PHP thông qua PHP-FPM:
Ở đây ta có thể sử dụng TCP/IP stack mặc định hoặc dùng trự tiếp Unix Socket Connection. Chúng ta cũng có thể sử dụng PHP-FPM để lắng nghe trên IP:Port (thường là 127.0.0.1:9000).
location ~* \.php$ {
    try_files       $uri /index.php;
    fastcgi_index   index.php;
    fastcgi_pass    127.0.0.1:9000;
    #fastcgi_pass   unix:/var/run/php-fpm/php-fpm.sock;
    include         fastcgi_params;
    fastcgi_param   SCRIPT_FILENAME    $document_root$fastcgi_script_name;
    fastcgi_param   SCRIPT_NAME        $fastcgi_script_name;
}
Chúng ta hoàn toàn có thể tách biệt PHP-FPM và Nginx chạy trên các server khác nhau.
10) Cấm các truy cập tới các tệp tin ẩn trên Nginx:
Đôi khi trên các thư mục web chúng ta có lưu những tệp tin ẩn (bắt đầu với dấu chấm “.”) như .svn, .htaccess. Đây là các tệp tin không mang tính  public đối với người dùng. Để ngăn chặn các truy xuất tới các tệp tin ẩn này ta có thể thêm vào đoạn cấu hình sau
location ~ /\. {
    access_log off;
    log_not_found off; 
    deny all;
}

II – Tối ưu PHP-FPM
1) Bố trí lại các tệp tin cấu hình:
Thông thường thì các cấu hình của PHP-FPM được thiết lập trong file cấu hình /etc/php-fpm.conf và ở thư mục /etc/php-fpm.d. Các tệp tin cấu hình khác của PHP-FPM nên đặt trong cùng thư mục/etc/php-fpm.d. Chúng ta có thể thêm dòng cấu hình sau vào file php-fpm.conf để thực hiện điều này
include=/etc/php-fpm.d/*.conf

2) Cấu hình nhiều PHP-FPM Pool:
Đối với PHP-FPM ta có thể tạo ra nhiều pool khác nhau cho các website khác nhau có trên web server. Bằng cách này chúng ta có thể có được các cấu hình cấp phát tài nguyên và nhóm sở hữu khác nhau đối với từn website. Ví dụ ở đây mình tạo 3 pool cho 3 website khác nhau là
/etc/php-fpm.d/site.conf /etc/php-fpm.d/blog.conf /etc/php-fpm.d/forums.conf
Các cấu hình mẫu:
/etc/php-fpm.d/site.conf
[site]
listen = 127.0.0.1:9000
user = site
group = site
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/slowlog-site.log
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 5
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 4
pm.max_requests = 200
listen.backlog = -1
pm.status_path = /status
request_terminate_timeout = 120s
rlimit_files = 131072
rlimit_core = unlimited
catch_workers_output = yes
env[HOSTNAME] = $HOSTNAME
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp
/etc/php-fpm.d/blog.conf
[blog]
listen = 127.0.0.1:9001
user = blog
group = blog
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/slowlog-blog.log
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 4
pm.start_servers = 2
pm.min_spare_servers = 1
pm.max_spare_servers = 3
pm.max_requests = 200
listen.backlog = -1
pm.status_path = /status
request_terminate_timeout = 120s
rlimit_files = 131072
rlimit_core = unlimited
catch_workers_output = yes
env[HOSTNAME] = $HOSTNAME
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp
/etc/php-fpm.d/forums.conf
[forums]
listen = 127.0.0.1:9002
user = forums
group = forums
request_slowlog_timeout = 5s
slowlog = /var/log/php-fpm/slowlog-forums.log
listen.allowed_clients = 127.0.0.1
pm = dynamic
pm.max_children = 10
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 4
pm.max_requests = 400
listen.backlog = -1
pm.status_path = /status
request_terminate_timeout = 120s
rlimit_files = 131072
rlimit_core = unlimited
catch_workers_output = yes
env[HOSTNAME] = $HOSTNAME
env[TMP] = /tmp
env[TMPDIR] = /tmp
env[TEMP] = /tmp
3) Cấu hình PHP-FPM Pool Process Manager (pm):
Trong việc quản lý các tiến trình của PHP-FPM ta nên sử dụng cách quản lý động để chỉ khởi động những tiến trình khi cần thiết. Các cấu hình ở đây cũng tương tự như là cấu hình các thông số của worker_porcessworker_connection của Nginx mà mình đã trình bày ở trên. Tùy theo lượng truy cập tới website của chúng ta và dung lượng bộ nhớ RAM của web server hiện có mà ta có các cách cấu hình cho phù hợp.
Giải sử Web Server của chúng ta có 512 MB ram, ở những lúc website có lượng truy cập cao, ta check dung lượng bộ nhớ RAM hiện đang dùng (bằng lệnh top chẳn hạn). Dung lượng RAM được cấp phát cho PHP-FPM là 220 MB, mỗi tiến trình của nó sử dụng 24 MB. Ta có thể tính được giá trị của max_children là 220/24 = 9.17.
Vậy giá trị pm.max_children ta nên thiết đặt cho web server là 9.
Trong file cấu hình pool của website ta có thể có cấu hình mẫu như sau:
pm.max_children = 9
pm.start_servers = 3
pm.min_spare_servers = 2
pm.max_spare_servers = 4
pm.max_requests = 200
Mặc định thì số lượng request ở mỗi tiến trình là không bị giới hạn. Tuy nhiên ta nên thiết đặt lại nó ở một giá trị xác định để tránh các vấn đề về bộ nhớ. Như ở ví dụ trên pm.max_requests được gán giá trị là 200.
1. Sơ lược về hoạt cấu hình nginx Trong diễn đàn này chắc ai cũng biết sử dụng nginx để làm reverse proxy cho apache nhằm “nâng cao hiệu suất” xử lý web, tuy vậy nhiều người chỉ cài mặc định lên mà không có bất cứ một sự optimize nào cho nó cả, thậm chí còn không tạo các vhost riêng cho các website, việc này có khi làm website của bạn chậm hơn. Mình xin “translate” một bài viết cũng khá hay của nginx nhằm giúp bạn đọc hiểu rõ hơn về hoạt động của nginx nhằm giúp cho các phần sau đọc dễ hơn. Bài này không nhớ lấy ở đâu nên không trích nguồn A. Name-based virtual servers Nginx sẽ quyết định server block nào sẽ xử lý khi một request tới. Chẳng hạn như ta có các dòng config sau: Code:
Mã:
server {

listen       80;

server_name  nginx.org  www.nginx.org;

...

}

server {

listen       80;

server_name  nginx.net  www.nginx.net;

...

}

server {

listen       80;

server_name  nginx.com  www.nginx.com;

...

}
trong những cấu hình trên, nginx sẽ dựa vào trường server_name để để xác định request sẽ được chuyển cho server block nào xử lý. Nếu như trong Host header của request không có trùng với bất cứ một server_name nào thì nó sẽ được đưa cho default_server như sau: Code:
Mã:
server {

listen       80  default_server;

server_name  nginx.net  www.nginx.net;

...

}
Nếu không có chỉ định trên, default_server sẽ là server block đầu tiên như trong ví dụ thứ 1 thì là : Code:
Mã:
server {

listen       80;

server_name  nginx.org  www.nginx.org;

...

}
B. Làm thế nào để chặn các request không có Host name trùng với server_name Ta có thể dùng một đoạn cấu hình ngắn như sau: Code:
Mã:
server {

listen       80;

server_name  "";

return       444;

}
C. Cấu hình PHP Chúng ta xem một đoạn cấu hình PHP đơn giản như sau: Code:
Mã:
server {

listen        80;

server_name   nginx.org  www.nginx.org;

root          /data/www;

location / {

index     index.html  index.php;

}

location ~* .(gif|jpg|png)$ {

expires   30d;

}

location ~ .php$ {

fastcgi_pass   localhost:9000;

fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;

include        fastcgi_params;

}

}
trong ví dụ trên bất kể thứ tự ra sao, nginx sẽ xử lý request tới như sau: + location “/” phù hợp với mọi request nhưng nó là literal string location nên nó sẽ được sử dụng cuối cùng. + Sau đó nginx sẽ tìm các location với regular expression được liệt kê theo thứ tự trong file config, ứng với cái nào thì nginx sẽ dùng lại và dùng các chỉ thị ở đó. + Nếu không có regular expression nào phù hợp nginx sẽ tìm literal string location nào đầu tiên. Ngoài ra nginx không quan tâm đến các biến chuổi đi theo sau như:
Mã:
/index.php?user=john&page=1
/index.php?page=1&user=john
Một vài ví dụ + Request /logo.gif ứng với literal location “/” và regular expression “.(gif|jpg|png)$” nên nó sẽ được xử lý bởi Code:
Mã:
location ~* .(gif|jpg|png)$ {

expires   30d;

}
Ở trên có khai báo root /data/www nên request trên sẽ ứng với /data/www/logo.gif và nó sẽ được gửi trả lại cho client. + Request /index.php ứng với literal string locaiont “/” và regular expression “.(php)$” nên nó sẽ được xử lý bởi Code:
Mã:
location ~ .php$ {

fastcgi_pass   localhost:9000;

fastcgi_param  SCRIPT_FILENAME $document_root$fastcgi_script_name;

include        fastcgi_params;

}
request trên sẽ được đưa qua FastCGI server ở localhost:9000. Chỉ định fastcgi_param sẽ gán SCRIPT_FILENAME thành “/data/www/index.php” và sau đó FastCGI sẽ xử lý yêu cầu đó. Xem thêm $document_root và $fastcgi_script_name trên trang wiki của nginx. + Request “/about.html” chỉ ứng với location “/” nên nginx tương tự trả về cho người dùng file “/data/www/about.html” + Request “/”. Ứng với location “/”, nó sẽ dựa vào chỉ thị index để tìm file index.php trong /data/www/. Nếu file này tồn tại, request lúc này sẽ trở thành “/index.php”, sau đó nginx sẽ tiếp tục quá trình tìm kiếm location phù hợp. Ghi chú: literal string là một chuỗi đơn 2. Các thông số cơ bản của caching nginx - Khai báo thư mục lưu cache (nằm trong phân đoạn http):
Mã:
proxy_cache_path /tmp/nginx_cache levels=2 keys_zone=cache_one:500m inactive=1d max_size=2048m;
  • /tmp/nginx_cache là thư mục chứa cache, bạn có thể để thư mục này trên RAM nếu cần truy xuất nhanh và RAM dư giả. Sử dụng MD5 để lưu tên cache (key_cache) .
  • level=2 phân cấp thư mục lưu cache
  • key_zone=cache_one:500m tên của cache zone là cache_one
  • inactive=1d: thành phần cache nào không được truy cập trong vòng 1 ngày sẽ bị xóa.
Ngoài ra vì khi cache, nginx tạo ra rất nhiều file tạm và file cache nên bạn cần đặt tham số worker_rlimit_nofile cao cao một chút hoặc sử dụng lệnh ulimit. - Cấu hình cache cho website ( phần này nằm trong phân đoạn server ) Đây là phần quan trọng nhất của việc cấu hình cache, mình xin giới thiệu sơ qua một số tham số quan trọng:
  • proxy_cache cache_one : chỉ định cache vào zone cache_one
  • proxy_cache_valid 200 15m : các request có file tồn tại (200) sẽ có giá trị cache trong vòng 15m.
  • proxy_cache_key $host$request_uri : chỉ định key cache, giúp nginx lần được cache của một URL ở đâu trong thư mục cache.
  • proxy_set_header X-Forwarded-For $remote_addr: dùng để thêm một dòng trong header trả về cho client.
  • proxy_ignore_headers : cái này để thông báo cho nginx bỏ qua những header nào của client. Trong các header của người dùng có thể có những trường để điều khiển cache (cache control) nên chúng ta cần báo cho nginx biết bỏ qua những thông số này để việc cache được kiểm soát tối đa.
Ngoài ra còn 2 tham số rất quan trọng nữa là proxy_cache_bypass để bypass cache và proxy_cache_nocache để bỏ qua việc cache respond của server trả về. Vậy là xong phần cơ bản, cũng khá đơn giản. Tuy nhiên để cấu hình cache động cho một website thì cần mất rất nhiều công sức để optimize lại dần dần, vì không phải site nào cũng giống site nào. Mình sẽ trình bày thêm về việc dynamic cache vào các bài post tiếp theo.