文章目錄
一、Nginx相關問題
1.1 Nginx是如何處理一個請求的呢?
(1)nginx在啓動時,會解析配置文件,得到需要監聽的端口與ip地址;然後在nginx的master進程裏面,先初始化好這個監控的socket,再進行listen;然後再fork出多個子進程出來, 子進程會競爭accept新的連接。
(2)此時,客戶端就可以向nginx發起連接了。當客戶端與nginx進行三次握手,與nginx建立好一個連接後;
(3)此時,某一個子進程會accept成功,然後Nginx創建對連接的封裝,即ngx_connection_t結構體。接着,根據事件調用相應的事件處理模塊,如http模塊與客戶端進行數據的交換。
(4)最後,nginx或客戶端來主動關掉連接,到此,一個連接就完成了。
1.2 Nginx爲什麼不使用多線程?爲什麼性能這麼高?nginx是如何實現高併發的?
(1)例如Apache服務器的工作原理,創建多個進程或線程,而每個進程或線程都會爲其分配cpu和內存,但是併發過大會榨乾服務器資源。由於web server的工作性質決定了每個request的大部份生命都是在網絡傳輸中,實際上花費在server機器上的時間片不多。這是幾個進程就解決高併發的祕密所在。即webserver剛好屬於網絡io密集型應用,不算是計算密集型。
(2)所以Nginx,採用單線程異步非阻塞處理請求,運用epoll模型,提供了一個隊列,排隊解決,不會爲每個請求分配cpu和內存資源,節省了大量資源,同時也減少了大量的CPU的上下文切換。所以才使得Nginx支持更高的併發。
(3)Nginx會有一個master主進程,一個master對應多個worker工作進程,每個worker工作進程可以處理多個請求,每進來一個request,會有一個worker進程去處理。但不是全程的處理,處理到可能發生阻塞的地方,比如向後端服務器轉發request,並等待請求返回。那麼,這個處理的worker繼續處理其他請求,而一旦後端服務器返回了,就會觸發這個事件,worker纔會來接手,這個request纔會接着往下走。
二、Nginx的編譯安裝及調優
2.1 編譯安裝
1、 解壓:
1)解壓tar包: tar zxf nginx-1.15.7.tar.gz
2)隱藏nginx版本,防止被攻擊:vim nginx-1.15.7/src/core/nginx.h
nginx-1.15.9目錄裏的內容
1)auto目錄:裏面有4個子目錄,cc是編譯使用的,os是判斷操作系統類型的,其他都是輔助configure編譯的,也就是操作系統有什麼特性供nginx使用
2)CHANGES:各版本的改變,bug修復等信息;CHANGES.ru:nginx作者是俄羅斯人,這是俄羅斯版本
3)conf: 爲了方便運維配置,conf目錄裏有示例文件,安裝好後會拷貝到安裝目錄
4)configure
5)contrib:提供nginx語法檢測字體
cp -r contrib/vim/* ~/.vim ##家目錄下如果沒有.vim目錄,手動新建
6)html:默認發佈目錄,50x.html是報500錯誤時的頁面
7) src:源碼目錄
修改文件 /nginx-1.15.9/src/core/nginx.h
#define NGINX_VER "nginx/" NGINX_VERSION
改爲:
#define NGINX_VER "nginx/"
3) 在Nginx源碼/auto/cc/gcc文件中: vim auto/cc/gcc
- 在此Debug模式模式會插入許多追蹤和ASSERT之類的信息,在正常編譯過程中結束,會產生幾兆大小的包,我們可以在編譯之前關閉debug模式,這樣在編譯結束,只會產生幾百K左右的包大小。
# debug
#CFLAGS="$CFLAGS -g" ##本行註釋掉,關閉debug日誌模式,
2、編譯安裝nginx
1)安裝gcc編譯器和openssl-devel: yum install gcc openssl-devel -y
2)在 /root/nginx/nginx-1.15.9 目錄下編譯:
./configure \
###自定義配置:
--prefix=/usr/local/nginx \
--pid-path=/var/run/nginx/nginx.pid \
--lock-path=/var/lock/nginx.lock \
--error-log-path=/var/log/nginx/error.log \
--http-log-path=/var/log/nginx/access.log \
--with-http_gzip_static_module \
--http-client-body-temp-path=/var/temp/nginx/client \
--http-proxy-temp-path=/var/temp/nginx/proxy \
--http-fastcgi-temp-path=/var/temp/nginx/fastcgi \
--http-uwsgi-temp-path=/var/temp/nginx/uwsgi \
--http-scgi-temp-path=/var/temp/nginx/scgi
--prefix=PATH
## 指定nginx的安裝目錄。默認 /usr/local/nginx
--with-http_realip_module
##通過這個模塊允許我們改變客戶端請求頭中客戶端IP地址值(例如X-Real-IP 或 X-Forwarded-For)
##意義在於能夠使得後臺服務器記錄原始客戶端的IP地址
--with-http_ssl_module
##使用https協議模塊。默認情況下,該模塊沒有被構建。前提是openssl與openssl-devel已安裝
--without-http_rewrite_module
##perl正則表達式使用在location指令和ngx_http_rewrite_module模塊中。
發現報錯:
原因:缺少依賴性:gd-devel-2.0.35-26.el7.x86_64.rpm(缺什麼裝什麼)
3) 安裝:yum install gd-devel-2.0.35-26.el7.x86_64.rpm,再次編譯。
4)make && make install 安裝
3、啓動腳本做軟鏈接: ln -s /usr/local/nginx/sbin/nginx /usr/local/sbin/
nginx -t ##檢測配置文件語法
nginx ##啓動nginx
4、修改配置
1)查看nginx的默認配置文件:vim //usr/local/nginx/conf/nginx.conf
2)修改用戶,讓nginx以nginx用戶和用戶組啓動。
3)檢測配置文件語法 :nginx -t 修改了nginx用戶和組後檢測會報錯,需要手動創建nginx用戶
4)添加nginx用戶:useradd nginx
,此時就不報錯了。
useradd -s /sbin/nologin -M -d /usr/local/nginx/ nginx
創建用戶
再次加載nginx的配置可能會出現報錯:
nginx: [error] invalid PID number "" in "/usr/local/nginx/logs/nginx.pid"
#解決:刪除/usr/local/nginx/logs/nginx.pid,再重新啓動
5)此時 ps aux,可看到兩個nginx進程,一個是root用戶開啓的master進程,一個是由nginx用戶開啓的worker進程
2.2調優
5、升級nginx的配置,調優
開啓的work進程數是由cpu的總核心數決定的(有幾核就有幾個work進程)
1) 修改虛擬機cpu核心數(虛擬機關機)
2)查看邏輯CPU的個數:cat /proc/cpuinfo | grep “processor” | wc -l 發現是4核的
3)修改nginx開啓的work進程數:vim //usr/local/nginx/conf/nginx.conf,並且重置nginx nginx -s reload
user nginx nginx;
worker_processes 4;
worker_cpu_affinity 0001 0010 0100 1000;
- nginx默認沒有開啓利用多核cpu,這樣配置後,在計算最費時的資源時,使用的cpu核數越多,越快,'0001’從左到右一次表示第四,第三,第二,第一個cpu核心,0表示關閉,1表示開啓
此時可以看到開啓了4個work進程。
4)修改併發連接數:vim //usr/local/nginx/conf/nginx.conf,並且重置nginx nginx -s reload
worker_connections 65535 :
單個後臺worker process進程的 最大併發鏈接數 ,同時也受linux系統進程的最大打開文件數的限制,需要修改’ulimit -n 65536’設置後,方可生效。epoll
是多路複用IO(I/O Multiplexing)中的一種方式 ,但是僅用於linux2.6以上內核,可以大大提高nginx的性能 ,Nginx正是由此IO模型支持高併發。Nginx支持select,poll,epoll,kqueue,resig,/dev/poll.kqueue對應BSD系統的高效IO模型,epoll對應Linux系統的高效模型。I/O多路複用
:如果一個I/O流進來,我們就開啓一個進程處理這個I/O流。那麼假設現在有一百萬個I/O流進來,那我們就需要開啓一百萬個進程一一對應處理這些I/O流(——這就是傳統意義下的多進程併發處理)。思考一下,一百萬個進程,你的CPU佔有率會多高,這個實現方式及其的不合理。所以人們提出了I/O多路複用這個模型,一個線程,通過記錄I/O流的狀態來同時管理多個I/O,可以提高服務器的吞吐能力
ps aux 查看進程,cat /proc/2212/limits
如果不是我們期望的65535,
ulimit -a #查看系統的最大打開文件數
ulimit -n 65535 #更改系統配置,立即生效
nginx -s reload
,然後重新查看。
三、Nginx主配置文件結構
nginx配置文件主要分爲六個區域:
main(全局設置)
、events(nginx工作模式)
、http(http設置)
、sever(主機設置)
、location(URL匹配)
、upstream(負載均衡服務器設置)
。
3.1 main模塊
下面時一個main區域,他是一個全局的設置:
user nobody nobody;
worker_processes 1;
error_log /usr/local/var/log/nginx/error.log notice;
pid /usr/local/var/run/nginx/nginx.pid;
worker_rlimit_nofile 1024;
user
來指定Nginx Worker進程運行用戶以及用戶組,默認由nobody賬號運行。但我們一般更改爲用戶nginx運行,需要在nginx主機創建用戶nginx。
worker_processes
來指定了Nginx要開啓的子進程數。每個Nginx進程平均耗費10M~12M內存。根據經驗,一般指定1個進程就足夠了,如果是多核CPU,建議指定和CPU的數量一樣的進程數即可。
error_log
用來定義全局錯誤日誌文件。日誌輸出級別有debug、info、notice、warn、error、crit可供選擇,其中,debug輸出日誌最爲最詳細,而crit輸出日誌最少。
pid
用來指定進程id的存儲文件位置。
worker_rlimit_nofile
用於指定一個nginx進程可以打開的最多文件描述符數目,這裏是65535,需要使用命令“ulimit -n 65535
”來設置。
3.2 events模塊
events模塊來用指定nginx的工作模式和工作模式及連接數上限,一般是這樣:
events {
use kqueue; #mac平臺
worker_connections 1024;
}
use
用來指定Nginx的工作模式。Nginx支持的工作模式有select、poll、kqueue、epoll、rtsig和/dev/poll。其中select和poll都是標準的工作模式,kqueue和epoll是高效的工作模式,不同的是epoll用在Linux平臺上,而kqueue用在BSD系統中,因爲Mac基於BSD,所以Mac也得用這個模式,對於Linux系統,epoll工作模式是首選。
worker_connections
用於定義Nginx每個進程的最大連接數,即接收前端的最大請求數,默認是1024。最大客戶端連接數由worker_processes和worker_connections決定,即Max_clients=worker_processes*worker_connections,在作爲反向代理時,Max_clients變爲:Max_clients = worker_processes * worker_connections/4。
進程的最大連接數受Linux系統進程的最大打開文件數限制,在執行操作系統命令“ulimit -n 65536”後worker_connections的設置才能生效。
3.3 http 模塊
http模塊是最核心的模塊了,它負責HTTP服務器相關屬性的配置,它裏面的server和upstream子模塊,至關重要,等到反向代理和負載均衡以及虛擬目錄等會仔細說。
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 /usr/local/var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 10;
#gzip on;
upstream myproject {
.....
}
server {
....
}
}
include
用來設定文件的mime類型,類型在配置文件目錄下的mime.type文件定義,來告訴nginx來識別文件類型。
default_type
設定了默認的類型爲二進制流,也就是當文件類型未定義時使用這種方式,例如在沒有配置asp 的locate 環境時,Nginx是不予解析的,此時,用瀏覽器訪問asp文件就會出現下載了。
log_format
用於設置日誌的格式,和記錄哪些參數,這裏設置爲main,剛好用於access_log來記錄這種類型。
main
的類型日誌如下:也可以增刪部分參數。
127.0.0.1 - - [21/Apr/2015:18:09:54 +0800] "GET /index.php HTTP/1.1" 200 87151 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.76 Safari/537.36"
access_log
用來紀錄每次的訪問日誌的文件地址,後面的main是日誌的格式樣式,對應於log_format的main。
sendfile
參數用於開啓高效文件傳輸模式。將tcp_nopush和tcp_nodelay兩個指令設置爲on用於防止網絡阻塞。
keepalive_timeout
設置客戶端連接保持活動的超時時間。在超過這個時間之後,服務器會關閉該連接。
3.4 server 模塊
sever 模塊是http的子模塊,它用來定一個虛擬主機。
server {
listen 8080;
server_name localhost 192.168.12.10 www.yangyi.com;
# 全局定義,如果都是這一個目錄,這樣定義最簡單。
root /Users/yangyi/www;
index index.php index.html index.htm;
charset utf-8;
access_log usr/local/var/log/host.access.log main;
aerror_log usr/local/var/log/host.error.log error;
....
}
server
標誌定義虛擬主機開始。
listen
用於指定虛擬主機的服務端口。
server_name
用來指定IP地址或者域名,多個域名之間用空格分開。
root
表示在這整個server虛擬主機內,全部的root web根目錄。注意要和locate {}下面定義的區分開來。
index
全局定義訪問的默認首頁地址。注意要和locate {}下面定義的區分開來。
charset
用於設置網頁的默認編碼格式。
access_log
用來指定此虛擬主機的訪問日誌存放路徑,最後的main用於指定訪問日誌的輸出格式。
3.5 location模塊
location模塊是nginx中用的最多的,也是最重要的模塊了,負載均衡、反向代理、虛擬域名都與它相關。
location 根據它字面意思就知道是來定位的,定位URL,解析URL,所以,它也提供了強大的正則匹配功能,也支持條件判斷匹配,用戶可以通過location指令實現Nginx對動、靜態網頁進行過濾處理。像我們的php環境搭建就是用到了它。
我們先來設定默認首頁和虛擬機目錄。
location / {
root /Users/yangyi/www;
index index.php index.html index.htm;
}
location /
表示匹配訪問根目錄。
root
指令用於指定訪問根目錄時,虛擬主機的web目錄,這個目錄可以是相對路徑(相對路徑是相對於nginx的安裝目錄)。也可以是絕對路徑。
index
用於設定我們只輸入域名後訪問的默認首頁地址,有個先後順序:index.php index.html index.htm,如果沒有開啓目錄瀏覽權限,又找不到這些默認首頁,就會報403錯誤。
location
還有一種方式就是正則匹配,開啓正則匹配這樣:location ~
。後面加個~。下面就是:
location ~ \.php$ {
root /Users/yangyi/www;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
include fastcgi.conf;
}
\.php$
熟悉正則的我們直到,這是匹配.php
結尾的URL,用來解析php文件。裏面的root
也是一樣,用來表示虛擬主機的根目錄。
fast_pass
鏈接的是php-fpm
的地址。
3.6 upstream 模塊
upstream 模塊負責負載均衡模塊,通過一個簡單的調度算法來實現客戶端IP到後端服務器的負載均衡。
upstream iyangyi.com{
ip_hash;
server 192.168.12.1:80;
server 192.168.12.2:80 down;
server 192.168.12.3:8080 max_fails=3 fail_timeout=20s;
server 192.168.12.4:8080;
}
在上面的例子中,通過upstream
指令指定了一個負載均衡器的名稱iyangyi.com
。這個名稱可以任意指定,在後面需要的地方直接調用即可。
裏面是ip_hash
這是其中的一種負載均衡調度算法,下面會着重介紹。緊接着就是各種服務器了。用server關鍵字表識,後面接ip。
Nginx的負載均衡模塊目前支持4種調度算法:
- weight輪詢(默認)。每個請求按時間順序逐一分配到不同的後端服務器,如果後端某臺服務器宕機,故障系統被自動剔除,使用戶訪問不受影響。weight。指定輪詢權值,weight值越大,分配到的訪問機率越高,主要用於後端每個服務器性能不均的情況下。
- ip_hash。每個請求按訪問IP的hash結果分配,這樣來自同一個IP的訪客固定訪問一個後端服務器,有效解決了動態網頁存在的session共享問題。
- fair。比上面兩個更加智能的負載均衡算法。此種算法可以依據頁面大小和加載時間長短智能地進行負載均衡,也就是根據後端服務器的響應時間來分配請求,響應時間短的優先分配。Nginx本身是不支持fair的,如果需要使用這種調度算法,必須下載Nginx的upstream_fair模塊。
- url_hash。按訪問url的hash結果來分配請求,使每個url定向到同一個後端服務器,可以進一步提高後端緩存服務器的效率。Nginx本身是不支持url_hash的,如果需要使用這種調度算法,必須安裝Nginx的hash軟件包。
在HTTP Upstream模塊中,可以通過server指令指定後端服務器的IP地址和端口,同時還可以**設定每個後端服務器在負載均衡調度中的狀態**。常用的狀態有:
- down,表示當前的server暫時不參與負載均衡。
- backup,預留的備份機器。當其他所有的非backup機器出現故障或者忙的時候,纔會請求backup機器,因此這臺機器的壓力最輕。
- max_fails,允許請求失敗的次數,默認爲1。當超過最大次數時,返回proxy_next_upstream 模塊定義的錯誤。
- fail_timeout,在經歷了max_fails次失敗後,暫停服務的時間。max_fails可以和fail_timeout一起使用。
注意 :當負載調度算法爲ip_hash
時,後端服務器在負載均衡調度中的狀態不能是weight和backup。