nginx經常掛在嘴邊的就是反向代理,不過他還可以干很多事,我所了解的只是反向代理、靜態文件緩存、靜態資源服務器,對于負載均衡只是略有涉及。
Nginx (“engine x”) 是一個高性能的 HTTP 和 反向代理 服務器 ,也是一個 IMAP/POP3/SMTP 代理 服務器 。 Nginx 是由 Igor Sysoev 為俄羅斯訪問量第二的 Rambler.ru 站點開發的,第一個公開版本0.1.0發布于2004年10月4日。其將源代碼以類BSD許可證的形式發布,因它的穩定性、豐富的功能集、示例配置文件和低系統資源的消耗而聞名
引用一下菜鳥教程的簡介:Nginx功能豐富,可作為HTTP服務器,也可作為反向代理服務器,郵件服務器。支持FastCGI、SSL、Virtual Host、URL Rewrite、Gzip等功能。并且支持很多第三方的模塊擴展。Nginx的穩定性、功能集、示例配置文件和低系統資源的消耗讓他后來居上,在全球活躍的網站中有12.18%的使用比率,大約為2220萬個網站。
特點
(1):代理服務器,快速高效反向代理,提升網站性能。
(2):負載均衡器,內部支持Rails和PHP,也可支持HTTP代理服務器,對外進行服務。同時支持簡單容錯和利用算法進行負載均衡。
(3):性能方面,Nginx專門為性能設計,實現注重效率。采用Poll模型,可以支持更多的并發連接,并在大并發時占用很低內存。
(4):穩定性方面,采用分階段資源分配技術,使CPU資源占用率低。
(5):高可用性方面,支持熱備,啟動迅速。
nginx安裝
mac 下安裝
brew install nginx
安裝目錄為 /usr/local/Cellar/nginx/1.17.2/
配置文件目錄為 /usr/local/etc/nginx/nginx.conf
服務器默認路徑 /usr/local/var/www
常用命令
mac 下的啟動命令
啟動
nginx
快速停止關閉
nignx -s stop
優雅的關閉
nginx -s quit
承載配置文件
nginx -s reload
查看nginx進程
ps -ef | grep nginx
查看配置文件是否正確
nginx -t
優雅的殺死nginx進程
kill -quit 進程號
快速的殺死nginx進程
kill -term 進程號
nginx配置
nginx 文件的默認配置文件位置 /usr/local/etc/nginx/nginx.conf
打開 /usr/local/etc/nginx/
目錄可以看到,里面有很多的配置文件,啟動有一個nginx.conf
和 nginx.conf.default
兩個配置文件,剛開始安裝的時候,兩個文件的內容是一樣的,所以我們可以肆意的修改nginx.conf
搞崩的話就直接把nginx.conf.default
中的內容復制過來就行了又是一個新的nginx。
配置文件架構
// nginx全局塊
...
// events塊
events {
...
}
// http 塊
http {
// http全局塊
...
// server塊
server {
...
}
// http全局塊
...
}
配置文件加注釋說明
# 配置nginx的用戶組 默認為nobody
#user nobody;
# 配置nginx的主線程數量 nginx是一個主線程下面多個子線程
worker_processes 1;
# 配置nginx的錯誤日志 格式為 log路徑 log級別
# error_log 的日志級別為: debug info notice warn error crit alert emerg 緊急由低到高
# error_log的默認日志級別為error,那么就只有緊急程度大于等于error的才會記錄在日志
# error_log 的作用域為 main http mail stream server location
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
# 指定nginx進程運行文件存放地址
#pid logs/nginx.pid;
events {
# poll是多路復用IO中的一種方式,但是僅用于linux2.6以上內核,可以大大提高nginx的性能
# use poll
# 設置網絡的連接序列化 防止驚群現象發生 默認為 on
# accept_mutex on;
# 設置一個進程是否同時接受多個網絡連接 默認為 off
# multi_accept off
# 最大連接數 默認為 512
worker_connections 1024;
}
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"';
// 允許通過日志配置
#access_log logs/access.log main;
# sendfile 指定使用 sendfile 系統調用來傳輸文件。優點在于在兩個文件描述符之間傳遞數據(完全在內核中操作),從而避免了數據在內核緩沖區和用戶緩沖區之間的拷貝,效率高,稱之為零拷貝,這個東西有點講究,自行百度
# sendfile 作用域 location server http
sendfile on;
#tcp_nopush on;
# 鏈接超時時間 默認 75s 作用域 http server location
#keepalive_timeout 0;
keepalive_timeout 65;
# 開始gzip壓縮
#gzip on;
server {
# 端口號
listen 8080;
# 域名或ip
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
# 對請求的路由進行過濾 正則匹配
location / {
root html;
index index.html index.htm;
}
...
}
include servers/*;
}
nginx 日志
nginx的日志大致分為 access_log
和 error_log
。error_log 記錄的是nginx的錯誤日志。(以下對日志的理解不是很全面,還只是基礎的)
error_log
- 記錄nginx錯誤日志
- 作用域為
main http mail stream server location
- 日志級別
debug info notice warn error crit alert emerg
- 日志級別默認為 error 當級別高于或等于指定級別時才會記錄
access_log
- 記錄請求通過的日志
- 作用域為
http server location limit_except
- 日志格式默認為
combined
- 日志格式是可以自定義的
# 定義一個為 main 的日志格式
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 logs/access.log main;
上方的 log_format
后面類似 $remote_addr
是nginx的內置變量,取值如下
$remote_addr, $http_x_forwarded_for(反向) 記錄客戶端IP地址
$remote_user 記錄客戶端用戶名稱
$request 記錄請求的URL和HTTP協議
$status 記錄請求狀態
$body_bytes_sent 發送給客戶端的字節數,不包括響應頭的大小; 該變量與Apache模塊mod_log_config里的“%B”參數兼容。
$bytes_sent 發送給客戶端的總字節數。
$connection 連接的序列號。
$connection_requests 當前通過一個連接獲得的請求數量。
$msec 日志寫入時間。單位為秒,精度是毫秒。
$pipe 如果請求是通過HTTP流水線(pipelined)發送,pipe值為“p”,否則為“.”。
$http_referer 記錄從哪個頁面鏈接訪問過來的
$http_user_agent 記錄客戶端瀏覽器相關信息
$request_length 請求的長度(包括請求行,請求頭和請求正文)。
$request_time 請求處理時間,單位為秒,精度毫秒; 從讀入客戶端的第一個字節開始,直到把最后一個字符發送給客戶端后進行日志寫入為止。
$time_iso8601 ISO8601標準格式下的本地時間。
$time_local 通用日志格式下的本地時間。
反向代理
正向代理 反向代理
- 正向代理大概的意思就是,客戶端發送一個請求,這個請求包含服務器地址,那么代理服務器收到了請求后會將請求發送到客戶端指定的服務器,并將響應內容傳遞給客戶端,在這個過程中,客戶端是知道請求的服務器地址的,但是服務器是不知道哪個客戶端請求的。VPN做的就是這個事。
- 反向代理大概的意思就是,客戶端發送一個請求給代理服務器,由代理服務器來決定這個請求該交給哪個服務器,這就是實現了服務器負載均衡,可以將請求轉發到比較空閑的服務器來響應,這個時候,代理服務器就是相對于客戶端的服務器,因為此時客戶端也不知道請求交給了哪個服務器。
我所理解的正向代理和反向代理就是這個意思,如有錯誤歡迎下方評論。
proxy_pass
那么nginx使用的就是proxy_pass
屬性來進行反向代理的處理,使用也是很簡單。下面以nodejs開啟一個建立在 localhost:4000 的服務
const http = require('http');
http.createServer(function(req, res){
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('server 4000');
}).listen(4000);
請求 localhost:4000
可以打開我們的頁面
那么我們需要做的是將 localhost:5000
的所有請求都代理到4000這個server里,這樣就會出現我們訪問5000和訪問4000一樣的效果。具體配置如下
server {
# 監聽 localhost:5000
listen 5000;
server_name localhost;
# / 表示匹配所有的請求,所有的請求都會經過這個過濾器
location / {
# 設定請求轉發的地址
proxy_pass http://localhost:4000;
}
}
這邊需要注意的是 proxy_pass
的寫法,必須是http://
或者https://
開頭的,http頭是不能省的。
請求5000端口效果如下:
本地代理至百度
上方的例子過于簡單,那么這一個和上面的有點類似,這次是將4000的端口號代理到www.baidu.com
。修改一下proxy_pass
server {
# 監聽 localhost:5000
listen 5000;
server_name localhost;
# / 表示匹配所有的請求,所有的請求都會經過這個過濾器
location / {
# 設定請求轉發的地址
proxy_pass https://www.baidu.com;
}
}
這樣就可以了,至于這邊寫的是http
還是 https
,這個倒是不影響,因為百度內部會自動將http
轉成https
畢竟安全嘛。
百度代理至本地
那么按照剛剛的思路就是監聽 www.baidu.com
然后設置一下 proxy_pass
為 localhost:4000
配置如下
server {
listen 80;
server_name www.baidu.com;
location / {
proxy_pass http://localhost:4000;
}
}
試一下,是不是沒有用,沒有用就對了。要是這么輕松的搞定nginx
還玩個蛋。那么這個里面又有點操作了,先看正確的配置,修改本地的hosts文件,mac下的文件位置為 /etc/hosts
但是需要 sudo 來進行修改,畢竟這個文件比較重要嘛
sudo vim /etc/hosts
添加一句
server {
listen 80;
server_name baidu.com;
location / {
proxy_pass http://127.0.0.1:4000;
}
}
一般在本地開發的時候我們都會修改本地的hosts
文件,但是會遇到一個問題就是有的時候是有用的有的時候又沒用了,我這邊的解決辦法是,每次修改完hosts
文件就清楚瀏覽器的瀏覽數據,尤其是緩存這一塊的東西。
如果遇到nginx配置完全正確hosts
文件也配置了,但是還是沒有用,不妨清一下緩存,至少在我這是每次都是清完緩存才有用的。
nginx跨域
跨域的解決辦法就是在header
里面加上允許跨域的源等信息
location / {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
if ($request_method = 'OPTIONS') {
return 204;
}
}
但是在實際項目里。origin
還是不要設置為*
比較好,因為前端使用axios的話在獲取session這一塊會出現問題。
nginx緩存
這邊有一個需要注意的地方,nginx作為靜態資源服務器的時候是不做緩存的,只有當nginx進行反向代理的時候才具備緩存這個功能。我一開始寫了半天發現鳥用都沒有,最后才發現只有做代理的時候才具備緩存。
各大瀏覽器本身已經具有緩存了,比如說谷歌,我們可以寫一個html,然后在html引入一張圖片,我們可以看看瀏覽器是怎么對圖片這些靜態資源進行緩存的。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<img src="./static/test.jpg" alt="">
</body>
</html>
這個html代碼我使用http-server
部署在了http-server -a 127.0.0.1 -p 4000
可以看出是在127.0.0.1 4000
端口。
這是首次加載的時候的狀況
刷新一下
那么區別還是很大的
-
html
文件的狀態碼從200到304,304狀態碼表明該文件是從緩存中讀取 -
jpg
文件可以發現在size
這一列,多了一個memory cache
,這表明這個圖片是從瀏覽器緩存中讀取的 - 各個文件的加載事件明顯減少,尤其是圖片
可能有的人會發現第一次加載的時候,顯示的是from desk cache
,那么chrome瀏覽器的緩存分為兩種,一種是磁盤緩存一種是內存緩存,
官方文檔:
Chrome employs two caches — an on-disk cache and a very fast in-memory cache. The lifetime of an in-memory cache is attached to the lifetime of a render process, which roughly corresponds to a tab. Requests that are answered from the in-memory cache are invisible to the web request API. If a request handler changes its behavior (for example, the behavior according to which requests are blocked), a simple page refresh might not respect this changed behavior. To make sure the behavior change goes through, call handlerBehaviorChanged() to flush the in-memory cache. But don't do it often; flushing the cache is a very expensive operation. You don't need to call handlerBehaviorChanged() after registering or unregistering an event listener.
大概意思就是:chrome有兩種緩存,一種是desk cache
一種是memory cache
,然后memory cache
的效率高于前者。
那么打開chrome devtools點開圖片可以發現
在cache-control
后面有一個max-age
,那么具體的有關緩存的技術這邊就不說了我回頭整理一下
那么針對不同類型的文件進行緩存還是很簡單的,需要注意的在于location
的正則匹配
那么第一種最簡單的緩存,就是直接設置expires 緩存時間
設置expires
location ~ .*\.(jpg|png)$ {
proxy_pass http://127.0.0.1:4000;
expires 3m;
}
expires
是以秒為單位的,那么我們設置為3m
也就是180秒,發現確實是可以的。
proxy_cache_path 的使用
那么我們也可以指定我們的nginx緩存目錄,通過proxy_cache_path 屬性
proxy_cache_path /tmp/cache/test levels=1:2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off;
location ~ .*\.(jpg|png)$ {
proxy_pass http://127.0.0.1:4000;
proxy_cache my_cache;
proxy_cache_valid 200 304 1y;
proxy_cache_valid any 1m;
expires 1y;
}
- proxy_cache_path 執行緩存文件的目錄,如果沒有的話需要提前創建,不然nginx會報錯
- levels 采用2級目錄來存儲
- key_zone 在共享內存中設置一塊存儲區域來存放緩存的key和metadata(類似使用次數),這樣nginx可以快速判斷一個request是否命中或者未命中緩存,1m可以存儲8000個key,10m可以存儲80000個key
- max_size 最大cache空間,如果不指定,會使用掉所有disk space,當達到配額后,會刪除最少使用的cache文件
- inactive 未被訪問文件在緩存中保留時間,在指定時間內未被訪問的文件會被刪除
- use_temp_path 如果為off,則nginx會將緩存文件直接寫入指定的cache文件中,而不是使用temp_path存儲,official建議為off,避免文件在不同文件系統中不必要的拷貝;
- proxy_cache 啟用proxy cache,對應著配置的key_zone;
- proxy_cache_valid 根據不同的狀態碼設置不同的緩存時間
可以查看一下nginx
進程,會發現這個時候是有緩存的進程在開著的。
這邊可以看到,我們的圖片的緩存時間已經被設置為1年
location匹配優先級
在緩存中需要注意的一點就是location的匹配規則和優先級
- = 開頭表示精確匹配
- ^~ 開頭表示uri以某個常規字符串開頭,不是正則匹配;
- ~ 開頭表示區分大小寫的正則匹配;
- ~* 開頭表示不區分大小寫的正則匹配;
- / 通用匹配, 如果沒有其它匹配,任何請求都會匹配到;
upstream負載均衡
負載均衡是nginx的另一大特點,可以配置多個服務器,將請求分發到最合適的那臺服務器,避免某一臺服務器請求太多而崩潰。使用upstream
屬性來配置
upstream favtomcat {
server 192.168.1.100:4000;
server 192.168.1.111:5000;
}
location / {
root html;
index index.html index.htm;
# 對應上方的 favtomcat
proxy_pass http://favtomcat;
}
這是最基礎的負載均衡配置,采用的是輪詢
的策略進行負載,每個請求按時間順序逐一分配到不同的后端服務器,適用于圖片服務器集群和純靜態頁面服務器集群。
優點: 方式簡便、成本低廉
缺點: 可靠性低和負載分配不均衡
那么upstream
還有其他的負載策略
weight權重
可以給每一臺服務器設置一個權重,這樣權重高的干的活也就會多一點
upstream favtomcat {
server 192.168.1.100:4000 weight=5;
server 192.168.1.111:5000 weight=10;
}
ip_hash
這種方式是基于客戶端的ip地址,采用hash算法計算下一個請求要選擇哪一個服務器,這樣固定的ip會訪問同一個服務器,可以解決session問題
upstream favtomcat {
ip_hash;
server 192.168.1.100:4000;
server 192.168.1.111:5000;
}
least_conn最少鏈接
會將下一個請求分發到當前鏈接數最少的一臺服務器
upstream favtomcat {
least_conn;
server 192.168.1.100:4000;
server 192.168.1.111:5000;
}
fair
按后端服務器的響應時間來分配請求,響應時間短的優先分配。
upstream favtomcat {
fair;
server 192.168.1.100:4000;
server 192.168.1.111:5000;
}
url_hash
按訪問url的hash結果來分配請求,使每個url定向到同一個后端服務器
upstream backserver {
server squid1:3128;
server squid2:3128;
hash $request_uri;
hash_method crc32;
}
參考
https://juejin.im/post/59f94f626fb9a045023af34c;
Nginx Proxy Cache原理和最佳實踐