Nginx 反向代理為什麼可以提高網站性能?

如果作為純粹的反向代理伺服器,不做任何緩存,也沒有靜態文件服務,每一個請求都轉發到後端,這樣還能提高性能嗎?


最高贊回答的點是buffer,這個其實意義不大,應用層的伺服器自己也可以開buffer。反向代理的主要作用是分發請求。

首先我們要了解系統的性能瓶頸在哪裡,一般來說網路io速度和內存io接近,都遠高於磁碟io。假定一個介面請求返回數據100k(一般沒有這麼大,只是假定一個方便計算的值),10個並發請求就是1M,那麼全雙工千兆網卡(現在還有萬兆網卡,但成本太高,應用還不廣),可以支撐並發10000個請求,開雙網卡,理論的上限就是20000個並發請求。

假設我們收到請求馬上就返回,那麼最高並發數就是我們上面計算的結果,但是,問題在於,應用伺服器做不到馬上返回,因為它有很多業務邏輯需要執行處理,比如給用戶發推送發簡訊發郵件,本地磁碟寫日誌,請求資料庫增刪改查,調用微信的登錄介面等等等等,都附加了各個層面的io。

所以第一層的優化,我們會盡量優化應用服務自身,把發推送發簡訊發郵件的活推到隊列,讓別的伺服器去干。這個一般用內存隊列,io很高。

開多線程或者協程的方式非同步寫日誌,但再怎麼優化,磁碟io的上限突破不了,這個io很低。還有更激進的方案,乾脆日誌也寫內存,或者通過內網網路同步到別的伺服器上,可以更優化。

資料庫復用連接池,減少連接和斷開的時間開銷。查詢語句盡量優化,減少等待資料庫操作的時間。當然,再怎麼優化,一樣有個上限。

調用微信的登錄介面等外部介面,這個就更難辦了,受制於人,除了tcp連接池復用能稍微優化一點點,完全是取決於外部條件。

木桶理論取最短板,所有這些條件里,總有最慢最落後的那個。假如拖後腿的這個,最佳狀態也只能優化到支持2000個並發,那就尷尬了,本來能支持20000個請求的系統,只能用到1/10性能。

( 當然也可以在dns對應不同ip方式分布請求,但是dns層面的分布更複雜更麻煩,因為dns緩存的原因,請求也不能均勻分布,而且ip地址也是越來越稀缺的資源,沒有背景沒有後台的,搞這麼多ip也不容易啊 )

單個公網ip算一個節點的話,這個節點本來的潛力是響應20000個並發請求,實際在應用層面只能到2000並發,潛力還未發掘啊。這個時候,就是反向代理起到用武之地的時候了。

首先一個反向代理的伺服器拋開所有業務層的東西,只單純的接下請求再返回,那麼可以支持到20000並發了。接下來應用層面誰來處理?找來10個小弟,轉發給他們,每人2000正好。這樣這個節點系統雖然性價比只有10/11,但是性能潛力好歹挖盡了。

這就是反向代理的作用了。


打個比方來說:
把伺服器想像成飯店,沒有Nginx的情況,就如同每一個廚師服務一桌顧客,從點菜開始到炒菜到上菜到收銀,有n個廚師就只能服務n桌顧客。有了Nginx的話,Nginx就成了強大的服務員,把招呼,點菜、上菜和收銀的活都做了,廚師只需要專心炒菜就行。這樣飯店的效率就大大提高了。

技術一點的話:
請求如果直接發到同步處理的後端,那麼從收到請求到把響應發出去這段時間,一個進程的資源就被佔用了(比如Apache的prefork模式)。在慢連接的情況下,這個進程除了處理之外的大多數時間基本上都耗費在了無意義的等待上。Nginx在這方面的優勢就在於它的非同步非阻塞模型。這意味著Nginx可以通過基於事件的方式同時處理和維護多個請求,而後端就只需要去做邏輯計算,節約了等待時間去處理更多的請求。


對於後端是動態服務來說,比如Java和PHP。這類伺服器(如JBoss和PHP-FPM)的IO處理能力往往不高。Nginx有個好處是它會把Request在讀取完整之前buffer住,這樣交給後端的就是一個完整的HTTP請求,從而提高後端的效率,而不是斷斷續續的傳遞(互聯網上連接速度一般比較慢)。同樣,Nginx也可以把response給buffer住,同樣也是減輕後端的壓力。


