前言
作爲一名前端開發人員,你是不是經常碰到領導讓你上服務器去修改 Nginx
配置,然而你會以“我是前端,這個我不會”爲理由搪塞過去呢!今天就讓我們一起告別這種尷尬,向“真正”的程序員邁進!!!
如果本文對你有所幫助,請點個👍 👍 👍 吧!
Nginx 概述
Nginx
是開源、高性能、高可靠的 Web
和反向代理服務器,而且支持熱部署,幾乎可以做到 7 * 24 小時不間斷運行,即使運行幾個月也不需要重新啓動,還能在不間斷服務的情況下對軟件版本進行熱更新。性能是 Nginx
最重要的考量,其佔用內存少、併發能力強、能支持高達 5w 個併發連接數,最重要的是, Nginx
是免費的並可以商業化,配置使用也比較簡單。
Nginx 特點
- 高併發、高性能;
- 模塊化架構使得它的擴展性非常好;
- 異步非阻塞的事件驅動模型這點和
Node.js
相似; - 相對於其它服務器來說它可以連續幾個月甚至更長而不需要重啓服務器使得它具有高可靠性;
- 熱部署、平滑升級;
- 完全開源,生態繁榮;
Nginx 作用
Nginx 的最重要的幾個使用場景:
- 靜態資源服務,通過本地文件系統提供服務;
- 反向代理服務,延伸出包括緩存、負載均衡等;
API
服務,OpenResty
;
對於前端來說 Node.js
並不陌生, Nginx
和 Node.js
的很多理念類似, HTTP
服務器、事件驅動、異步非阻塞等,且 Nginx
的大部分功能使用 Node.js
也可以實現,但 Nginx
和 Node.js
並不衝突,都有自己擅長的領域。 Nginx
擅長於底層服務器端資源的處理(靜態資源處理轉發、反向代理,負載均衡等), Node.js
更擅長上層具體業務邏輯的處理,兩者可以完美組合。
用一張圖表示:
Nginx 安裝
本文演示的是 Linux
centOS 7.x
的操作系統上安裝 Nginx
,至於在其它操作系統上進行安裝可以網上自行搜索,都非常簡單的。
使用 yum
安裝 Nginx
:
yum install nginx -y
複製代碼
安裝完成後,通過 rpm -ql nginx
命令查看 Nginx
的安裝信息:
# Nginx配置文件
/etc/nginx/nginx.conf # nginx 主配置文件
/etc/nginx/nginx.conf.default
# 可執行程序文件
/usr/bin/nginx-upgrade
/usr/sbin/nginx
# nginx庫文件
/usr/lib/systemd/system/nginx.service # 用於配置系統守護進程
/usr/lib64/nginx/modules # Nginx模塊目錄
# 幫助文檔
/usr/share/doc/nginx-1.16.1
/usr/share/doc/nginx-1.16.1/CHANGES
/usr/share/doc/nginx-1.16.1/README
/usr/share/doc/nginx-1.16.1/README.dynamic
/usr/share/doc/nginx-1.16.1/UPGRADE-NOTES-1.6-to-1.10
# 靜態資源目錄
/usr/share/nginx/html/404.html
/usr/share/nginx/html/50x.html
/usr/share/nginx/html/index.html
# 存放Nginx日誌文件
/var/log/nginx
複製代碼
主要關注的文件夾有兩個:
/etc/nginx/conf.d/
是子配置項存放處,/etc/nginx/nginx.conf
主配置文件會默認把這個文件夾中所有子配置項都引入;/usr/share/nginx/html/
靜態文件都放在這個文件夾,也可以根據你自己的習慣放在其他地方;
Nginx 常用命令
systemctl
系統命令:
# 開機配置
systemctl enable nginx # 開機自動啓動
systemctl disable nginx # 關閉開機自動啓動
# 啓動Nginx
systemctl start nginx # 啓動Nginx成功後,可以直接訪問主機IP,此時會展示Nginx默認頁面
# 停止Nginx
systemctl stop nginx
# 重啓Nginx
systemctl restart nginx
# 重新加載Nginx
systemctl reload nginx
# 查看 Nginx 運行狀態
systemctl status nginx
# 查看Nginx進程
ps -ef | grep nginx
# 殺死Nginx進程
kill -9 pid # 根據上面查看到的Nginx進程號,殺死Nginx進程,-9 表示強制結束進程
複製代碼
Nginx
應用程序命令:
nginx -s reload # 向主進程發送信號,重新加載配置文件,熱重啓
nginx -s reopen # 重啓 Nginx
nginx -s stop # 快速關閉
nginx -s quit # 等待工作進程處理完成後關閉
nginx -T # 查看當前 Nginx 最終的配置
nginx -t # 檢查配置是否有問題
複製代碼
Nginx 核心配置
配置文件結構
Nginx
的典型配置示例:
# main段配置信息
user nginx; # 運行用戶,默認即是nginx,可以不進行設置
worker_processes auto; # Nginx 進程數,一般設置爲和 CPU 核數一樣
error_log /var/log/nginx/error.log warn; # Nginx 的錯誤日誌存放目錄
pid /var/run/nginx.pid; # Nginx 服務啓動時的 pid 存放位置
# events段配置信息
events {
use epoll; # 使用epoll的I/O模型(如果你不知道Nginx該使用哪種輪詢方法,會自動選擇一個最適合你操作系統的)
worker_connections 1024; # 每個進程允許最大併發數
}
# http段配置信息
# 配置使用最頻繁的部分,代理、緩存、日誌定義等絕大多數功能和第三方模塊的配置都在這裏設置
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段配置信息
server {
listen 80; # 配置監聽的端口
server_name localhost; # 配置的域名
# location段配置信息
location / {
root /usr/share/nginx/html; # 網站根目錄
index index.html index.htm; # 默認首頁文件
deny 172.168.22.11; # 禁止訪問的ip地址,可以爲all
allow 172.168.33.44;# 允許訪問的ip地址,可以爲all
}
error_page 500 502 503 504 /50x.html; # 默認50x對應的訪問頁面
error_page 400 404 error.html; # 同上
}
}
複製代碼
main
全局配置,對全局生效;events
配置影響Nginx
服務器與用戶的網絡連接;http
配置代理,緩存,日誌定義等絕大多數功能和第三方模塊的配置;server
配置虛擬主機的相關參數,一個http
塊中可以有多個server
塊;location
用於配置匹配的uri
;upstream
配置後端服務器具體地址,負載均衡配置不可或缺的部分;
用一張圖清晰的展示它的層級結構:
配置文件 main 段核心參數
user
指定運行 Nginx
的 woker
子進程的屬主和屬組,其中組可以不指定。
user USERNAME [GROUP]
user nginx lion; # 用戶是nginx;組是lion
複製代碼
pid
指定運行 Nginx
master
主進程的 pid
文件存放路徑。
pid /opt/nginx/logs/nginx.pid # master主進程的的pid存放在nginx.pid的文件
複製代碼
worker_rlimit_nofile_number
指定 worker
子進程可以打開的最大文件句柄數。
worker_rlimit_nofile 20480; # 可以理解成每個worker子進程的最大連接數量。
複製代碼
worker_rlimit_core
指定 worker
子進程異常終止後的 core
文件,用於記錄分析問題。
worker_rlimit_core 50M; # 存放大小限制
working_directory /opt/nginx/tmp; # 存放目錄
複製代碼
worker_processes_number
指定 Nginx
啓動的 worker
子進程數量。
worker_processes 4; # 指定具體子進程數量
worker_processes auto; # 與當前cpu物理核心數一致
複製代碼
worker_cpu_affinity
將每個 worker
子進程與我們的 cpu
物理核心綁定。
worker_cpu_affinity 0001 0010 0100 1000; # 4個物理核心,4個worker子進程
複製代碼
將每個 worker
子進程與特定 CPU
物理核心綁定,優勢在於,避免同一個 worker
子進程在不同的 CPU
核心上切換,緩存失效,降低性能。但其並不能真正的避免進程切換。
worker_priority
指定 worker
子進程的 nice
值,以調整運行 Nginx
的優先級,通常設定爲負值,以優先調用 Nginx
。
worker_priority -10; # 120-10=110,110就是最終的優先級
複製代碼
Linux
默認進程的優先級值是120,值越小越優先; nice
定範圍爲 -20
到 +19
。
[備註] 應用的默認優先級值是120加上 nice
值等於它最終的值,這個值越小,優先級越高。
worker_shutdown_timeout
指定 worker
子進程優雅退出時的超時時間。
worker_shutdown_timeout 5s;
複製代碼
timer_resolution
worker
子進程內部使用的計時器精度,調整時間間隔越大,系統調用越少,有利於性能提升;反之,系統調用越多,性能下降。
timer_resolution 100ms;
複製代碼
在 Linux
系統中,用戶需要獲取計時器時需要向操作系統內核發送請求,有請求就必然會有開銷,因此這個間隔越大開銷就越小。
daemon
指定 Nginx
的運行方式,前臺還是後臺,前臺用於調試,後臺用於生產。
daemon off; # 默認是on,後臺運行模式
複製代碼
配置文件 events 段核心參數
use
Nginx
使用何種事件驅動模型。
use method; # 不推薦配置它,讓nginx自己選擇
method 可選值爲:select、poll、kqueue、epoll、/dev/poll、eventport
複製代碼
worker_connections
worker
子進程能夠處理的最大併發連接數。
worker_connections 1024 # 每個子進程的最大連接數爲1024
複製代碼
accept_mutex
是否打開負載均衡互斥鎖。
accept_mutex on # 默認是off關閉的,這裏推薦打開
複製代碼
server_name 指令
指定虛擬主機域名。
server_name name1 name2 name3
# 示例:
server_name www.nginx.com;
複製代碼
域名匹配的四種寫法:
- 精確匹配:
server_name www.nginx.com
; - 左側通配:
server_name *.nginx.com
; - 右側統配:
server_name www.nginx.*
; - 正則匹配:
server_name ~^www\.nginx\.*$
;
匹配優先級:精確匹配 > 左側通配符匹配 > 右側通配符匹配 > 正則表達式匹配
server_name
配置實例:
1、配置本地 DNS
解析 vim /etc/hosts
( macOS
系統)
# 添加如下內容,其中 121.42.11.34 是阿里雲服務器IP地址
121.42.11.34 www.nginx-test.com
121.42.11.34 mail.nginx-test.com
121.42.11.34 www.nginx-test.org
121.42.11.34 doc.nginx-test.com
121.42.11.34 www.nginx-test.cn
121.42.11.34 fe.nginx-test.club
複製代碼
[注意] 這裏使用的是虛擬域名進行測試,因此需要配置本地 DNS
解析,如果使用阿里雲上購買的域名,則需要在阿里雲上設置好域名解析。
2、配置阿里雲 Nginx
,vim /etc/nginx/nginx.conf
# 這裏只列舉了http端中的sever端配置
# 左匹配
server {
listen 80;
server_name *.nginx-test.com;
root /usr/share/nginx/html/nginx-test/left-match/;
location / {
index index.html;
}
}
# 正則匹配
server {
listen 80;
server_name ~^.*\.nginx-test\..*$;
root /usr/share/nginx/html/nginx-test/reg-match/;
location / {
index index.html;
}
}
# 右匹配
server {
listen 80;
server_name www.nginx-test.*;
root /usr/share/nginx/html/nginx-test/right-match/;
location / {
index index.html;
}
}
# 完全匹配
server {
listen 80;
server_name www.nginx-test.com;
root /usr/share/nginx/html/nginx-test/all-match/;
location / {
index index.html;
}
}
複製代碼
3、訪問分析
- 當訪問
www.nginx-test.com
時,都可以被匹配上,因此選擇優先級最高的“完全匹配”; - 當訪問
mail.nginx-test.com
時,會進行“左匹配”; - 當訪問
www.nginx-test.org
時,會進行“右匹配”; - 當訪問
doc.nginx-test.com
時,會進行“左匹配”; - 當訪問
www.nginx-test.cn
時,會進行“右匹配”; - 當訪問
fe.nginx-test.club
時,會進行“正則匹配”;
root
指定靜態資源目錄位置,它可以寫在 http
、 server
、 location
等配置中。
root path
例如:
location /image {
root /opt/nginx/static;
}
當用戶訪問 www.test.com/image/1.png 時,實際在服務器找的路徑是 /opt/nginx/static/image/1.png
複製代碼
[注意] root
會將定義路徑與 URI
疊加, alias
則只取定義路徑。
alias
它也是指定靜態資源目錄位置,它只能寫在 location
中。
location /image {
alias /opt/nginx/static/image/;
}
當用戶訪問 www.test.com/image/1.png 時,實際在服務器找的路徑是 /opt/nginx/static/image/1.png
複製代碼
[注意] 使用 alias 末尾一定要添加 /
,並且它只能位於 location
中。
location
配置路徑。
location [ = | ~ | ~* | ^~ ] uri {
...
}
複製代碼
匹配規則:
=
精確匹配;~
正則匹配,區分大小寫;~*
正則匹配,不區分大小寫;^~
匹配到即停止搜索;
匹配優先級: =
> ^~
> ~
> ~*
> 不帶任何字符。
實例:
server {
listen 80;
server_name www.nginx-test.com;
# 只有當訪問 www.nginx-test.com/match_all/ 時纔會匹配到/usr/share/nginx/html/match_all/index.html
location = /match_all/ {
root /usr/share/nginx/html
index index.html
}
# 當訪問 www.nginx-test.com/1.jpg 等路徑時會去 /usr/share/nginx/images/1.jpg 找對應的資源
location ~ \.(jpeg|jpg|png|svg)$ {
root /usr/share/nginx/images;
}
# 當訪問 www.nginx-test.com/bbs/ 時會匹配上 /usr/share/nginx/html/bbs/index.html
location ^~ /bbs/ {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
複製代碼
location 中的反斜線
location /test {
...
}
location /test/ {
...
}
複製代碼
- 不帶
/
當訪問www.nginx-test.com/test
時,Nginx
先找是否有test
目錄,如果有則找test
目錄下的index.html
;如果沒有test
目錄,nginx
則會找是否有test
文件。 - 帶
/
當訪問www.nginx-test.com/test
時,Nginx
先找是否有test
目錄,如果有則找test
目錄下的index.html
,如果沒有它也不會去找是否存在test
文件。
return
停止處理請求,直接返回響應碼或重定向到其他 URL
;執行 return
指令後, location
中後續指令將不會被執行。
return code [text];
return code URL;
return URL;
例如:
location / {
return 404; # 直接返回狀態碼
}
location / {
return 404 "pages not found"; # 返回狀態碼 + 一段文本
}
location / {
return 302 /bbs ; # 返回狀態碼 + 重定向地址
}
location / {
return https://www.baidu.com ; # 返回重定向地址
}
複製代碼
rewrite
根據指定正則表達式匹配規則,重寫 URL
。
語法:rewrite 正則表達式 要替換的內容 [flag];
上下文:server、location、if
示例:rewirte /images/(.*\.jpg)$ /pic/$1; # $1是前面括號(.*\.jpg)的反向引用
複製代碼
flag
可選值的含義:
last
重寫後的URL
發起新請求,再次進入server
段,重試location
的中的匹配;break
直接使用重寫後的URL
,不再匹配其它location
中語句;redirect
返回302臨時重定向;permanent
返回301永久重定向;
server{
listen 80;
server_name fe.lion.club; # 要在本地hosts文件進行配置
root html;
location /search {
rewrite ^/(.*) https://www.baidu.com redirect;
}
location /images {
rewrite /images/(.*) /pics/$1;
}
location /pics {
rewrite /pics/(.*) /photos/$1;
}
location /photos {
}
}
複製代碼
按照這個配置我們來分析:
- 當訪問
fe.lion.club/search
時,會自動幫我們重定向到https://www.baidu.com
。 - 當訪問
fe.lion.club/images/1.jpg
時,第一步重寫URL
爲fe.lion.club/pics/1.jpg
,找到pics
的location
,繼續重寫URL
爲fe.lion.club/photos/1.jpg
,找到/photos
的location
後,去html/photos
目錄下尋找1.jpg
靜態資源。
if 指令
語法:if (condition) {...}
上下文:server、location
示例:
if($http_user_agent ~ Chrome){
rewrite /(.*)/browser/$1 break;
}
複製代碼
condition
判斷條件:
$variable
僅爲變量時,值爲空或以0開頭字符串都會被當做false
處理;=
或!=
相等或不等;~
正則匹配;! ~
非正則匹配;~*
正則匹配,不區分大小寫;-f
或! -f
檢測文件存在或不存在;-d
或! -d
檢測目錄存在或不存在;-e
或! -e
檢測文件、目錄、符號鏈接等存在或不存在;-x
或! -x
檢測文件可以執行或不可執行;
實例:
server {
listen 8080;
server_name localhost;
root html;
location / {
if ( $uri = "/images/" ){
rewrite (.*) /pics/ break;
}
}
}
複製代碼
當訪問 localhost:8080/images/
時,會進入 if
判斷裏面執行 rewrite
命令。
autoindex
用戶請求以 /
結尾時,列出目錄結構,可以用於快速搭建靜態資源下載網站。
autoindex.conf
配置信息:
server {
listen 80;
server_name fe.lion-test.club;
location /download/ {
root /opt/source;
autoindex on; # 打開 autoindex,,可選參數有 on | off
autoindex_exact_size on; # 修改爲off,以KB、MB、GB顯示文件大小,默認爲on,以bytes顯示出⽂件的確切⼤⼩
autoindex_format html; # 以html的方式進行格式化,可選參數有 html | json | xml
autoindex_localtime off; # 顯示的⽂件時間爲⽂件的服務器時間。默認爲off,顯示的⽂件時間爲GMT時間
}
}
複製代碼
當訪問 fe.lion.com/download/
時,會把服務器 /opt/source/download/
路徑下的文件展示出來,如下圖所示:
變量
Nginx
提供給使用者的變量非常多,但是終究是一個完整的請求過程所產生數據, Nginx
將這些數據以變量的形式提供給使用者。
下面列舉些項目中常用的變量:
變量名 | 含義 |
---|---|
remote_addr |
客戶端 IP 地址 |
remote_port |
客戶端端口 |
server_addr |
服務端 IP 地址 |
server_port |
服務端端口 |
server_protocol |
服務端協議 |
binary_remote_addr |
二進制格式的客戶端 IP 地址 |
connection |
TCP 連接的序號,遞增 |
connection_request |
TCP 連接當前的請求數量 |
uri |
請求的URL,不包含參數 |
request_uri |
請求的URL,包含參數 |
scheme |
協議名, http 或 https |
request_method |
請求方法 |
request_length |
全部請求的長度,包含請求行、請求頭、請求體 |
args |
全部參數字符串 |
arg_參數名 |
獲取特定參數值 |
is_args |
URL 中是否有參數,有的話返回 ? ,否則返回空 |
query_string |
與 args 相同 |
host |
請求信息中的 Host ,如果請求中沒有 Host 行,則在請求頭中找,最後使用 nginx 中設置的 server_name 。 |
http_user_agent |
用戶瀏覽器 |
http_referer |
從哪些鏈接過來的請求 |
http_via |
每經過一層代理服務器,都會添加相應的信息 |
http_cookie |
獲取用戶 cookie |
request_time |
處理請求已消耗的時間 |
https |
是否開啓了 https ,是則返回 on ,否則返回空 |
request_filename |
磁盤文件系統待訪問文件的完整路徑 |
document_root |
由 URI 和 root/alias 規則生成的文件夾路徑 |
limit_rate |
返回響應時的速度上限值 |
實例演示 var.conf
:
server{
listen 8081;
server_name var.lion-test.club;
root /usr/share/nginx/html;
location / {
return 200 "
remote_addr: $remote_addr
remote_port: $remote_port
server_addr: $server_addr
server_port: $server_port
server_protocol: $server_protocol
binary_remote_addr: $binary_remote_addr
connection: $connection
uri: $uri
request_uri: $request_uri
scheme: $scheme
request_method: $request_method
request_length: $request_length
args: $args
arg_pid: $arg_pid
is_args: $is_args
query_string: $query_string
host: $host
http_user_agent: $http_user_agent
http_referer: $http_referer
http_via: $http_via
request_time: $request_time
https: $https
request_filename: $request_filename
document_root: $document_root
";
}
}
複製代碼
當我們訪問 http://var.lion-test.club:8081/test?pid=121414&cid=sadasd
時,由於 Nginx
中寫了 return
方法,因此 chrome
瀏覽器會默認爲我們下載一個文件,下面展示的就是下載的文件內容:
remote_addr: 27.16.220.84
remote_port: 56838
server_addr: 172.17.0.2
server_port: 8081
server_protocol: HTTP/1.1
binary_remote_addr: 茉
connection: 126
uri: /test/
request_uri: /test/?pid=121414&cid=sadasd
scheme: http
request_method: GET
request_length: 518
args: pid=121414&cid=sadasd
arg_pid: 121414
is_args: ?
query_string: pid=121414&cid=sadasd
host: var.lion-test.club
http_user_agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.182 Safari/537.36
http_referer:
http_via:
request_time: 0.000
https:
request_filename: /usr/share/nginx/html/test/
document_root: /usr/share/nginx/html
複製代碼
Nginx
的配置還有非常多,以上只是羅列了一些常用的配置,在實際項目中還是要學會查閱文檔。
Nginx 應用核心概念
代理是在服務器和客戶端之間假設的一層服務器,代理將接收客戶端的請求並將它轉發給服務器,然後將服務端的響應轉發給客戶端。
不管是正向代理還是反向代理,實現的都是上面的功能。
正向代理
正向代理,意思是一個位於客戶端和原始服務器(origin server)之間的服務器,爲了從原始服務器取得內容,客戶端向代理髮送一個請求並指定目標(原始服務器),然後代理向原始服務器轉交請求並將獲得的內容返回給客戶端。
正向代理是爲我們服務的,即爲客戶端服務的,客戶端可以根據正向代理訪問到它本身無法訪問到的服務器資源。
正向代理對我們是透明的,對服務端是非透明的,即服務端並不知道自己收到的是來自代理的訪問還是來自真實客戶端的訪問。
反向代理
- 反向代理*(Reverse Proxy)方式是指以代理服務器來接受internet上的連接請求,然後將請求轉發給內部網絡上的服務器,並將從服務器上得到的結果返回給internet上請求連接的客戶端,此時代理服務器對外就表現爲一個反向代理服務器。
反向代理是爲服務端服務的,反向代理可以幫助服務器接收來自客戶端的請求,幫助服務器做請求轉發,負載均衡等。
反向代理對服務端是透明的,對我們是非透明的,即我們並不知道自己訪問的是代理服務器,而服務器知道反向代理在爲他服務。
反向代理的優勢:
- 隱藏真實服務器;
- 負載均衡便於橫向擴充後端動態服務;
- 動靜分離,提升系統健壯性;
那麼“動靜分離”是什麼?負載均衡又是什麼?
動靜分離
動靜分離是指在 web
服務器架構中,將靜態頁面與動態頁面或者靜態內容接口和動態內容接口分開不同系統訪問的架構設計方法,進而提示整個服務的訪問性和可維護性。
一般來說,都需要將動態資源和靜態資源分開,由於 Nginx
的高併發和靜態資源緩存等特性,經常將靜態資源部署在 Nginx
上。如果請求的是靜態資源,直接到靜態資源目錄獲取資源,如果是動態資源的請求,則利用反向代理的原理,把請求轉發給對應後臺應用去處理,從而實現動靜分離。
使用前後端分離後,可以很大程度提升靜態資源的訪問速度,即使動態服務不可用,靜態資源的訪問也不會受到影響。
負載均衡
一般情況下,客戶端發送多個請求到服務器,服務器處理請求,其中一部分可能要操作一些資源比如數據庫、靜態資源等,服務器處理完畢後,再將結果返回給客戶端。
這種模式對於早期的系統來說,功能要求不復雜,且併發請求相對較少的情況下還能勝任,成本也低。隨着信息數量不斷增長,訪問量和數據量飛速增長,以及系統業務複雜度持續增加,這種做法已無法滿足要求,併發量特別大時,服務器容易崩。
很明顯這是由於服務器性能的瓶頸造成的問題,除了堆機器之外,最重要的做法就是負載均衡。
請求爆發式增長的情況下,單個機器性能再強勁也無法滿足要求了,這個時候集羣的概念產生了,單個服務器解決不了的問題,可以使用多個服務器,然後將請求分發到各個服務器上,將負載分發到不同的服務器,這就是負載均衡,核心是「分攤壓力」。 Nginx
實現負載均衡,一般來說指的是將請求轉發給服務器集羣。
舉個具體的例子,晚高峯乘坐地鐵的時候,入站口經常會有地鐵工作人員大喇叭“請走 B
口, B
口人少車空....”,這個工作人員的作用就是負載均衡。
Nginx
實現負載均衡的策略:
- 輪詢策略:默認情況下采用的策略,將所有客戶端請求輪詢分配給服務端。這種策略是可以正常工作的,但是如果其中某一臺服務器壓力太大,出現延遲,會影響所有分配在這臺服務器下的用戶。
- 最小連接數策略:將請求優先分配給壓力較小的服務器,它可以平衡每個隊列的長度,並避免向壓力大的服務器添加更多的請求。
- 最快響應時間策略:優先分配給響應時間最短的服務器。
- 客戶端
ip
綁定策略:來自同一個ip
的請求永遠只分配一臺服務器,有效解決了動態網頁存在的session
共享問題。
Nginx 實戰配置
在配置反向代理和負載均衡等等功能之前,有兩個核心模塊是我們必須要掌握的,這兩個模塊應該說是 Nginx
應用配置中的核心,它們分別是: upstream
、proxy_pass
。
upstream
用於定義上游服務器(指的就是後臺提供的應用服務器)的相關信息。
語法:upstream name {
...
}
上下文:http
示例:
upstream back_end_server{
server 192.168.100.33:8081
}
複製代碼
在 upstream
內可使用的指令:
server
定義上游服務器地址;zone
定義共享內存,用於跨worker
子進程;keepalive
對上游服務啓用長連接;keepalive_requests
一個長連接最多請求HTTP
的個數;keepalive_timeout
空閒情形下,一個長連接的超時時長;hash
哈希負載均衡算法;ip_hash
依據IP
進行哈希計算的負載均衡算法;least_conn
最少連接數負載均衡算法;least_time
最短響應時間負載均衡算法;random
隨機負載均衡算法;
server
定義上游服務器地址。
語法:server address [parameters]
上下文:upstream
複製代碼
parameters
可選值:
weight=number
權重值,默認爲1;max_conns=number
上游服務器的最大併發連接數;fail_timeout=time
服務器不可用的判定時間;max_fails=numer
服務器不可用的檢查次數;backup
備份服務器,僅當其他服務器都不可用時纔會啓用;down
標記服務器長期不可用,離線維護;
keepalive
限制每個 worker
子進程與上游服務器空閒長連接的最大數量。
keepalive connections;
上下文:upstream
示例:keepalive 16;
複製代碼
keepalive_requests
單個長連接可以處理的最多 HTTP
請求個數。
語法:keepalive_requests number;
默認值:keepalive_requests 100;
上下文:upstream
複製代碼
keepalive_timeout
空閒長連接的最長保持時間。
語法:keepalive_timeout time;
默認值:keepalive_timeout 60s;
上下文:upstream
複製代碼
配置實例
upstream back_end{
server 127.0.0.1:8081 weight=3 max_conns=1000 fail_timeout=10s max_fails=2;
keepalive 32;
keepalive_requests 50;
keepalive_timeout 30s;
}
複製代碼
proxy_pass
用於配置代理服務器。
語法:proxy_pass URL;
上下文:location、if、limit_except
示例:
proxy_pass http://127.0.0.1:8081
proxy_pass http://127.0.0.1:8081/proxy
複製代碼
URL
參數原則
URL
必須以http
或https
開頭;URL
中可以攜帶變量;URL
中是否帶URI
,會直接影響發往上游請求的URL
;
接下來讓我們來看看兩種常見的 URL
用法:
proxy_pass http://192.168.100.33:8081
proxy_pass http://192.168.100.33:8081/
這兩種用法的區別就是帶 /
和不帶 /
,在配置代理時它們的區別可大了:
- 不帶
/
意味着Nginx
不會修改用戶URL
,而是直接透傳給上游的應用服務器; - 帶
/
意味着Nginx
會修改用戶URL
,修改方法是將location
後的URL
從用戶URL
中刪除;
不帶 /
的用法:
location /bbs/{
proxy_pass http://127.0.0.1:8080;
}
複製代碼
分析:
- 用戶請求
URL
:/bbs/abc/test.html
- 請求到達
Nginx
的URL
:/bbs/abc/test.html
- 請求到達上游應用服務器的
URL
:/bbs/abc/test.html
帶 /
的用法:
location /bbs/{
proxy_pass http://127.0.0.1:8080/;
}
複製代碼
分析:
- 用戶請求
URL
:/bbs/abc/test.html
- 請求到達
Nginx
的URL
:/bbs/abc/test.html
- 請求到達上游應用服務器的
URL
:/abc/test.html
並沒有拼接上 /bbs
,這點和 root
與 alias
之間的區別是保持一致的。
配置反向代理
這裏爲了演示更加接近實際,作者準備了兩臺雲服務器,它們的公網 IP
分別是: 121.42.11.34
與 121.5.180.193
。
我們把 121.42.11.34
服務器作爲上游服務器,做如下配置:
# /etc/nginx/conf.d/proxy.conf
server{
listen 8080;
server_name localhost;
location /proxy/ {
root /usr/share/nginx/html/proxy;
index index.html;
}
}
# /usr/share/nginx/html/proxy/index.html
<h1> 121.42.11.34 proxy html </h1>
複製代碼
配置完成後重啓 Nginx
服務器 nginx -s reload
。
把 121.5.180.193
服務器作爲代理服務器,做如下配置:
# /etc/nginx/conf.d/proxy.conf
upstream back_end {
server 121.42.11.34:8080 weight=2 max_conns=1000 fail_timeout=10s max_fails=3;
keepalive 32;
keepalive_requests 80;
keepalive_timeout 20s;
}
server {
listen 80;
server_name proxy.lion.club;
location /proxy {
proxy_pass http://back_end/proxy;
}
}
複製代碼
本地機器要訪問 proxy.lion.club
域名,因此需要配置本地 hosts
,通過命令:vim /etc/hosts
進入配置文件,添加如下內容:
121.5.180.193 proxy.lion.club
複製代碼
分析:
- 當訪問
proxy.lion.club/proxy
時通過upstream
的配置找到121.42.11.34:8080
; - 因此訪問地址變爲
http://121.42.11.34:8080/proxy
; - 連接到
121.42.11.34
服務器,找到8080
端口提供的server
; - 通過
server
找到/usr/share/nginx/html/proxy/index.html
資源,最終展示出來。
配置負載均衡
配置負載均衡主要是要使用 upstream
指令。
我們把 121.42.11.34
服務器作爲上游服務器,做如下配置( /etc/nginx/conf.d/balance.conf
):
server{
listen 8020;
location / {
return 200 'return 8020 \n';
}
}
server{
listen 8030;
location / {
return 200 'return 8030 \n';
}
}
server{
listen 8040;
location / {
return 200 'return 8040 \n';
}
}
複製代碼
配置完成後:
nginx -t
檢測配置是否正確;nginx -s reload
重啓Nginx
服務器;- 執行
ss -nlt
命令查看端口是否被佔用,從而判斷Nginx
服務是否正確啓動。
把 121.5.180.193
服務器作爲代理服務器,做如下配置( /etc/nginx/conf.d/balance.conf
):
upstream demo_server {
server 121.42.11.34:8020;
server 121.42.11.34:8030;
server 121.42.11.34:8040;
}
server {
listen 80;
server_name balance.lion.club;
location /balance/ {
proxy_pass http://demo_server;
}
}
複製代碼
配置完成後重啓 Nginx
服務器。並且在需要訪問的客戶端配置好 ip
和域名的映射關係。
# /etc/hosts
121.5.180.193 balance.lion.club
複製代碼
在客戶端機器執行 curl http://balance.lion.club/balance/
命令:
不難看出,負載均衡的配置已經生效了,每次給我們分發的上游服務器都不一樣。就是通過簡單的輪詢策略進行上游服務器分發。
接下來,我們再來了解下 Nginx
的其它分發策略。
hash 算法
通過制定關鍵字作爲 hash key
,基於 hash
算法映射到特定的上游服務器中。關鍵字可以包含有變量、字符串。
upstream demo_server {
hash $request_uri;
server 121.42.11.34:8020;
server 121.42.11.34:8030;
server 121.42.11.34:8040;
}
server {
listen 80;
server_name balance.lion.club;
location /balance/ {
proxy_pass http://demo_server;
}
}
複製代碼
hash $request_uri
表示使用 request_uri
變量作爲 hash
的 key
值,只要訪問的 URI
保持不變,就會一直分發給同一臺服務器。
ip_hash
根據客戶端的請求 ip
進行判斷,只要 ip
地址不變就永遠分配到同一臺主機。它可以有效解決後臺服務器 session
保持的問題。
upstream demo_server {
ip_hash;
server 121.42.11.34:8020;
server 121.42.11.34:8030;
server 121.42.11.34:8040;
}
server {
listen 80;
server_name balance.lion.club;
location /balance/ {
proxy_pass http://demo_server;
}
}
複製代碼
最少連接數算法
各個 worker
子進程通過讀取共享內存的數據,來獲取後端服務器的信息。來挑選一臺當前已建立連接數最少的服務器進行分配請求。
語法:least_conn;
上下文:upstream;
複製代碼
示例:
upstream demo_server {
zone test 10M; # zone可以設置共享內存空間的名字和大小
least_conn;
server 121.42.11.34:8020;
server 121.42.11.34:8030;
server 121.42.11.34:8040;
}
server {
listen 80;
server_name balance.lion.club;
location /balance/ {
proxy_pass http://demo_server;
}
}
複製代碼
最後你會發現,負載均衡的配置其實一點都不復雜。
配置緩存
緩存可以非常有效的提升性能,因此不論是客戶端(瀏覽器),還是代理服務器( Nginx
),乃至上游服務器都多少會涉及到緩存。可見緩存在每個環節都是非常重要的。下面讓我們來學習 Nginx
中如何設置緩存策略。
proxy_cache
存儲一些之前被訪問過、而且可能將要被再次訪問的資源,使用戶可以直接從代理服務器獲得,從而減少上游服務器的壓力,加快整個訪問速度。
語法:proxy_cache zone | off ; # zone 是共享內存的名稱
默認值:proxy_cache off;
上下文:http、server、location
複製代碼
proxy_cache_path
設置緩存文件的存放路徑。
語法:proxy_cache_path path [level=levels] ...可選參數省略,下面會詳細列舉
默認值:proxy_cache_path off
上下文:http
複製代碼
參數含義:
path
緩存文件的存放路徑;level path
的目錄層級;keys_zone
設置共享內存;inactive
在指定時間內沒有被訪問,緩存會被清理,默認10分鐘;
proxy_cache_key
設置緩存文件的 key
。
語法:proxy_cache_key
默認值:proxy_cache_key $scheme$proxy_host$request_uri;
上下文:http、server、location
複製代碼
proxy_cache_valid
配置什麼狀態碼可以被緩存,以及緩存時長。
語法:proxy_cache_valid [code...] time;
上下文:http、server、location
配置示例:proxy_cache_valid 200 304 2m;; # 說明對於狀態爲200和304的緩存文件的緩存時間是2分鐘
複製代碼
proxy_no_cache
定義相應保存到緩存的條件,如果字符串參數的至少一個值不爲空且不等於“ 0”,則將不保存該響應到緩存。
語法:proxy_no_cache string;
上下文:http、server、location
示例:proxy_no_cache $http_pragma $http_authorization;
複製代碼
proxy_cache_bypass
定義條件,在該條件下將不會從緩存中獲取響應。
語法:proxy_cache_bypass string;
上下文:http、server、location
示例:proxy_cache_bypass $http_pragma $http_authorization;
複製代碼
upstream_cache_status 變量
它存儲了緩存是否命中的信息,會設置在響應頭信息中,在調試中非常有用。
MISS: 未命中緩存
HIT: 命中緩存
EXPIRED: 緩存過期
STALE: 命中了陳舊緩存
REVALIDDATED: Nginx驗證陳舊緩存依然有效
UPDATING: 內容陳舊,但正在更新
BYPASS: X響應從原始服務器獲取
複製代碼
配置實例
我們把 121.42.11.34
服務器作爲上游服務器,做如下配置( /etc/nginx/conf.d/cache.conf
):
server {
listen 1010;
root /usr/share/nginx/html/1010;
location / {
index index.html;
}
}
server {
listen 1020;
root /usr/share/nginx/html/1020;
location / {
index index.html;
}
}
複製代碼
把 121.5.180.193
服務器作爲代理服務器,做如下配置( /etc/nginx/conf.d/cache.conf
):
proxy_cache_path /etc/nginx/cache_temp levels=2:2 keys_zone=cache_zone:30m max_size=2g inactive=60m use_temp_path=off;
upstream cache_server{
server 121.42.11.34:1010;
server 121.42.11.34:1020;
}
server {
listen 80;
server_name cache.lion.club;
location / {
proxy_cache cache_zone; # 設置緩存內存,上面配置中已經定義好的
proxy_cache_valid 200 5m; # 緩存狀態爲200的請求,緩存時長爲5分鐘
proxy_cache_key $request_uri; # 緩存文件的key爲請求的URI
add_header Nginx-Cache-Status $upstream_cache_status # 把緩存狀態設置爲頭部信息,響應給客戶端
proxy_pass http://cache_server; # 代理轉發
}
}
複製代碼
緩存就是這樣配置,我們可以在 /etc/nginx/cache_temp
路徑下找到相應的緩存文件。
對於一些實時性要求非常高的頁面或數據來說,就不應該去設置緩存,下面來看看如何配置不緩存的內容。
...
server {
listen 80;
server_name cache.lion.club;
# URI 中後綴爲 .txt 或 .text 的設置變量值爲 "no cache"
if ($request_uri ~ \.(txt|text)$) {
set $cache_name "no cache"
}
location / {
proxy_no_cache $cache_name; # 判斷該變量是否有值,如果有值則不進行緩存,如果沒有值則進行緩存
proxy_cache cache_zone; # 設置緩存內存
proxy_cache_valid 200 5m; # 緩存狀態爲200的請求,緩存時長爲5分鐘
proxy_cache_key $request_uri; # 緩存文件的key爲請求的URI
add_header Nginx-Cache-Status $upstream_cache_status # 把緩存狀態設置爲頭部信息,響應給客戶端
proxy_pass http://cache_server; # 代理轉發
}
}
複製代碼
HTTPS
在學習如何配置 HTTPS
之前,我們先來簡單回顧下 HTTPS
的工作流程是怎麼樣的?它是如何進行加密保證安全的?
HTTPS 工作流程
- 客戶端(瀏覽器)訪問
https://www.baidu.com
百度網站; - 百度服務器返回
HTTPS
使用的CA
證書; - 瀏覽器驗證
CA
證書是否爲合法證書; - 驗證通過,證書合法,生成一串隨機數並使用公鑰(證書中提供的)進行加密;
- 發送公鑰加密後的隨機數給百度服務器;
- 百度服務器拿到密文,通過私鑰進行解密,獲取到隨機數(公鑰加密,私鑰解密,反之也可以);
- 百度服務器把要發送給瀏覽器的內容,使用隨機數進行加密後傳輸給瀏覽器;
- 此時瀏覽器可以使用隨機數進行解密,獲取到服務器的真實傳輸內容;
這就是 HTTPS
的基本運作原理,使用對稱加密和非對稱機密配合使用,保證傳輸內容的安全性。
關於HTTPS更多知識,可以查看作者的另外一篇文章《學習 HTTP 協議》。
配置證書
下載證書的壓縮文件,裏面有個 Nginx
文件夾,把 xxx.crt
和 xxx.key
文件拷貝到服務器目錄,再進行如下配置:
server {
listen 443 ssl http2 default_server; # SSL 訪問端口號爲 443
server_name lion.club; # 填寫綁定證書的域名(我這裏是隨便寫的)
ssl_certificate /etc/nginx/https/lion.club_bundle.crt; # 證書地址
ssl_certificate_key /etc/nginx/https/lion.club.key; # 私鑰地址
ssl_session_timeout 10m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # 支持ssl協議版本,默認爲後三個,主流版本是[TLSv1.2]
location / {
root /usr/share/nginx/html;
index index.html index.htm;
}
}
複製代碼
如此配置後就能正常訪問 HTTPS
版的網站了。
配置跨域 CORS
先簡單回顧下跨域究竟是怎麼回事。
跨域的定義
同源策略限制了從同一個源加載的文檔或腳本如何與來自另一個源的資源進行交互。這是一個用於隔離潛在惡意文件的重要安全機制。通常不允許不同源間的讀操作。
同源的定義
如果兩個頁面的協議,端口(如果有指定)和域名都相同,則兩個頁面具有相同的源。
下面給出了與 URL http://store.company.com/dir/page.html
的源進行對比的示例:
http://store.company.com/dir2/other.html 同源
https://store.company.com/secure.html 不同源,協議不同
http://store.company.com:81/dir/etc.html 不同源,端口不同
http://news.company.com/dir/other.html 不同源,主機不同
複製代碼
不同源會有如下限制:
Web
數據層面,同源策略限制了不同源的站點讀取當前站點的Cookie
、IndexDB
、LocalStorage
等數據。DOM
層面,同源策略限制了來自不同源的JavaScript
腳本對當前DOM
對象讀和寫的操作。- 網絡層面,同源策略限制了通過
XMLHttpRequest
等方式將站點的數據發送給不同源的站點。
Nginx 解決跨域的原理
例如:
- 前端
server
的域名爲:fe.server.com
- 後端服務的域名爲:
dev.server.com
現在我在 fe.server.com
對 dev.server.com
發起請求一定會出現跨域。
現在我們只需要啓動一個 Nginx
服務器,將 server_name
設置爲 fe.server.com
然後設置相應的 location
以攔截前端需要跨域的請求,最後將請求代理回 dev.server.com
。如下面的配置:
server {
listen 80;
server_name fe.server.com;
location / {
proxy_pass dev.server.com;
}
}
複製代碼
這樣可以完美繞過瀏覽器的同源策略: fe.server.com
訪問 Nginx
的 fe.server.com
屬於同源訪問,而 Nginx
對服務端轉發的請求不會觸發瀏覽器的同源策略。
配置開啓 gzip 壓縮
GZIP
是規定的三種標準 HTTP
壓縮格式之一。目前絕大多數的網站都在使用 GZIP
傳輸 HTML
、CSS
、 JavaScript
等資源文件。
對於文本文件, GZiP
的效果非常明顯,開啓後傳輸所需流量大約會降至 1/4~1/3
。
並不是每個瀏覽器都支持 gzip
的,如何知道客戶端是否支持 gzip
呢,請求頭中的 Accept-Encoding
來標識對壓縮的支持。
啓用 gzip
同時需要客戶端和服務端的支持,如果客戶端支持 gzip
的解析,那麼只要服務端能夠返回 gzip
的文件就可以啓用 gzip
了,我們可以通過 Nginx
的配置來讓服務端支持 gzip
。下面的 respone
中 content-encoding:gzip
,指服務端開啓了 gzip
的壓縮方式。
在 /etc/nginx/conf.d/
文件夾中新建配置文件 gzip.conf
:
# # 默認off,是否開啓gzip
gzip on;
# 要採用 gzip 壓縮的 MIME 文件類型,其中 text/html 被系統強制啓用;
gzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;
# ---- 以上兩個參數開啓就可以支持Gzip壓縮了 ---- #
# 默認 off,該模塊啓用後,Nginx 首先檢查是否存在請求靜態文件的 gz 結尾的文件,如果有則直接返回該 .gz 文件內容;
gzip_static on;
# 默認 off,nginx做爲反向代理時啓用,用於設置啓用或禁用從代理服務器上收到相應內容 gzip 壓縮;
gzip_proxied any;
# 用於在響應消息頭中添加 Vary:Accept-Encoding,使代理服務器根據請求頭中的 Accept-Encoding 識別是否啓用 gzip 壓縮;
gzip_vary on;
# gzip 壓縮比,壓縮級別是 1-9,1 壓縮級別最低,9 最高,級別越高壓縮率越大,壓縮時間越長,建議 4-6;
gzip_comp_level 6;
# 獲取多少內存用於緩存壓縮結果,16 8k 表示以 8k*16 爲單位獲得;
gzip_buffers 16 8k;
# 允許壓縮的頁面最小字節數,頁面字節數從header頭中的 Content-Length 中進行獲取。默認值是 0,不管頁面多大都壓縮。建議設置成大於 1k 的字節數,小於 1k 可能會越壓越大;
# gzip_min_length 1k;
# 默認 1.1,啓用 gzip 所需的 HTTP 最低版本;
gzip_http_version 1.1;
複製代碼
其實也可以通過前端構建工具例如 webpack
、rollup
等在打生產包時就做好 Gzip
壓縮,然後放到 Nginx
服務器中,這樣可以減少服務器的開銷,加快訪問速度。
關於 Nginx
的實際應用就學習到這裏,相信通過掌握了 Nginx
核心配置以及實戰配置,之後再遇到什麼需求,我們也能輕鬆應對。接下來,讓我們再深入一點學習下 Nginx
的架構。
Nginx 架構
進程結構
多進程結構 Nginx
的進程模型圖:
多進程中的 Nginx
進程架構如下圖所示,會有一個父進程( Master Process
),它會有很多子進程( Child Processes
)。
Master Process
用來管理子進程的,其本身並不真正處理用戶請求。- 某個子進程
down
掉的話,它會向Master
進程發送一條消息,表明自己不可用了,此時Master
進程會去新起一個子進程。 - 某個配置文件被修改了
Master
進程會去通知work
進程獲取新的配置信息,這也就是我們所說的熱部署。
- 某個子進程
- 子進程間是通過共享內存的方式進行通信的。
配置文件重載原理
reload
重載配置文件的流程:
- 向
master
進程發送HUP
信號(reload
命令); master
進程檢查配置語法是否正確;master
進程打開監聽端口;master
進程使用新的配置文件啓動新的worker
子進程;master
進程向老的worker
子進程發送QUIT
信號;- 老的
worker
進程關閉監聽句柄,處理完當前連接後關閉進程; - 整個過程
Nginx
始終處於平穩運行中,實現了平滑升級,用戶無感知;
Nginx 模塊化管理機制
Nginx
的內部結構是由核心部分和一系列的功能模塊所組成。這樣劃分是爲了使得每個模塊的功能相對簡單,便於開發,同時也便於對系統進行功能擴展。Nginx
的模塊是互相獨立的,低耦合高內聚。
來源:https://juejin.cn/post/6942607113118023710