歷史:2004年俄羅斯人 伊戈爾·賽索耶夫 發布Nginx
官網:http://nginx.org/
第三方模塊:https://www.nginx.com/resources/wiki/modules/index.html
概念
正向代理:客戶端訪問服務器地址,本地的代理模塊會把請求改造成訪問代理的地址,通過代理訪問實際的服務器
反向代理:客戶端訪問代理的地址,通過代理訪問實際的服務器
Nagle算法:將要發送的數據存放在緩存里,當積累到一定量或一定時間,再將它們發送出去
Lua:一種輕量小巧的腳本語言
URI:Uniform Resource Identifier,統一資源標識符,標識一個資源,例如 cay@horstman.com
URL:Uniform Resource Locator,統一資源定位符,定位一個資源,例如http://www.test.com/index.html,是URI的子集
Nginx作為LB 與 HAProxy對比
1、Nginx除了LB 還有 Web 服務器功能
2、HAProxy是單進程,Nginx是多進程,可以利用多核
3、Nginx社區更活躍
Nginx作為Web服務器 與 Apache HTTP Server 對比
1、Nginx輕量級,占用資源少
2、Apache是同步多進程模型,一個連接對應一個進程;Nginx是異步多進程模式,多個連接對應一個進程,支持更多的并發連接
3、Nginx處理靜態文件效率高
4、Nginx 配置簡潔
CentOs安裝:yum install epel-release; yum install nginx
Ubuntu安裝:apt-get update; apt-get install nginx
tar.gz安裝
tar -zxvf nginx.tar.gz
./configure --without-http_rewrite_module --without-http_gzip_module # 配置,不聯網時忽略rewrite 和 gzip 模塊
make
make install # 會提示所安裝的目錄
./sbin/nginx # 在nginx 安裝目錄里 執行啟動
啟停:
service nginx start|restart|stop
nginx -s reload|reopen|stop|quit
配置文件檢查:nginx -t
配置
/etc/conf/nginx.conf
user nginx; # worker進程所屬的用戶組
worker_processes auto; # worker進程數量
error_log /var/log/nginx/error.log; # 錯誤日志
pid /run/nginx.pid; # 記錄主進程ID的文件
include /usr/share/nginx/modules/*.conf; # 引入其他配置文件
events {
worker_connections 1024; # 一個worker的最大連接數
}
http {
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# $upstream_addr 上游地址
access_log /var/log/nginx/access.log main; # 訪問日志 路徑 和 格式
# access_log off; # 關閉訪問日志
sendfile on; # 把文件數據從磁盤直接傳到套接字,不經過buffer
tcp_nopush on; # 啟用套接字TCP_CORK選項,即sendfile時盡可能一次發完
tcp_nodelay on; # 禁用Nagle算法
keepalive_timeout 65; # 在響應頭中設置keepalive
types_hash_max_size 2048;
client_max_body_size 20m; # 最大上傳量,也可以放在server{} 或 location{} 里
include /etc/nginx/mime.types;
default_type application/octet-stream;
include /etc/nginx/conf.d/*.conf;
server { # 一個虛擬服務器
listen 80 default_server; # default_server表示對80端口的請求,任何 server_name都不匹配時,選用這個server
listen [::]:80 default_server; # 監聽IPv6的80端口
server_name www.test.com; # 這個server綁定的域名
root /usr/share/nginx/html; # 靜態文件根目錄,不設置root,則默認為 /usr/share/nginx/html
include /etc/nginx/default.d/*.conf;
if ($scheme = http) {
return 301 https://$server_name$request_uri; # 將http重定向到https,要寫在監聽80端口的server{}下
}
location ^~ /static/ {
## 通過 expires 設置 Expires 和 Cache-Control 兩個字段,指導瀏覽器進行緩存驗證
expires 24h; # 緩存,緩存有效期內不進行服務器端驗證
expires max; # 使得 Expires 和 Cache-Control 為 10 年
expires -1s; 或 epoch; # 使得 Cache-Control 為 no-cache,緩存,但緩存過期,進行服務端驗證
## 單獨設置 Cache-Control
add_header Cache-Control "no-store"; # 禁用緩存
## 服務端驗證 設置
if_modified_since exact; # 提供Last-Modified,處理If-Modified-Since,精確度是秒,HTTP 1.0 就支持
etag on; # 提供Etag(跟最后修改時間、文件大小有關),處理If-None-Match,HTTP 1.1 才支持
# if_modified_since 和 etag 都沒變化才會返回304,兩項一般會同時變化,先驗證etag變化就返回200
root /webroot/static/; # 訪問本地文件, 該目錄下如果沒有index文件,會出現403
}
location /contract {
// alias 和 root 二選一
alias /usr/; # 實際訪問的是 /usr/
root /usr/; # 實際訪問的是 /usr/contract
add_header Access-Control-Allow-Origin *; # 允許跨域訪問
}
location / {
proxy_pass http://tomcat:8080/ # 轉發給后端服務器,如果這里使用了變量,則需要配置 resolver 來指定DNS
proxy_read_timeout 3600; # 超時時間
}
error_page 403 404 /404.html; # 碰到403、404 則 跳轉到 /404.html
location = /404.html {
root /usr/static;
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
error_page 403 404 =301 $scheme://$host:$server_port/; # 重定向
}
server{
# 第二個server
}
}
location匹配規則
精確匹配:=,例 location = /login
匹配路徑:^~,例 location ^~ /static/ 匹配 /static/a.html
區分大小寫的正則匹配:~,location ~ .(gif|jpg|png|js|css)$
不區分大小寫的正則匹配:~*
區分大小寫的正則不匹配:!~
不區分大小寫的正則不匹配:!~*
通配:/
匹配優先級:=、^~、正則匹配(按書寫順序)、/
全局變量
$args: 請求行中問號后面的參數
$content_length: 請求頭中的Content-length字段。
$content_type: 請求頭中的Content-Type字段。
$host: 請求目標主機
$http_user_agent: 客戶端agent信息
$http_cookie: 客戶端cookie信息
$request_method: 客戶端請求的方法
$remote_addr: 客戶端的IP地址。
$remote_port: 客戶端的端口。
$request_filename: 當前所請求文件的路徑
$scheme: HTTP協議(如http,https)
$server_protocol: 請求使用的協議,通常是HTTP/1.0或HTTP/1.1。
$server_port: 請求到達服務器的端口號。
$request_uri: 請求參數的原始URI,不包含主機名,如:”/foo/bar.php?arg=baz”。
$uri: 不帶參數的當前URI,不包含主機名,如”/foo/bar.html”。
$document_uri: 與$uri相同
反向代理
location ^~ /uri {
proxy_pass http://tomcat:8080/newuri; # 轉發時可以改變端口 和 路徑,如果location是正則匹配,轉發時路徑不會變
proxy_http_version 1.1 # 默認為1.0
# 設置給上游服務器的 頭
proxy_set_header Host $host; # 客戶端請求的目標host,即Nginx
proxy_set_header Proxy-Host $proxy_port; # Nginx請求的目標host,即上游服務器
proxy_set_header X-Real-IP $remote_addr; # 客戶端host
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 請求頭中的X-Forwarded-For + $remote_addr
# client -> nginx -> nginx 結構中,第一臺nginx的$remote_addr是客戶端,$proxy_add_x_forwarded_for也為$remote_addr,第二臺nginx的$remote_addr為第一臺nginx,$proxy_add_x_forwarded_for為第一臺nginx的$proxy_add_x_forwarded_for + 第一臺nginx,即客戶端 + 第一臺nginx
sub_filter 'a' 'b'; # 響應內容替換
sub_filter_once off; # 非單處替換
}
# websocket 代理
location ^~ /socket.io {
proxy_pass http://127.0.0.1:5000;
proxy_read_timeout 3600;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
upstream tomcats{ # 定義上游服務器
server 10.0.100.10:11211;
server 10.0.100.20:11211;
keepalive 64;
}
location ^~ /uri {
proxy_pass http://tomcat; # 使用upstream,轉發時會有 /uri
proxy_pass http://tomcat/; # 轉發時沒有 /uri
}
負載均衡算法
1、輪詢(默認算法)
2、IP hash
3、最少連接數
upstream tomcats{ # 定義上游服務器
server 10.0.100.10:11211;
server 10.0.100.20:11211;
least_conn; # 開啟最少連接數 算法
}
HTTPS
server {
listen 443 default ssl;
server_name www.example.com example.com;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 10m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
ssl_certificate /usr/local/etc/nginx/www.example.com.crt; # 證書
ssl_ certificate_ key /usr/local/etc/nginx/www.example.com.key; # 私鑰
或
ssl_certificate "/etc/nginx/cert/5810237_example.com.pem";
ssl_certificate_key "/etc/nginx/cert/5810237_example.com.key";
location / {
# https頁面 訪問的資源也必須是 https
add_header Content-Security-Policy upgrade-insecure-requests; # 如果html引用了http資源,強制改為https
proxy_set_header X-FORWARDED-PROTO https; # 讓上游服務器知道原始請求是https
proxy_pass http://upstream; # nginx和上游之間不用https
# proxy_pass https://upstream; # nginx和上游之間也用https
}
}
gzip 壓縮
http {
gzip on;
gzip_http_version 1.0;
gzip_comp_level 2;
gzip_disable msie6;
gzip_types text/plain text/css application/x-javascript text/xml
application/xml application/xml+rss text/javascript
application/javascript application/json;
}
rewrite
執行順序
1、執行server塊的rewrite
2、匹配location
3、執行location塊的rewrite,如果URL被重寫,則重新執行1-3,直到匹配location 并 不被 rewrite
4、如果循環超過10次,則返回500 Internal Server Error錯誤
格式
rewrite 正則 新串 [標志位];
例:rewrite ^/images/(.*)_(\d+)x(\d+)\.(png|jpg|gif)$ /resizer/$1.$4?width=$2&height=$3? last;
含義:原url中的路徑部分 去匹配 正則,把匹配串替換為新串,新串中的$1即引用匹配串中的第1個分組(小括號)
標志位flag
last: 后續只匹配,不再rewrite
break: 用在location中,不再rewrite,采用當前location
redirect: 返回302臨時重定向,地址欄會顯示跳轉后的地址
permanent: 返回301永久重定向,地址欄會顯示跳轉后的地址
server {
if ($request_method = POST) { # if里的判斷運算符 與 location 判斷運算符 含義一致
return 405; # 直接返回狀態碼
}
location / {
rewrite ^/listings/(.*)$ /$remote_port.html?listing=$1 last; # 新串中可以引用全局變量$remote_port等
set $flag 0; # 由于 if 不能嵌套使用 也 不能多重條件,因此用 變量來實現復雜條件
if ($http_user_agent ~* "(Android|iPhone|Windows Phone|UC|Kindle|Mobile)"){ # 移動端,正則表達式匹配
set $flag "${flag}1";
}
if ($request_uri !~ /mobile){ # 正則表達式不匹配
set $flag "${flag}1";
}
if ($request_uri !~ \.){ # 正則表達式 轉義
set $flag "${flag}1";
}
if ($flag = "0111"){
return 301 http://$server_name/mobile/index; # 重定向
}
proxy_pass http://upstream;
}
location / {
try_files $uri $uri/ /index.html;
}
location = / {
set $flag 0;
if ($http_user_agent ~* "(Android|iPhone|Windows Phone|UC|Kindle|Mobile)"){
set $flag "${flag}1";
}
if ($flag = "01"){
return 301 http://$server_name/mobile/index; # if 會使 try_files 失效
}
if ($flag != "01"){
return 301 http://$server_name/home;
}
}
}
try_file
location ^~ /test/ {
index index.html;
# try_files 表示找到一個可用的文件返回
try_files /2.html /1.html /test/test2.html @bd; # 引用location
}
location @bd { # 定義location
rewrite ^/(.*)$ http://www.google.com;
}
案例
1、小程序里需要打開第三方網頁,用我方域名代理第三方域名
location ~ "^\/.*\..*\/" { # 識別出 "https://我方域名/某域名/path" 格式的請求;需要轉義的正則表達式得用引號包起來
proxy_pass https:/$request_uri; # 拼接出 "https://某域名/path"
proxy_set_header Referer 'www.目標.com';
}
location / {
proxy_pass https://www.目標.com;
proxy_set_header Referer 'www.目標.com';
proxy_set_header Accept-Encoding ""; # 避免gzip,使得sub_filter能正常工作
sub_filter_once off; # 替換所有
sub_filter https:// https://www.我方.cn/; # 從目標獲取的響應內容里,在所有寫明的域名前加一個我方域名,形成https://我方域名/某域名/path;避免頁面直接向別的域名發送請求,造成跨域限制(CORS、防盜鏈)
}