僅僅「轉發」請求到後端服務,nginx 也可以通過 LB 策略影響整站性能分布。

拿非同步並發來說事就太扯了,nginx 在並發上的優化只是努力降低 LB 層的損耗而已。


單純的反向代理模式顯然並不能提高性能,後端收到的請求數沒變,協議也沒變,帶寬也沒有減小,而且如果是默認配置,還會把HTTP/1.1變成HTTP/1.0,反而會降低性能,那些說性能提高的通常也沒測過,只是個心理作用而已。


提高的是吞吐量,而不是性能。


1、分離IO與業務,提高業務效率;
2、實現分流與負載均衡。


反向代理可以提高網站性能???這個單機環境下是沒啥作用的。在集群環境下,Nginx可以支持負載均衡,使網站獲得更好的性能和穩定性。
Nginx的快速, 其實在於Linux上優秀網路模型epoll 的支持
而傳統Select模型很低效


如果同時部署在一台機器上,不會有提升,在高並發下性能反而會下降,你可以用ab壓一壓。
起碼在我測試python web server的時候是這樣的,前端包個nginx返向代理,做ab測試時,並發100的情況下,平均性能會比不用ngingx下降30%左右,本來建立一次tcp連接就完事了,因為有個返向代理還得再多做一次,在高並發的情況下肯定有性能損失。


對於配置或架構不合理的後端應用程序,如默認只有100並發的http://asp.net程序,nginx可以在不增加進程數量級的情況下增加並發處理網路的數量,相當於給阻塞式服務套了個非阻塞的殼。

例:一台低配置阿里雲,默認配置tomcat和nginx
1、沒有nginx 2000並發失敗。
zhblue@XXXXXX:~$ ab -n 10000 -c 2000 http://127.0.0.1:8080/pm/
This is ApacheBench, Version 2.3 &<$Revision: 1528965 $&>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, Welcome to The Apache Software Foundation!

Benchmarking 127.0.0.1 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
apr_socket_recv: Connection reset by peer (104)
Total of 9701 requests completed
2、加上nginx
zhblue@XXXXXX:~$ ab -n 10000 -c 2000 http://127.0.0.1/pm/
This is ApacheBench, Version 2.3 &<$Revision: 1528965 $&>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, Welcome to The Apache Software Foundation!

Benchmarking 127.0.0.1 (be patient)
Completed 1000 requests
Completed 2000 requests
Completed 3000 requests
Completed 4000 requests
Completed 5000 requests
Completed 6000 requests
Completed 7000 requests
Completed 8000 requests
Completed 9000 requests
Completed 10000 requests
Finished 10000 requests


Server Software: nginx/1.4.6
Server Hostname: 127.0.0.1
Server Port: 80

Document Path: /pm/
Document Length: 945 bytes

