Nginx 介紹
Nginx 是開源、高性能、高可靠的 Web 和反向代理服務器,而且支持熱部署,幾乎可以做到 7 * 24
小時不間斷運行,即使運行幾個月也不需要重新啟動,還能在不間斷服務的情況下對軟件版本進行熱更新。性能是 Nginx 最重要的考量,其占用內存少、并發能力強、能支持高達 5w 個并發連接數,最重要的是,Nginx 是免費的并可以商業化,配置使用也比較簡單。
Nginx 最重要的幾個使用場景:
-
• 靜態資源服務,通過本地文件系統提供服務
-
• 反向代理服務,延伸出包括緩存、負載均衡等
-
• API 服務,OpenResty
相關概念
-
• 簡單請求
-
• 非簡單請求
-
• 跨域
-
• 正向代理
-
• 反向代理
-
• 負載均衡
-
• 動靜分離
簡單請求
簡單請求,瀏覽器請求頭信息中會增加Origin
字段,用來說明本次請求來自的哪個源(協議+域名+端口)。
-
• 請求方法是 HEAD、GET、POST 三種之一
-
• HTTP 頭信息不超過右邊著幾個字段:
Accept
、Accept-Language
、Content-Language
、Last-Event-ID Content-Type
只限于三個值Application/x-www-form-urlencoded
、multipart/form-data
、text/plAIn
同時滿足上面兩個條件,就屬于簡單請求,反之則屬于非簡單請求。
非簡單請求
非簡單請求是對服務器有特殊要求的請求,請求方式是PUT
、DELETE
,或者Content-Type
為application/json
的。
在請求前會發送一次HTTP
預檢OPTIONS
請求,詢問服務器當前請求所在的域名是否在服務器的許可名單之中,只有得到肯定答復,才會發出正式的XHR
請求,否則報錯。
常用命令
控制臺中輸入 nginx -h
就可以看到完整的命令,常用的命令:
nginx -s reload # 向主進程發送信號,重新加載配置文件,熱重啟
nginx -s reopen # 重啟 Nginx
nginx -s stop # 快速關閉
nginx -s quit # 等待工作進程處理完成后關閉
nginx -T # 查看當前 Nginx 最終的配置
nginx -t -c <配置路徑> # 檢查配置是否有問題,如果已經在配置目錄,則不需要-c
配置語法
Nginx 的主配置文件是 /etc/nginx/nginx.conf
,可以使用 cat -n nginx.conf
來查看配置。
nginx.conf
結構圖:
main # 全局配置,對全局生效
├── events # 配置影響 Nginx 服務器或與用戶的網絡連接
├── http # 配置代理,緩存,日志定義等絕大多數功能和第三方模塊的配置
│ ├── upstream # 配置后端服務器具體地址,負載均衡配置不可或缺的部分
│ ├── server # 配置虛擬主機的相關參數,一個 http 塊中可以有多個 server 塊
│ ├── server
│ │ ├── location # server 塊可以包含多個 location 塊,location 指令用于匹配 uri
│ │ ├── location
│ │ └── ...
│ └── ...
└── ...
Nginx 的典型配置:
user nginx; # 運行用戶,默認即是nginx,可以不進行設置
worker_processes 1; # Nginx 進程數,一般設置為和 CPU 核數一樣
error_log /var/log/nginx/error.log warn; # Nginx 的錯誤日志存放目錄
pid /var/run/nginx.pid; # Nginx 服務啟動時的 pid 存放位置
events {
use epoll; # 使用epoll的I/O模型(如果你不知道Nginx該使用哪種輪詢方法,會自動選擇一個最適合你操作系統的)
worker_connections 1024; # 每個進程允許最大并發數
}
http { # 配置使用最頻繁的部分,代理、緩存、日志定義等絕大多數功能和第三方模塊的配置都在這里設置
# 設置日志模式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main; # Nginx訪問日志存放位置
sendfile on; # 開啟高效傳輸模式
tcp_nopush on; # 減少網絡報文段的數量
tcp_nodelay on;
keepalive_timeout 65; # 保持連接的時間,也叫超時時間,單位秒
types_hash_max_size 2048;
include /etc/nginx/mime.types; # 文件擴展名與類型映射表
default_type application/octet-stream; # 默認文件類型
include /etc/nginx/conf.d/*.conf; # 加載子配置項
server {
listen 80; # 配置監聽的端口
server_name localhost; # 配置的域名
location / {
root /usr/share/nginx/html; # 網站根目錄
index index.html index.htm; # 默認首頁文件
deny 172.168.27.36; # 禁止訪問的ip地址,可以為all
allow 172.168.27.44; # 允許訪問的ip地址,可以為all
}
error_page 500 502 503 504 /50x.html; # 默認50x對應的訪問頁面
error_page 400 404 error.html; # 同上
}
}
server 塊可以包含多個 location 塊,location 指令用于匹配 uri,語法:
location [ = | ~ | ~* | ^~] uri {
...
}
指令:
-
•
=
精確匹配路徑,用于不含正則表達式的 uri 前,如果匹配成功,不再進行后續的查找; -
•
^~
用于不含正則表達式的 uri; 前,表示如果該符號后面的字符是最佳匹配,采用該規則,不再進行后續的查找; -
•
~
表示用該符號后面的正則去匹配路徑,區分大小寫; -
•
~*
表示用該符號后面的正則去匹配路徑,不區分大小寫。跟 ~ 優先級都比較低,如有多個 location 的正則能匹配的話,則使用正則表達式最長的那個;
如果 uri 包含正則表達式,則必須要有
~
或~*
標志。
全局變量
全局變量 | 功能 |
$host | 請求信息中的 Host,如果請求中沒有 Host 行,則等于設置的服務器名,不包含端口 |
$request_method | 客戶端請求類型,如 GET、POST |
$remote_addr | 客戶端的 IP 地址 |
$args | 請求中的參數 |
$arg_PARAMETER | GET 請求中變量名 PARAMETER 參數的值,例如:$http_user_agent(Uaer-Agent 值), $http_referer |
$content_length | 請求頭中的 Content-length 字段 |
$http_user_agent | 客戶端agent信息 |
$http_cookie | 客戶端cookie信息 |
$remote_addr | 客戶端的IP地址 |
$remote_port | 客戶端的端口 |
$http_user_agent | 客戶端agent信息 |
$server_protocol | 請求使用的協議,如 HTTP/1.0、HTTP/1.1 |
$server_addr | 服務器地址 |
$server_name | 服務器名稱 |
$server_port | 服務器的端口號 |
$scheme | HTTP 方法(如http,https) |
配置反向代理
反向代理是工作中最常用的服務器功能,經常被用來解決跨域問題。
如監聽 9001 端口,然后把訪問不同路徑的請求進行反向代理:
-
• 訪問 http://127.0.0.1:9000/text1 的請求轉發到 http://127.0.0.1:8080
-
• 訪問 http://127.0.0.1:9000/text2 的請求轉發到 http://127.0.0.1:8081
vim /etc/nginx/nginx.conf
server {
listen 9000;
server_name *.tansci.com;
location ~ /text1/ {
proxy_pass http://127.0.0.1:8080;
}
location ~ /text2/ {
proxy_pass http://127.0.0.1:8081;
}
}
保存退出,nginx -s reload
重新加載
反向代理指令:
-
•
proxy_set_header
:在將客戶端請求發送給后端服務器之前,更改來自客戶端的請求頭信息。 -
•
proxy_connect_timeout
:配置 Nginx 與后端代理服務器嘗試建立連接的超時時間。 -
•
proxy_read_timeout
:配置 Nginx 向后端服務器組發出 read 請求后,等待相應的超時時間。 -
•
proxy_send_timeout
:配置 Nginx 向后端服務器組發出 write 請求后,等待相應的超時時間。 -
•
proxy_redirect
:用于修改后端服務器返回的響應頭中的 Location 和 Refresh。
跨域 CORS 配置
在瀏覽器上當前訪問的網站向另一個網站發送請求獲取數據的過程就是跨域請求。
跨域是瀏覽器的同源策略決定的,是一個重要的瀏覽器安全策略,用于限制一個origin
的文檔或者它加載的腳本與另一個源的資源進行交互,它能夠幫助阻隔惡意文檔,減少可能被攻擊的媒介,可以使用CORS
配置解除這個限制。
例如:
# 同源的例子
http://tansci.com/test1/index.html # 只是路徑不同
http://tansci.com/test2/index.html
# 不同源的例子
http://tansci.com/test1 # 協議不同
https://tansci.com/test2
http://tansci.com # host 不同
http://www.tansci.com
http://app.tansci.com
http://tansci.com # 端口不同
http://tansci.com:8080
反向代理解決跨域
前端服務地址為 a.tansci.com
的頁面請求 b.tansci.com
的后端服務導致的跨域,可以這樣配置:
server {
listen 9000;
server_name a.tansci.com;
location / {
proxy_pass b.tansci.com;
}
}
這樣就將對前一個域名 a.tansci.com
的請求全都代理到了 b.tansci.com
,前端的請求都被我們用服務器代理到了后端地址下,繞過了跨域。
對靜態文件的請求和后端服務的請求都以 fa.tansci.com
開始,不易區分,所以為了實現對后端服務請求的統一轉發,通常我們會約定對后端服務的請求加上 /api/
前綴或者其他的 path
來和對靜態資源的請求加以區分,此時可以這樣配置:
# 請求跨域,約定代理后端服務請求path以/apis/開頭
location ^~/api/ {
# 這里重寫了請求,將正則匹配中的第一個分組的path拼接到真正的請求后面,并用break停止后續匹配
rewrite ^/api/(.*)$ /$1 break;
proxy_pass b.tansci.com;
# 兩個域名之間cookie的傳遞與回寫
proxy_cookie_domain b.tansci.com a.tansci.com;
}
這樣靜態資源我們使用 b.tansci.com/xx.html
,動態資源我們使用 b.tansci.com/api/getXxx
,瀏覽器頁面看起來仍然訪問的前端服務器,繞過了瀏覽器的同源策略,畢竟我們看起來并沒有跨域。
配置 header 解決跨域
當瀏覽器在訪問跨源的服務器時,也可以在跨域的服務器上直接設置 Nginx,從而前端就可以無感地開發,不用把實際上訪問后端的地址改成前端服務的地址,這樣可適性更高。
比如前端站點是 a.tansci.com
,這個地址下的前端頁面請求 b.tansci.com
下的資源,比如前者的 a.tansci.com/index.html
內容是這樣的:
<html>
<body>
<h1>welcome fe.sherlocked93.club!!<h1>
<script type='text/JAVAscript'>
var xmlhttp = new XMLHttpRequest()
xmlhttp.open("GET", "http://b.tansci.com/index.html", true);
xmlhttp.send();
</script>
</body>
</html>
瀏覽器訪問 http://a.tansci.com/index.html
就會報跨域,直接訪問 http://b.tansci.com/index.html
是可以訪問的。
在 /etc/nginx/conf.d/ 文件夾中新建一個配置文件,對應二級域名 b.tansci.com
:
# /etc/nginx/conf.d/b.tansci.com.conf
server {
listen 80;
server_name b.tansci.com;
add_header 'Access-Control-Allow-Origin' $http_origin; # 全局變量獲得當前請求origin,帶cookie的請求不支持*
add_header 'Access-Control-Allow-Credentials' 'true'; # 為 true 可帶上 cookie
add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS'; # 允許請求方法
add_header 'Access-Control-Allow-Headers' $http_access_control_request_headers; # 允許請求的 header,可以為 *
add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Max-Age' 1728000; # OPTIONS 請求的有效期,在有效期內不用發出另一條預檢請求
add_header 'Content-Type' 'text/plain; charset=utf-8';
add_header 'Content-Length' 0;
return 204; # 200 也可以
}
location / {
root /usr/share/nginx/html/b;
index index.html;
}
}
nginx -s reload
重新加載配置,再訪問 http://a.tansci.com/index.html
就正常訪問了。
配置負載均衡
主要思想就是把負載均勻合理地分發到多個服務器上,實現壓力分流的目的。
主要配置如下:
http {
upstream myserver {
# ip_hash; # ip_hash 方式
# fair; # fair 方式
server 127.0.0.1:8081; # 負載均衡目的服務地址
server 127.0.0.1:8080;
server 127.0.0.1:8082 weight=10; # weight 方式,不寫默認為 1
}
server {
location / {
proxy_pass http://myserver;
proxy_connect_timeout 10;
}
}
}
分配方式:
-
•
輪詢
默認方式,每個請求按時間順序逐一分配到不同的后端服務器,如果后端服務掛了,能自動剔除; -
•
weight
權重分配,指定輪詢幾率,權重越高,在被訪問的概率越大,用于后端服務器性能不均的情況; -
•
ip_hash
每個請求按訪問 IP 的 hash 結果分配,這樣每個訪客固定訪問一個后端服務器,可以解決動態網頁 session 共享問題; -
•
fair(第三方)
按后端服務器的響應時間分配,響應時間短的優先分配,依賴第三方插件 nginx-upstream-fair,需要先安裝;·
配置動靜分離
動靜分離就是把動態和靜態的請求分開。方式主要有兩種:
-
• 一種 是純粹把靜態文件獨立成單獨的域名,放在獨立的服務器上,也是目前主流推崇的方案。
-
• 另外一種方法就是動態跟靜態文件混合在一起發布, 通過 Nginx 配置來分開。
server {
location /www/ {
root /data/;
index index.html index.htm;
}
location /image/ {
root /data/;
autoindex on;
}
}
配置 HTTPS
先申請好證書,在/usr/local/nginx/conf/
目錄下創建文件夾 cert
:
[root@t2 conf]# mkdir cert
# 查看上傳的證書
[root@t2 cert]# ll
-rw-r----- 1 root root 4101 Jul 3 17:13 tansci.com_bundle.crt
-rw-r----- 1 root root 4101 Jul 3 17:13 tansci.com_bundle.pem
-rw-r----- 1 root root 1012 Jul 3 17:13 tansci.com.csr
-rw-r----- 1 root root 1702 Jul 3 17:13 tansci.com.key
上傳證書文件至 /usr/local/nginx/conf/cert
目錄下。
修改 nginx.conf
文件:
# HTTPS server
server {
listen 443 ssl;
server_name www.tansci.com;
# 證書配置
ssl_certificate cert/tansci.com_bundle.crt;
ssl_certificate_key cert/tansci.com.key;
ssl_session_cache shared:SSL:1m;
ssl_session_timeout 5m;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
location / {
root /usr/web;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
# 靜態文件代理
location /static {
root /usr/web;
autoindex on;
}
# api 接口代理
location /api/ {
proxy_pass http://localhost:8080/;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST';
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header REMOTE-HOST $remote_addr;
}
}
常用技巧
靜態服務
server {
listen 80;
server_name static.tansci.com;
charset utf-8; # 防止中文文件名亂碼
location /download {
alias /usr/share/nginx/html/static; # 靜態資源目錄
autoindex on; # 開啟靜態資源列目錄
autoindex_exact_size off; # on(默認)顯示文件的確切大小,單位是byte;off顯示文件大概大小,單位KB、MB、GB
autoindex_localtime off; # off(默認)時顯示的文件時間為GMT時間;on顯示的文件時間為服務器時間
}
}
圖片防盜鏈
server {
listen 80;
server_name *.tansci.com;
# 圖片防盜鏈
location ~* .(gif|jpg|jpeg|png|bmp|swf)$ {
valid_referers none blocked server_names ~.google. ~.baidu. *.qq.com; # 只允許本機 IP 外鏈引用,感謝 @木法傳 的提醒,將百度和谷歌也加入白名單
if ($invalid_referer){
return 403;
}
}
}
請求過濾
# 非指定請求全返回 403
if ( $request_method !~ ^(GET|POST|HEAD)$ ) {
return 403;
}
location / {
# IP訪問限制(只允許IP是 192.168.27.2 機器訪問)
allow 192.168.27.2;
deny all;
root html;
index index.html index.htm;
}
配置圖片、字體等靜態文件緩存
# 圖片緩存時間設置
location ~ .*.(css|js|jpg|png|gif|swf|woff|woff2|eot|svg|ttf|otf|mp3|m4a|aac|txt)$ {
expires 10d;
}
# 如果不希望緩存
expires -1;
HTTP 請求轉發到 HTTPS
server {
listen 80;
server_name www.tansci.com;
# 單域名重定向
if ($host = 'www.tansci.com'){
return 301 https://www.tansci.com$request_uri;
}
# 全局非 https 協議時重定向
if ($scheme != 'https') {
return 301 https://$server_name$request_uri;
}
# 或者全部重定向
return 301 https://$server_name$request_uri;
# 以上配置選擇自己需要的即可,不用全部加
}
泛域名路徑分離
這是一個非常實用的技能,經常有時候我們可能需要配置一些二級或者三級域名,希望通過 Nginx 自動指向對應目錄,如:
-
• test1.doc.tansci.com 自動指向 /usr/share/nginx/html/doc/test1 服務器地址
-
• test2.doc.tansci.com 自動指向 /usr/share/nginx/html/doc/test2 服務器地址
server {
listen 80;
server_name ~^([w-]+).doc.tansci.com$;
root /usr/share/nginx/html/doc/$1;
}