Nginx高性能Web服務器詳解(二)
在上一節中我們安裝了nginx後,每次運行nginx都要到 /usr/local/nginx/sbin/nginx 下執行nginx,這樣顯然比較麻煩,所以我們可以製作軟連接來解決此問題
1.nginx的常用命令
選項 說明
不加選項 啓動nginx服務
-h或-? 查看inginx命令幫助
-v 查看nginx版本信息
-V 查看nginx編譯信息
-c 啓動nginx時指定主配置文件
-t 測試nginx.conf文件是否存在語法錯誤
-s 向正在運行的nginx主進程發送信號,信號的可用值有stop, quit, reopen, reload
具體說明:
nginx -h nginx -?
如果你想要看看nginx命令都有哪些可用的選項,則可以使用"-h選項"或者
-t選項或者-T
使用"-t"選項或者"-T"選項可以測試nginx.conf配t文件中是否存在語法錯誤
nginx.conf中的配置指令需要以分號結尾,如果沒有以分號結尾,會在啓動nignx時報語法錯誤。其實,在啓動nginx服務之前,我們就可以使用nginx -t命令對nginx.conf文件進行語法檢查,如果配文件中存在語法錯誤,那麼相應的錯誤信息會輸出
-s選項
-s選項的作用就是向正在運行的nginx進程發送信號
比如,當nginx服務已經啓動,我們想要停止nginx服務,則可以使用nginx -s stop命令停止nginx服務,“nginx -s stop"命令表示向nginx進程發送stop信號,使用”-s"選項除了能夠發送stop信號,還能發送quit信號、reopen信號以及reload信號
quit信號
與stop信號的作用類似,quit信號作用也是用於停止nginx服務, quit信號和stop信號的區別在於,nignx進程收到stop信號以後會立即停止服務,而收到quit信號後,不會再接收新的請求,但是會先處理完已經接受的鏈接請求,處理完這些請求之後再停止服務,這種停止方式被稱之爲"優雅的停止”
reload信號
reload信號的作用就是在不停止服務的情況下重載配文件,比如,nginx正在正常的提供服務,此時,管理員修改了nginx.conf文件中的配置指令,管理員希望新的配立刻生效,但是又不希望重啓nginx服務,此時就可以使用nginx -s reload命令重載配置文件,以便在不重啓nginx的情況下載入新的配置,同時避免了因重啓而造成的服務中斷
reopen信號
利用reopen信號可以使nignx進程重新打開日誌文件,以便實現日誌分割的效果
2.模塊、配置指令、塊之間的關係
下面通過一個實驗讓大家更好的理解三者之間的關係
在server1中:
vim /usr/local/nginx/conf/nginx.conf
配置發佈目錄:
mkdir /opt/demo
cd /opt/demo/
vim index.html
nginx -s reload
測試:
通過上述示例,你肯定明白了一個道理,同一個配指令,配在不同的塊中時,對應的“作用域”是不同的。
某些配指令只能在http塊中配置,某些配指令只能在location塊中配置,有些配置指令既能在server塊中配置又能在http塊中配置,而有些配置指令只能在main區中進行配置。
剛纔示例中的index指令就屬於那種既能在location塊中配置,又能在server塊中配置,還能在http塊中配的指令,只不過,當index指令配置在不同的塊中時,對應的作用域不同。
有些指令既能配在server塊中,也能配在http塊中,當多個server存在相同的配置時,我們可以將這些完全相同的配置指令提取到上一級的http塊中,以便多個server塊共用這些配置。
當然,如果你在某個server中單獨配置了對應的指令,那麼這個server仍然會以自己的配置爲準。
其實,“配置指令"不僅和"塊"有一定的關係,“配置指令"和"模塊"也有非常緊密的對應關係。nginx是模塊化的,不同的"模塊"負責不同的"功能”。所以,當我們需要針對某個"功能"進行配置時,就需要使用到對應的"配置指令"。從根本上來說,每個"配置指令"都屬於某一個"模塊",一個"模塊"中會有一個或多個"配置指令",當我們想要對相關模塊或者功能進行設定時,就會使用到對應模塊中的配置指令。
3.nginx進程
1.查看nginx進程
當你啓動nginx以後,使用ps命令查nginx進程,會發現nginx進程不只有一個。默認情況下,你會看到至少兩個nginx進程
2.指定worker進程的運行用戶
編譯安裝nginx後,默認情況下worker進程是以nobody用戶的身份運行的,如果我們想要指定worker進程的運行用戶,則可以使用user指令
指定worker進程以nginx用戶的身份運行:
建立nginx用戶:
useradd -u 900 nginx
id nginx
修改配置文件:
vim /usr/local/nginx/conf/nginx.conf #修改文件第一行
nginx -t #語法檢測
nginx -s reload #在不暫停服務的情況下重新加載
ps -ef | grep nginx|grep -v grep
3.定義worker進程數
當我啓動nginx以後,有兩個nginx進程:一個master進程,一個worker進程。
這兩個nginx進程都有各自的作用,見名知意,"worker"進程天生就是來"幹活"的,真正負責處理請求的進程就是你看到的"worker"進程,那麼"master"進程有什麼用呢? "mlaster"進程其實是負責管理"worker"進程的,除了管理"worker"進程,master"進程還負責讀取配通文件、判斷配文件語法的工作。
"master進程"也叫"主進程",在nginx中, "master"進程只能有一個,而"worker"進程可以有多個,worker"進程的數量可以由管理員自己進行定義
默認的nginx.conf配置文件中有這樣一條配置worker_ processes 1;
上述配置的意思就是啓動nginx後只有1個worker進程,你想要多少個worker進程,將worker_processes指令的值設成多少就好了。
worker_ processes指令只能在main區域中使用,通常情況下,worker_ processes的值通常不會大於服務器中cpu的核心數量。換句話說就是,worker進程的數量通常與服務器有多少cpu核心有關
比如,nginx所在服務器擁有4核cpu,那麼worker_ processes的值通常不會大於4,這樣做的原因是爲了儘讓每個worker進程都有一個cpu可以使用,儘量避免了多個worker進程搶佔同一個cpu的情況,我們也可以將worker _processes的值設置爲auto
當worker_ processes的值爲auto時,nginx 會自動檢測當前主機的cpu核心數,並啓動對應數量的worker進程。比如,nginx檢測到當前主機一共有4個cpu核心,那麼nginx就會啓動4個worker進程
將服務器的CPU數設置爲4:
修改配置文件:
vim /usr/local/nginx/conf/nginx.conf #修改文件第二行
nginx -t #語法檢測
nginx -s reload #在不暫停服務的情況下重新加載
ps -ef | grep nginx|grep -v grep
4.綁定worker和CPU
爲了避免cpu在切換進程時產生性能損耗,我們也可以將worker進程與cpu核心進行"綁定",當worker進程與cpu核心綁定以後,worker進程可以更好的專注的使用某個cpu核心上的緩存,從而減少因爲cpu切換不同worker進程而帶來的緩存失效。
如果想要讓worker進程與某個cpu核心綁定,則需要藉助另外-一個配置指令worker_ cpu_ _affinity指令
想要搞明白怎樣使用"worker_ cpu_ _affinity"指令, 最好先來了解一個概念
這個概念就是"cpu掩碼",我們可以通過"cpu掩碼"表示某個cpu核心
比如,當前機器上一共有4個cpu核心,那麼我們就用4個0表示這4個核
也就是說,我們可以使用如下字符表示這4個核: 0000那麼:
第一個核就用如下字符表示
0001
第二個核就用如下字符表示
0010
第三個核就用如下字符表示
0100
第四個和就用如下字符表示
規律就是,有幾個核,就用幾個0表示,如果想要使用某個核,就將對應位置的0改成1。從右邊開始數。
比如:如果有8個核,我就可以使用如下字符表示這8個核中的第二個核:00000010
4.漫談5種IO模型
5種IO模型分別是阻塞IO模型、非阻塞IO模型、IO複用模型、信號驅動的IO模型、異步IO模型
1.什麼是IO
IO在計算機中指Input/Output,也就是輸入和輸出。由於程序和運行時數據是在內存中駐留,由CPU這個超快的計算核心來執行,涉及到數據交換的地方,通常是磁盤、網絡等,就需要IO接口。
比如你打開瀏覽器,訪問騰訊首頁,瀏覽器這個程序就需要通過網絡IO獲取騰訊的網頁。瀏覽器首先會發送數據給騰訊服務器,告訴它我想要首頁的HTML,這個動作是往外發數據,叫Output,騰訊服務器把網頁發過來,這個動作是從外面接收數據,叫Input。所以,通常,程序完成IO操作會有Input和Output兩個數據流。當然也有隻用一個的情況,比如,從磁盤讀取文件到內存,就只有Input操作,反過來,把數據寫到磁盤文件裏,就只是一個Output操作。
I/O操作是相對於內存而言的,從外部設備進入內存就叫Input,反之從內存輸出到外部設備就叫Output
通常用戶進程中的一個完整IO分爲兩階段:用戶進程空間<- ->內核空間、內核空間<- ->設備空間(磁盤、網絡等)。IO有內存IO、 網絡IO和磁盤IO三種,通常我們說的IO指的是後兩者。
I/O按照設備來分的話,分爲兩種,其一是網絡I/O,也就是通過網絡進行數據的拉取和輸出。還有一種是磁盤I/O,主要是對磁盤進行讀寫工作。
LINUX中進程無法直接操作I/O設備,其必須通過系統調用請求kernel來協助完成I/O動作;內核會爲每個I/O設備維護一個緩衝區。
對於一個輸入操作來說,進程IO系統調用後,內核會先看緩衝區中有沒有相應的緩存數據,沒有的話再到設備中讀取,因爲設備IO一般速度較慢,需要等待;內核緩衝區有數據則直接複製到進程空間。
所以,對於一個網絡輸入操作通常包括兩個不同階段:
等待網絡數據到達網卡→讀取到內核緩衝區,數據準備好; 從內核緩衝區複製數據到進程空間
2.什麼是用戶空間和內核空間
虛擬內存被操作系統劃分成兩塊:內核空間和用戶空間。
爲了安全,它們是隔離的,即使用戶的程序崩潰了,內核也不受影響。
內核空間是內核代碼運行的地方,用戶空間是用戶程序代碼運行的地方。
當進程運行在內核空間時就處於內核態,當進程運行在用戶空間時就處於用戶態。
3.什麼是同步和異步
由於CPU和內存的速度遠遠高於外設的速度。所以在IO編程中,就存在速度嚴重不匹配的問題。
舉個例子:
我們都是通過熱水壺來燒水的。在很久之前,科技還沒有這麼發達的時候,如果我們要燒水,需要把水壺放到火爐上,我們通過觀察水壺內的水的沸騰程度來判斷水有沒有燒開。
隨着科技的發展,現在市面上的水壺都有了提醒功能,當我們把水壺插電之後,水壺水燒開之後會通過聲音提醒我們水開了。
傳統水壺的燒水就是同步的,電熱水壺的燒水就是異步的。
同步請求:A調用B,B的處理是同步的,在處理完之前他不會通知A,只有處理完之後纔會明確的通知A。
異步請求:A調用B,B的處理是異步的,B在接到請求後先告訴A我已經接到請求了,然後異步去處理,處理完之後通過回調等方式再通知A。
同步和異步最大的區別就是被調用方的執行方式和返回時機。同步指的是被調用方做完事情之後再返回,異步指的是被調用方先返回,然後再做事情,做完之後再想辦法通知調用方。
4.什麼是阻塞和非阻塞
仍然用上面的例子來說:當你把水放到水壺裏面,按下開關後,你可以坐在水壺前面,別的事情什麼都不做,一直等着水燒好。你還可以先去客廳看電視,等着水開就好了。
對於你來說,坐在水壺前面等就是阻塞的,去客廳看電視等着水開就是非阻塞的。
阻塞請求:A調用B,A一直等着B的返回,別的事情什麼也不幹。
非阻塞請求:A調用B,A不用一直等着B的返回,先去忙別的事情了。
阻塞和非阻塞最大的區別就是在被調用方返回結果之前的這段時間內,調用方是否一直等待。阻塞指的是調用方一直等待別的事情什麼都不做。非阻塞指的是調用方先去忙別的事情。
5.阻塞IO模型
進程發起IO系統調用後,進程被阻塞,轉到內核空間處理,整個IO處理完畢後返回進程。操作成功則進程獲取到數據
6.非阻塞IO模型
進程發起IO系統調用後,如果內核緩衝區沒有數據,需要到IO設備中讀取,進程返回一個錯誤而不會被阻塞;進程發起IO系統調用後,如果內核緩衝區有數據,內核就會把數據返回進程。
對於上面的阻塞IO模型來說,內核數據沒準備好需要進程阻塞的時候,就返回一個錯誤,以使得進程不被阻塞
小明去火車站買票,隔12小時去火車站問有沒有退票,三天後買到一張票。 耗費:往返車站6次,路上6小時,其他時間做了好多事
7.IO複用模型
多個的進程的IO可以註冊到一個複用器(select)上,然後用一個進程調用該select,,select會監聽所有註冊進來的IO
如果select沒有監聽的IO在內核緩衝區都沒有可讀數據,select調用進程會被阻塞;而當任一IO在內核緩衝區中有可數據時,select調用就會返回;而後select調用進程可以自己或通知另外的進程(註冊進程)來再次發起讀取IO,讀取內核中準備好的數據
典型應用:select、 poll、 epoll三種方案,nginx都可以選擇使用這三個方案
Linux中IO複用的實現方式主要有select,poll和epoll:
Select:註冊IO、阻塞掃描,監聽的IO最大連接數不能多於FD_ SIZE(1024)
Poll:原理和Select相似,沒有數量限制,但IO數量大掃描線性性能下降
Epoll:事件驅動不阻塞,mmap實現內核與用戶空間的消息傳遞,數量很大,Linux2.6後內核支持
select/poll: 小明去火車站買票,委託黃牛,黃牛三天內買到票,然後打便所有人要買票人的電話找到小明,小明去火車站交錢領票。
耗費:往返車站2次,路上2小時,黃牛手續費100元,等待通知3小時 epoll:
小明去火車站買票,委託黃牛,黃牛買到後即通知小明去領,然後小明去火車站交錢領票。
耗費:往返車站2次,路上2小時,黃牛手續費100元,無需打電話
8.信號驅動IO模型
當進程發起一個IO操作,會向內核註冊一個信號處理函數,然後進程返回不阻塞;當內核數據就緒時會發送一個信號給進程,進程便在信號處理函數中調用IO讀取數據
小明去火車站買票,給售票員留下電話,有票後,售票員電話通知小明,然後小明去火車站交錢領票。
耗費:往返車站2次,路上2小時,免黃牛費100元,無需打電話
9.異步IO模型
當進程發起一個IO操作,進程返回(不阻塞),但也不能返回結果;內核把整個IO處理完後,會通知進程結果。如果IO操作成功則進程直接獲取到數據
小明去火車站買票,給售票員留下電話,有票後,售票員電話通知小明並快遞送票上門。
耗費:往返車站1次,路上1小時,免黃牛費100元,無需打電話
注意:
此模型和前面模型最大的區別是:前4個都是阻塞的,因爲需要自己把用戶準備好的數據,放在我的用戶空間,而全異步都幫我們做好了。
用戶線程完全不需要關心實際的整個IO操作是如何進行的,只需要先發起一個請求,當接收內核返回的成功信號時表示IO操作已經完成,可以直接去使用數據了。它是最理想的模型
10. 5種IO模型的區別
5.root和alias的區別
在nginx中,我們可以通過location塊與root指令結合的方式,將"url"與"服務器路徑"建立起對應關係,location塊負責匹配url,root指令負責將匹配到的url與服務器中某個具體目錄對應起來
其實,除了root指令,還有另一個指令也能實現類似的功能,它就是alias指令,root指令和alias指令都能將urI和服務器路徑進行對應,但是它們之間又存在一些區別
下面通過一個實驗來了解root和alias的區別
在root下
vim /usr/local/nginx/conf/nginx.conf
location /demo {
root /opt/test;
}
[root@server1 ~]# cd /opt/
[root@server1 opt]# ls
demo
[root@server1 opt]# mkdir test
[root@server1 opt]# mv demo test/
nginx -s reload #在不暫停服務的情況下重新加載
測試:
location塊匹配的url爲"/demo",root指令的路徑爲"/opt/test",那麼,根據上述配置,當我們訪問"/demo"這個urI時,實際上訪問的是/opt/test/demo路徑
配置上述location塊後,當我們訪問/demo/timg.jpg這個url時,我們訪問的是/opt/test/demo/timg.jpg
簡單來說,root就是把url路徑補在指定路徑後面
在alias下
vim /usr/local/nginx/conf/nginx.conf
location /demo {
alias /opt/test;
}
nginx -s reload
爲什麼會訪問不到呢?
總結:
location塊匹配的url爲"/demo",alias指令的路徑爲/opt/test 。如你所見,alias指令對應的值也是一個路徑,當alias指令與location塊結合時,當我們訪問/demo/timg.jpg時,其實就是在訪問服務器的/opt/test/timg.jpg,也就是說,當我們使用alias時,location的urI是與alias的路徑完全對等的
root指令和alias指令的區別的區別總結:
root指令會將location塊的"url路徑"帶入到"root指令路徑"中,將帶入後的路徑作爲"最終路徑",使用"最終路徑"與urI建立對應關係。
alias指令則直接將location塊的"urI路徑"與"alias指令路"建立對應關係
alias指令和root指令能夠處於的上下文位置也不同,alias指令只能在location塊中使用,而root指令則不然
6.獲取真實的客戶端ip
1.獲取真實的客戶端ip
實驗環境:server1作爲nginx服務器;server2爲server1的代理;server3爲客戶端
7.限制用戶訪問
1.爲什麼要限制用戶訪問?
我們經常會遇到這種情況,服務器流量異常,負載過大等等。對於大流量惡意的攻擊訪問,會帶來帶寬的浪費、服務器壓力、影響業務,往往考慮對同一個ip的連接數、併發數進行限制。
2.限制用戶訪問
修改nginx配置文件:
vim /usr/local/nginx/conf/nginx.conf
36 #gzip on;
37 limit_conn_zone $binary_remote_addr zone=addr:10m; #大小是10M內存來對於IP傳輸開銷
38 limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; #1s中不超過一個請求
39 server {
48 location /download {
49 limit_conn addr 1; #只能一個併發,多了會報錯
50 #limit_rate 50k; #限制帶寬,每秒最多50k
51 }
nginx -t #語法檢測
nginx -s reload #在不暫停服務的情況下重新加載
mkdir /usr/local/nginx/html/download/
cd /usr/local/nginx/html/download/
ls
cd /usr/local/nginx/logs
>access.log #清空日誌(方便實驗效果的觀察)
測試:
ab -c 10 -n 1000 http://172.25.254.1/download/c.jpg #查看日誌503報錯
8.HTTPS加密認證
同http服務一樣,nginx也可以設置https加密認證。當我們訪問http://www.westos.org時,它會幫我們自動跳轉到https://www.westos.org
下面是設定加密認證的步驟:
修改配置文件:
vim /usr/local/nginx/conf/nginx.conf
103 server {
104 listen 443 ssl;
105 server_name www.westos.org;
106
107 ssl_certificate cert.pem;
108 ssl_certificate_key cert.pem;
109
110 ssl_session_cache shared:SSL:1m;
111 ssl_session_timeout 5m;
112
113 ssl_ciphers HIGH:!aNULL:!MD5;
114 ssl_prefer_server_ciphers on;
115
116 location / {
117 root /web;
118 index index.html index.htm;
119 }
120 }
製作key:
cd /etc/pki/tls/certs/
make cert.pem
發送key:
cp cert.pem /usr/local/nginx/conf/
製作發佈頁面:
mkdir /web
vim /web/index.html
重新啓動nginx:
nginx -t #語法檢測
nginx -s reload #在不暫停服務的情況下重新加載