Concurrency Level: 2000
Time taken for tests: 17.208 seconds
Complete requests: 10000
Failed requests: 21
(Connect: 0, Receive: 0, Length: 21, Exceptions: 0)
Non-2xx responses: 21
Total transferred: 11862213 bytes
HTML transferred: 9433956 bytes
Requests per second: 581.12 [#/sec] (mean)
Time per request: 3441.625 [ms] (mean)
Time per request: 1.721 [ms] (mean, across all concurrent requests)
Transfer rate: 673.18 [Kbytes/sec] received

Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 30 61.3 0 200
Processing: 13 1605 2533.8 154 16915
Waiting: 13 1605 2533.8 154 16915
Total: 13 1635 2561.2 154 17026

Percentage of the requests served within a certain time (ms)
50% 154
66% 1103
75% 2185
80% 3020
90% 7012
95% 7409
98% 9738
99% 10115
100% 17026 (longest request)


反向代理提高網站性能主要通過三個方面:
1,反向代理可以理解為7層應用層的負載均衡,使用負載均衡之後可以非常便捷的橫向擴展伺服器集群,實現集群整體並發能力、抗壓能力的提高。
2,通常反向代理伺服器會帶有本地Cache功能,通過靜態資源的Cache,有效的減少後端伺服器所承載的壓力,從而提高性能
3,http壓縮,開啟壓縮後,網路流量傳輸減小,相同帶寬下可以服務更多用戶

最後還有一個TCP鏈接復用,不過說實話,如果不是商用的負載均衡器,一般沒這個功能。

其實,反向代理還可以有效的隱藏隔離內部伺服器,提高了安全性,這也算是提高性能的一個方面吧


推薦一本書《構建高性能Web站點》

學東西要系統


如果nginx 作為純粹的代理伺服器,我不認為對整個網站的性能有所提高,除非nginx 和你的後端是兩台不同的伺服器。
我們不要相信感覺,讓數字來說話。實測了得出數據來比較,才能下定論,性能提高了。

從理論上分析並猜測一下:
假設你的後端是指 apache+php handler ,前面搭一個nginx 作為代理。
nginx 只是將請求轉發,後端仍然要面對那麼多的請求,沒有任何性能上面的幫助,怎麼會性能高呢?
而且還nginx 和 apache 還建立了多一次的tcp 連接,在低並發下不會有什麼感覺,但在高並發下,性能肯定會下降。
我猜測一下,你的apache 開啟了keepalive ,apache 與用戶保持連接,apache 也是需要去維護這些連接的,當連接數逐漸高起來的時候,apache 也就吃不消了。而把nginx 假設在前面,nginx 與後端的連接是短連接,也就是,一個請求過去了,apache 返回了就斷開了。apache 不再需要維護這些連接,身上的重擔少了一塊,可以花多一點精力去處理請求了,從而讓你感覺到好像快了。

由此我可以得出,你感覺上的性能提升,是可以通過apache 的調優來完成的,與nginx 的代理無關。


我的理解:
1、分流請求
2、負載均衡
在做大型網站構架的時候,才能用得上,你一般的網站也沒必要用個好幾台伺服器吧


非常認同
1、分流請求
2、負載均衡
Nginx的確是大型網站構架的考慮。


其實,題主想問的應該是 「Nginx是怎麼實現高負載的」 ?

首先,題主對此問題的解釋,是不對的。「 1Ngxin+1後端 」,是不會對改後端服務有任何改善的;本質原因還是瓶頸就是「1後端」,所以處理能力就是「1後端」;同時,多了一次Nginx一層代理,多了一次TCP鏈接,所以處理能力甚至小於「1後端」;

所以,題主實際上想要的方案是:「1nginx + n 後端」的方式;


我給@叔度的答案補充一個例子,題目說的是不加緩存,我加緩衝應該還算切題

叔度的答案是2012年寫的,2015年我在實習的時候曾經搞過這麼一個系統,只說和這個題目相關的部分(其他部分做的太懶不好意思說)

當時後端是一個Tornado + LevelDB 運行在類似樹莓派的單板機上,CPU性能比較差,請求大部分都是通過網路來取LevelDB中的一塊數據,這個對於單板機並不是一個CPU消耗可以忽略的任務。

對於特定的取數據任務(一次請求總計取出100M的數據,Tornado 做Streaming,每個Chunk 大小4M左右)

當時我設計系統的時候,考慮到Linux的網路協議棧本身有緩衝,而且我用的都是Tornado的非同步模式,我就覺得Tornado 把一個Chunk發送給Linux內核,Tornado 的 Handler 的 write 就應該能返回了(考慮到Linux內核緩衝),然後做下一個chunk的讀取(CPU性能不足會有一定時間消耗)或者取處理其他請求(考慮到Tornado 非同步模型),而且我聽很多人說 Tornado 部署一般不再在前面放 nginx 了,我就沒放Nginx

然後測試的時候,發現CPU沒有佔滿,讀取帶寬上不去。這時候我就想到了很久之前看的這個問題下@叔度的回答

Nginx 反向代理為什麼可以提高網站性能?

嘗試在Tornado 前面加上 nginx 用 unix socekt 通信,然後nginx 配置了 16M 的響應緩衝,終於把CPU跑滿了,讀取帶寬達到最大。

這個項目當時做的比較倉促,後來也沒成功上線, @武翔宇 和 @gashero 可能對我加nginx這個事情都有印象

後來我才知道,linux 內核給 TCP 的緩衝好像是 Kb 級別,對於我這種一下響應幾M的,可能真的還是要等網卡把數據發出去才能callback(隨便分析了一下,不一定對)


這個問題根本就不存在。

nginx做反向代理的目標不是提高性能。

饅頭不能分開。
一個人吃1個饅頭要1分鐘。問:兩個人吃2個饅頭要幾分鐘?
100個饅頭限最短時間吃完,一個人吃更快(撐死)還是100個人吃更快?當你從50個人擴到100人,當然你覺得提高了性能,但是從100個人擴到200個人卻沒有什麼卵用。

看怎麼解釋「性能」這個詞了。


你覺得提高了性能,那是因為你用的後端語言處理IO的能力太差,阻塞式io幾個慢連接就可能堆死線程池。用golang,前面基本不用nginx,用也不是因為性能。


原因我就不多說了,我們來看一個使用keepalive實現nginx反向代理高可用的實驗。

在網站架構中,為了分散客戶端對伺服器的訪問壓力,可以使用nginx作為反向代理。但是使用一個nginx作為代理伺服器必定會面對單點故障的情況,所以一般使用多台nginx反代伺服器,而使用多台nginx伺服器還要面對如何協調調度的問題。在此,我給大家介紹使用keepalive協調調度nginx反代伺服器的方法。

keepalive簡介

說到keepalive就要說到他的實現核心——VRRP協議。VRRP即虛擬路由器冗餘協議,最初是為了解決多個路由器熱備份而制定的。它是通過主路由器定時在網路中發送主路由器信息,通知各個備路由器,當備路由器接收不到信息就會通過優先順序競選成為主路由器。

VRRP的優先順序範圍是0-255,可配置範圍1-254,其中0給路由器放棄MASTER位置時候使用,255保留給IP地址的擁有者使用。如果路由器的IP地址為虛擬IP地址時,只要其工作正常,則為MASTER路由器。

VRRP提供了三種認證方式:

1 無認證;

2 簡單字元認證:不能超過8個字元;

3 MD5認證。

實驗器材

lvs1 Centos7.3 172.18.55.74

lvs2 Centos7.3 172.18.55.75

web1 Centos6.8 172.18.55.61

web2 Centos7.3 172.18.55.71

實驗步驟:

1 安裝nginx反向代理服務

2 安裝keepalive服務

實驗過程:

1 安裝nginx反向代理服務

為了簡便,這裡分別在web1和web2上使用yum源安裝的方式

#yum install –y nginx

修改nginx的配置文件

# vim /etc/nginx/nginx.conf

增加伺服器組

upstream websvrs {
server 172.18.55.61:80;
server 172.18.55.71:80;
server 127.0.0.1:8080 backup; #sorry Server
}

增加sorry server配置

server {
listen 8080;
root /etc/nginx/html;
index sorry.html;
location / {
}
}

增加sorry server頁面文件

vim /etc/nginx/html/sorry.html

& Sorry !!! &

2 安裝keepalive服務

使用yum安裝

# yum install –y keepalived

修改配置文件

# vim /etc/keepalived/keepalived.conf

! Configuration File for keepalived

global_defs {
notification_email {
acassen@firewall.loc
failover@firewall.loc
sysadmin@firewall.loc
}
notification_email_from Alexandre.Cassen@firewall.loc
smtp_server 192.168.200.1
smtp_connect_timeout 30
router_id LVS_MASTER #備伺服器LVS_BACKUP
vrrp_mcast_group4 224.0.109.55 #組播地址,一個虛擬路由器組設置相同
}

vrrp_instance VI_1 {
state MASTER #備伺服器BACKUP
interface ens33 #網卡名
virtual_router_id 155 #虛擬路由器ID,主備設置相同
priority 100 #優先順序1-254,備伺服器98
advert_int 1 #網路通知時間間隔
authentication {
auth_type PASS #認證方式為密碼
auth_pass GOOD #密碼為GOOD,最多8位
}
virtual_ipaddress {
172.18.55.100/16 dev ens33 #指定虛擬IP地址和介面網卡
}
track_script {
ngxstatus #調用nginx狀態監測腳本
}
notify_backup "/etc/keepalived/notify.sh backup" #如果nginx的狀態改為了BACKUP,則執行此腳本
}

vrrp_script ngxstatus { #nginx狀態監測腳本
script "killall -0 nginx exit 0 || exit 1"
interval 1
weight -5
}

增加配置nginx重啟腳本,省略了非必要步驟

vim /etc/ keepalived/notify.sh

#!/bin/bash

myservice="nginx.service"

case $1 in

backup)
systemctl restart $myservice;;
esac

如果你還有學習Linux的興趣,可以看我們團隊整理的:【超全整理】《Linux雲計算從入門到精通》系列實戰筆記全放送 | Linux運維部落


推薦閱讀:

TAG:架構 | 網站架構 | 網站性能 | Nginx | 反向代理 |