「查缺補漏」鞏固你的Nginx知識體系
Nginx篇
基本介紹
Nginx是一款輕量級的 Web伺服器 / 反向代理伺服器 / 電子郵件(IMAP/POP3)代理伺服器,主要的優點是:
-
支援高併發連線,尤其是靜態介面,官方測試Nginx能夠支撐5萬併發連線
-
記憶體佔用極低
-
配置簡單,使用靈活,可以基於自身需要增強其功能,同時支援自定義模組的開發
使用靈活:可以根據需要,配置不同的負載均衡模式,URL地址重寫等功能
-
穩定性高,在進行反向代理時,宕機的概率很低
-
支援熱部署,應用啟動過載非常迅速
基礎使用
Windows版
安裝
檔案下載地址:http://nginx.org/en/docs/windows.html
如果下載很慢可以用該連結:
百度雲盤連結: https://pan.baidu.com/s/1r3mSEGhmz4HA46Cw9w6QTQ 提取碼: d8bi
解壓即可
基本命令
# 啟動 # 建議使用第一種,第二種會使視窗一直處於執行中,不能進行其他命令操作 C:\server\nginx-1.19.2> start nginx C:\server\nginx-1.19.2> nginx.exe # 停止 # stop是快速停止nginx,可能並不儲存相關資訊;quit是完整有序的停止nginx,並儲存相關資訊 C:\server\nginx-1.19.2> nginx.exe -s stop C:\server\nginx-1.19.2> nginx.exe -s quit # 過載Nginx # 當配置資訊修改,需要重新載入這些配置時使用此命令 C:\server\nginx-1.19.2> nginx.exe -s reload # 重新開啟日誌檔案 C:\server\nginx-1.19.2> nginx.exe -s reopen # 檢視Nginx版本 C:\server\nginx-1.19.2> nginx -v # 檢視配置檔案是否正確 C:\server\nginx-1.19.2> nginx -t
簡單Demo
-
利用
SwitchHost
軟體編輯域名和IP的對映關係,或到目錄C:\Windows\System32\drivers\etc
下,編輯hosts
檔案,增加配置如下(Mac 同理)127.0.0.1 kerwin.demo.com
PS:推薦使用軟體
SwitchHost
,工作時幾乎是必用的 -
修改配置,如圖所示:
效果如圖所示:
Nginx在架構體系中的作用
- 閘道器 (面向客戶的總入口)
- 虛擬主機(為不同域名 / ip / 埠提供服務。如:VPS虛擬伺服器)
- 路由(正向代理 / 反向代理)
- 靜態伺服器
- 負載叢集(提供負載均衡)
閘道器
閘道器:可以簡單的理解為使用者請求和伺服器響應的關口,即面向使用者的總入口
閘道器可以攔截客戶端所有請求,對該請求進行許可權控制、負載均衡、日誌管理、介面呼叫監控等,因此無論使用什麼架構體系,都可以使用Nginx
作為最外層的閘道器
虛擬主機
虛擬主機的定義:虛擬主機是一種特殊的軟硬體技術,它可以將網路上的每一臺計算機分成多個虛擬主機,每個虛擬主機可以獨立對外提供 www 服務,這樣就可以實現一臺主機對外提供多個 web 服務,每個虛擬主機之間是獨立的,互不影響的。
通過 Nginx 可以實現虛擬主機的配置,Nginx 支援三種類型的虛擬主機配置
- 基於 IP 的虛擬主機
- 基於域名的虛擬主機
- 基於埠的虛擬主機
表現形式其實大家多見過,即:
# 每個 server 就是一個虛擬主機
http {
# ...
server{
# ...
}
# ...
server{
# ...
}
}
路由
在Nginx
的配置檔案中,我們經常可以看到這樣的配置:
location / {
#....
}
location
在此處就起到了路由的作用,比如我們在同一個虛擬主機內定義兩個不同的路由,如下:
location / {
proxy_pass https://www.baidu.com/;
}
location /api {
proxy_pass https://apinew.juejin.im/user_api/v1/user/get?aid=2608&user_id=1275089220013336¬_self=1;
}
效果如下:
因為路由的存在,為我們後續解決跨域問題
提供了一定的思路,同時配置內容和API介面等更加方便
PS:路由的功能非常強大,支援正則匹配
正向與反向代理
此處額外解釋一下proxy_pass
的含義
在Nginx
中配置proxy_pass
代理轉發時,如果在proxy_pass
後面的url加 /
,表示絕對根路徑;
如果沒有/
,表示相對路徑
正向代理
- 代理客戶;
- 隱藏真實的客戶,為客戶端收發請求,使真實客戶端對伺服器不可見;
- 一個區域網內的所有使用者可能被一臺伺服器做了正向代理,由該臺伺服器負責 HTTP 請求;
- 意味著同伺服器做通訊的是正向代理伺服器;
反向代理
- 代理伺服器;
- 隱藏了真實的伺服器,為伺服器收發請求,使真實伺服器對客戶端不可見;
- 負載均衡伺服器,將使用者的請求分發到空閒的伺服器上;
- 意味著使用者和負載均衡伺服器直接通訊,即使用者解析伺服器域名時得到的是負載均衡伺服器的 IP ;
共同點
- 都是做為伺服器和客戶端的中間層
- 都可以加強內網的安全性,阻止 web 攻擊
- 都可以做快取機制,提高訪問速度
區別
- 正向代理其實是客戶端的代理,反向代理則是伺服器的代理。
- 正向代理中,伺服器並不知道真正的客戶端到底是誰;而在反向代理中,客戶端也不知道真正的伺服器是誰。
- 作用不同。正向代理主要是用來解決訪問限制問題;而反向代理則是提供負載均衡、安全防護等作用。
靜態伺服器
靜態伺服器是Nginx
的強項,使用非常容易,在預設配置下本身就是指向了靜態的HTML介面,如:
location / {
root html;
index index.html index.htm;
}
所以前端同學們,如果構建好了介面,可以進行相應的配置,把介面指向目標資料夾中即可,root
指的是html
資料夾
負載均衡
負載均衡功能是Nginx
另一大殺手鐗,一共有5種方式,著重介紹一下。
輪詢
每個請求按時間順序逐一分配到不同的後端伺服器,如果後端伺服器down掉,能自動剔除,配置如下:
upstream tomcatserver {
server 192.168.0.1;
server 192.168.0.2;
}
輪詢策略是預設的負載均衡策略
指定權重
即在輪詢的基礎之上,增加權重的概念,weight
和訪問比率成正比,用於後端伺服器效能不均的情況,配置如下:
upstream tomcatserver {
server 192.168.0.1 weight=1;
server 192.168.0.2 weight=10;
}
IP Hash
每個請求按訪問ip的hash結果分配,這樣每個訪客固定訪問一個後端伺服器,可以解決session的問題,配置如下:
upstream tomcatserver {
ip_hash;
server 192.168.0.14:88;
server 192.168.0.15:80;
}
fair
第三方提供的負載均衡策略,按後端伺服器的響應時間來分配請求,響應時間短的優先分配,生產環境中有各種情況可能導致響應時間波動,需要慎用
upstream tomcatserver {
server server1;
server server2;
fair;
}
url_hash
第三方提供的負載均衡策略,按訪問url的hash結果來分配請求,使每個url定向到同一個後端伺服器
upstream tomcatserver {
server squid1:3128;
server squid2:3128;
hash $request_uri;
hash_method crc32;
}
Nginx的模組化設計
先來看看Nginx
模組架構圖:
這5個模組由上到下重要性一次遞減。
(1)核心模組;
核心模組是Nginx伺服器正常執行必不可少的模組,如同作業系統的核心。它提供了Nginx最基本的核心服務。像程序管理、許可權控制、錯誤日誌記錄等;
(2)標準HTTP模組;
標準HTTP模組支援標準的HTTP的功能,如:埠配置,網頁編碼設定,HTTP響應頭設定等;
(3)可選HTTP模組;
可選HTTP模組主要用於擴充套件標準的HTTP功能,讓Nginx能處理一些特殊的服務,如:解析GeoIP請求,SSL支援等;
(4)郵件服務模組;
郵件服務模組主要用於支援Nginx的郵件服務;
(5)第三方模組;
第三方模組是為了擴充套件Nginx伺服器應用,完成開發者想要的功能,如:Lua支援,JSON支援等;
模組化設計使得Nginx方便開發和擴充套件,功能很強大
Nginx的請求處理流程
基於上文中的Nginx
模組化結構,我們很容易想到,在請求的處理階段也會經歷諸多的過程,Nginx
將各功能模組組織成一條鏈,當有請求到達的時候,請求依次經過這條鏈上的部分或者全部模組,進行處理,每個模組實現特定的功能。
一個 HTTP Request 的處理過程:
- 初始化 HTTP Request
- 處理請求頭、處理請求體
- 如果有的話,呼叫與此請求(URL 或者 Location)關聯的 handler
- 依次呼叫各 phase handler 進行處理
- 輸出內容依次經過 filter 模組處理
Nginx的多程序模型
Nginx 在啟動後,會有一個 master
程序和多個 worker
程序。
master
程序主要用來管理worker
程序,包括接收來自外界的訊號,向各 worker 程序傳送訊號,監控 worker 程序的執行狀態以及啟動 worker 程序。
worker
程序是用來處理來自客戶端的請求事件。多個 worker 程序之間是對等的,它們同等競爭來自客戶端的請求,各程序互相獨立,一個請求只能在一個 worker 程序中處理。worker 程序的個數是可以設定的,一般會設定與機器 CPU 核數一致,這裡面的原因與事件處理模型有關
Nginx 的程序模型,可由下圖來表示:
這種設計帶來以下優點:
1) 利用多核系統的併發處理能力
現代作業系統已經支援多核 CPU 架構,這使得多個程序可以分別佔用不同的 CPU 核心來工作。Nginx 中所有的 worker 工作程序都是完全平等的。這提高了網路效能、降低了請求的時延。
2) 負載均衡
多個 worker 工作程序通過程序間通訊來實現負載均衡,即一個請求到來時更容易被分配到負載較輕的 worker 工作程序中處理。這也在一定程度上提高了網路效能、降低了請求的時延。
3) 管理程序會負責監控工作程序的狀態,並負責管理其行為
管理程序不會佔用多少系統資源,它只是用來啟動、停止、監控或使用其他行為來控制工作程序。首先,這提高了系統的可靠性,當 worker 程序出現問題時,管理程序可以啟動新的工作程序來避免系統性能的下降。其次,管理程序支援 Nginx 服務執行中的程序升級、配置項修改等操作,這種設計使得動態可擴充套件性、動態定製性較容易實現。
Nginx如何解決驚群現象
什麼是驚群現象?
驚群效應(thundering herd)是指多程序(多執行緒)在同時阻塞等待同一個事件的時候(休眠狀態),如果等待的這個事件發生,那麼他就會喚醒等待的所有程序(或者執行緒),但是最終卻只能有一個程序(執行緒)獲得這個時間的“控制權”,對該事件進行處理,而其他程序(執行緒)獲取“控制權”失敗,只能重新進入休眠狀態,這種現象和效能浪費就叫做驚群效應。
上文中介紹了Nginx的多程序模型,而典型的多程序模型正如文中所說,多個worker程序之間是對等的,因此當一個請求到來的時候,所有程序會同時開始競爭,最終執行的又只有一個,這樣勢必會造成資源的浪費。
Nginx解決該問題的思路是:不讓多個程序在同一時間監聽接受連線的socket,而是讓每個程序輪流監聽,這樣當有連線過來的時候,就只有一個程序在監聽那肯定就沒有驚群的問題。
具體做法是:利用一把程序間鎖,每個程序中都嘗試獲得這把鎖,如果獲取成功將監聽socket加入wait集合中,並設定超時等待連線到來,沒有獲得鎖的程序則將監聽socket從wait集合去除。
事件驅動模型和非同步非阻塞IO
承接上文,我們知道了Nginx的多程序模型後瞭解到,其工作程序實際上只有幾個,但為什麼依然能獲得如此高的併發效能,當然與其採用的事件驅動模型和非同步非阻塞IO的方式來處理請求有關。
Nginx伺服器響應和處理Web請求的過程,是基於事件驅動模型的,它包含事件收集器、事件傳送器和事件處理器等三部分基本單元,著重關注事件處理器
,而一般情況下事件處理器有這麼幾種辦法:
- 事件傳送器每傳遞過來一個請求,目標物件就建立一個新的程序
- 事件傳送器每傳遞過來一個請求,目標物件就建立一個新的執行緒,來進行處理
- 事件傳送器每傳遞過來一個請求,目標物件就將其放入一個待處理事件的列表,使用非阻塞I/O方式呼叫
第三種方式,在編寫程式程式碼時,邏輯比前面兩種都複雜。大多數網路伺服器採用了第三種方式,逐漸形成了所謂的事件驅動處理庫
。
事件驅動處理庫
又被稱為多路IO複用方法
,最常見的包括以下三種:select模型,poll模型和epoll模型。
其中Nginx就預設使用的是epoll
模型,同時也支援其他事件模型。
epoll
的幫助就在於其提供了一種機制,可以讓程序同時處理多個併發請求,不用關心IO呼叫的具體狀態。IO呼叫完全由事件驅動模型來管理,這樣一來,當某個工作程序接收到客戶端的請求以後,呼叫IO進行處理,如果不能立即得到結果,就去處理其他的請求;而工作程序在此期間也無需等待響應,可以去處理其他事情;當IO返回時,epoll
就會通知此工作程序;該程序得到通知後,會來繼續處理未完的請求
Nginx配置的最佳實踐
在生產環境或者開發環境中Nginx一般會代理多個虛擬主機,如果把所有的配置檔案寫在預設的nginx.conf
中,看起來會非常臃腫,因此建議將每一個虛擬檔案單獨放置一個資料夾,Nginx支援這樣的配置,如下:
http {
# 省略中間配置
# 引用該目錄下以 .conf 檔案結尾的配置
include /etc/nginx/conf.d/*.conf;
}
具體檔案配置如:
# Demo
upstream web_pro_testin {
server 10.42.46.70:6003 max_fails=3 fail_timeout=20s;
ip_hash;
}
server {
listen 80;
server_name web.pro.testin.cn;
location / {
proxy_pass http://web_pro_testin;
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location ~ ^/(WEB-INF)/ {
deny all;
}
}
Nginx全量配置引數說明
# 執行使用者
user www-data;
# 啟動程序,通常設定成和cpu的數量相等
worker_processes 6;
# 全域性錯誤日誌定義型別,[debug | info | notice | warn | error | crit]
error_log logs/error.log;
error_log logs/error.log notice;
error_log logs/error.log info;
# 程序pid檔案
pid /var/run/nginx.pid;
# 工作模式及連線數上限
events {
# 僅用於linux2.6以上核心,可以大大提高nginx的效能
use epoll;
# 單個後臺worker process程序的最大併發連結數
worker_connections 1024;
# 客戶端請求頭部的緩衝區大小
client_header_buffer_size 4k;
# keepalive 超時時間
keepalive_timeout 60;
# 告訴nginx收到一個新連線通知後接受盡可能多的連線
# multi_accept on;
}
#設定http伺服器,利用它的反向代理功能提供負載均衡支援
http {
# 副檔名與檔案型別對映表義
include /etc/nginx/mime.types;
# 預設檔案型別
default_type application/octet-stream;
# 預設編碼
charset utf-8;
# 伺服器名字的hash表大小
server_names_hash_bucket_size 128;
# 客戶端請求頭部的緩衝區大小
client_header_buffer_size 32k;
# 客戶請求頭緩衝大小
large_client_header_buffers 4 64k;
# 設定通過nginx上傳檔案的大小
client_max_body_size 8m;
# 開啟目錄列表訪問,合適下載伺服器,預設關閉。
autoindex on;
# sendfile 指令指定 nginx 是否呼叫 sendfile 函式(zero copy 方式)來輸出檔案,對於普通應用,
# 必須設為 on,如果用來進行下載等應用磁碟IO重負載應用,可設定為 off,以平衡磁碟與網路I/O處理速度
sendfile on;
# 此選項允許或禁止使用socke的TCP_CORK的選項,此選項僅在使用sendfile的時候使用
#tcp_nopush on;
# 連線超時時間(單秒為秒)
keepalive_timeout 65;
# gzip模組設定
gzip on; #開啟gzip壓縮輸出
gzip_min_length 1k; #最小壓縮檔案大小
gzip_buffers 4 16k; #壓縮緩衝區
gzip_http_version 1.0; #壓縮版本(預設1.1,前端如果是squid2.5請使用1.0)
gzip_comp_level 2; #壓縮等級
gzip_types text/plain application/x-javascript text/css application/xml;
gzip_vary on;
# 開啟限制IP連線數的時候需要使用
#limit_zone crawler $binary_remote_addr 10m;
# 指定虛擬主機的配置檔案,方便管理
include /etc/nginx/conf.d/*.conf;
# 負載均衡配置
upstream mysvr {
# 請見上文中的五種配置
}
# 虛擬主機的配置
server {
# 監聽埠
listen 80;
# 域名可以有多個,用空格隔開
server_name www.jd.com jd.com;
# 預設入口檔名稱
index index.html index.htm index.php;
root /data/www/jd;
# 圖片快取時間設定
location ~ .*.(gif|jpg|jpeg|png|bmp|swf)${
expires 10d;
}
#JS和CSS快取時間設定
location ~ .*.(js|css)?${
expires 1h;
}
# 日誌格式設定
#$remote_addr與$http_x_forwarded_for用以記錄客戶端的ip地址;
#$remote_user:用來記錄客戶端使用者名稱稱;
#$time_local: 用來記錄訪問時間與時區;
#$request: 用來記錄請求的url與http協議;
#$status: 用來記錄請求狀態;成功是200,
#$body_bytes_sent :記錄傳送給客戶端檔案主體內容大小;
#$http_referer:用來記錄從那個頁面連結訪問過來的;
log_format access '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" $http_x_forwarded_for';
# 定義本虛擬主機的訪問日誌
access_log /usr/local/nginx/logs/host.access.log main;
access_log /usr/local/nginx/logs/host.access.404.log log404;
# 對具體路由進行反向代理
location /connect-controller {
proxy_pass http://127.0.0.1:88;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
# 後端的Web伺服器可以通過X-Forwarded-For獲取使用者真實IP
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
# 允許客戶端請求的最大單檔案位元組數
client_max_body_size 10m;
# 緩衝區代理緩衝使用者端請求的最大位元組數,
client_body_buffer_size 128k;
# 表示使nginx阻止HTTP應答程式碼為400或者更高的應答。
proxy_intercept_errors on;
# nginx跟後端伺服器連線超時時間(代理連線超時)
proxy_connect_timeout 90;
# 後端伺服器資料回傳時間_就是在規定時間之內後端伺服器必須傳完所有的資料
proxy_send_timeout 90;
# 連線成功後,後端伺服器響應的超時時間
proxy_read_timeout 90;
# 設定代理伺服器(nginx)儲存使用者頭資訊的緩衝區大小
proxy_buffer_size 4k;
# 設定用於讀取應答的緩衝區數目和大小,預設情況也為分頁大小,根據作業系統的不同可能是4k或者8k
proxy_buffers 4 32k;
# 高負荷下緩衝大小(proxy_buffers*2)
proxy_busy_buffers_size 64k;
# 設定在寫入proxy_temp_path時資料的大小,預防一個工作程序在傳遞檔案時阻塞太長
# 設定快取資料夾大小,大於這個值,將從upstream伺服器傳
proxy_temp_file_write_size 64k;
}
# 動靜分離反向代理配置(多路由指向不同的服務端或介面)
location ~ .(jsp|jspx|do)?$ {
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_pass http://127.0.0.1:8080;
}
}
}
Nginx還能做什麼
解決CORS跨域問題
思路有兩個:
- 基於多路由,把跨域的兩個請求發到各自的伺服器,然後統一訪問入口即可避免該問題
- 利用Nginx配置Headerd的功能,為其附上相應的請求頭
適配 PC 或移動裝置
根據使用者裝置不同返回不同樣式的站點,以前經常使用的是純前端的自適應佈局,但無論是複雜性和易用性上面還是不如分開編寫的好,比如我們常見的淘寶、京東......這些大型網站就都沒有采用自適應,而是用分開製作的方式,根據使用者請求的 user-agent
來判斷是返回 PC 還是 H5 站點
請求限流
Nginx按請求速率限速模組使用的是漏桶演算法,即能夠強行保證請求的實時處理速度不會超過設定的閾值,如:
http {
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
server {
location /search/ {
limit_req zone=one burst=5 nodelay;
}
}
}
其他技巧
如:圖片防盜鏈,請求過濾,泛域名轉發,配置HTTPS等等
可參考文章:《Nginx 從入門到實踐,萬字詳解!》
常見問題
Q1:Nginx一般用作什麼?
見上文中Nginx在架構體系中的作用,配合Nginx還能做什麼作答即可
Q2:為什麼要用Nginx?
理解閘道器的必要性,以及Nginx保證高可用,負載均衡的能力
Q3:為什麼Nginx這麼快?
如果一個server採用一個程序負責一個request的方式,那麼程序數就是併發數。那麼顯而易見的,就是會有很多程序在等待中。等什麼?最多的應該是等待網路傳輸。
而nginx 的非同步非阻塞工作方式正是利用了這點等待的時間。在需要等待的時候,這些程序就空閒出來待命了。因此表現為少數幾個程序就解決了大量的併發問題。
nginx是如何利用的呢,簡單來說:同樣的4個程序,如果採用一個程序負責一個request的方式,那麼,同時進來4個request之後,每個程序就負責其中一個,直至會話關閉。期間,如果有第5個request進來了。就無法及時反應了,因為4個程序都沒幹完活呢,因此,一般有個排程程序,每當新進來了一個request,就新開個程序來處理。
nginx不這樣,每進來一個request,會有一個worker程序去處理。但不是全程的處理,處理到什麼程度呢?處理到可能發生阻塞的地方,比如向上遊(後端)伺服器轉發request,並等待請求返回。那麼,這個處理的worker不會這麼傻等著,他會在傳送完請求後,註冊一個事件:“如果upstream返回了,告訴我一聲,我再接著幹”。於是他就休息去了。此時,如果再有request 進來,他就可以很快再按這種方式處理。而一旦上游伺服器返回了,就會觸發這個事件,worker才會來接手,這個request才會接著往下走。
由於web server的工作性質決定了每個request的大部份生命都是在網路傳輸中,實際上花費在server機器上的時間片不多。這是幾個程序就解決高併發的祕密所在。
總結:事件模型,非同步非阻塞,多程序模型加上細節優化的共同作用
Q4:什麼是正向代理和反向代理
見上文
Q5:Nginx負載均衡的演算法有哪些?
見上文
Q6:Nginx如何解決的驚群現象?
見上文
Q7:Nginx為什麼不用多執行緒模型?
深入理解多程序模型加上非同步非阻塞IO的好處以及多執行緒模型中上下文切換的劣勢
Q8:Nginx壓縮功能有什麼壞處嗎?
非常耗費伺服器的CPU
Q9:Nginx有幾種程序模型?
實際上有兩種,多程序和單程序,但是實際工作中都是多程序的
參考
最後
如果你覺得這篇內容對你挺有幫助的話:
- 當然要點贊支援一下啦~
- 搜尋並關注公眾號「是Kerwin啊」,一起嘮嘮嗑~
- 再來看看最近幾篇的「查漏補缺」系列吧,該系列會持續輸出~