<ruby id="h6500"><table id="h6500"></table></ruby>
    1. <ruby id="h6500"><video id="h6500"></video></ruby>
          1. <progress id="h6500"><u id="h6500"><form id="h6500"></form></u></progress>

            百萬(wàn)并發(fā)下的Nginx優(yōu)化,看這一篇就夠了!

            發(fā)表于:2018-11-12來(lái)源:高效運維作者:陶輝點(diǎn)擊數: 標簽:Nginx
            本文作者主要分享在 Nginx 性能方面的實(shí)踐經(jīng)驗,希望能給大家帶來(lái)一些系統化思考,幫助大家更有效地去做 Nginx。

             

            優(yōu)化方法論

            我重點(diǎn)分享如下兩個(gè)問(wèn)題:

            • 保持并發(fā)連接數,怎么樣做到內存有效使用。
            • 在高并發(fā)的同時(shí)保持高吞吐量的重要要點(diǎn)。

            實(shí)現層面主要是三方面優(yōu)化,主要聚焦在應用、框架、內核。

            硬件限制可能有的同學(xué)也都聽(tīng)過(guò),把網(wǎng)卡調到萬(wàn)兆、10G 或者 40G 是最好的,磁盤(pán)會(huì )根據成本的預算和應用場(chǎng)景來(lái)選擇固態(tài)硬盤(pán)或者機械式硬盤(pán),關(guān)注 IOPS 或者 BPS。

            CPU 是我們重點(diǎn)看的一個(gè)指標。實(shí)際上它是把操作系統的切換代價(jià)換到了進(jìn)程內部,所以它從一個(gè)連接器到另外一個(gè)連接器的切換成本非常低,它性能很好,協(xié)程 Openresty 其實(shí)是一樣的。

            資源的高效使用,降低內存是對我們增大并發(fā)性有幫助的,減少 RTT、提升容量。

            Reuseport 都是圍繞著(zhù)提升 CPU 的機核性。還有 Fast Socket,因為我之前在阿里云的時(shí)候還做過(guò)阿里云的網(wǎng)絡(luò ),所以它能夠帶來(lái)很大的性能提升,但是問(wèn)題也很明顯,就是把內核本身的那套東西繞過(guò)去了。

            請求的“一生”

            下面我首先會(huì )去聊一下怎么看“請求”,了解完這個(gè)以后再去看怎么優(yōu)化就會(huì )很清楚了。

            說(shuō)這個(gè)之前必須再說(shuō)一下 Nginx 的模塊結構,像 Nginx 以外,任何一個(gè)外部框架都有個(gè)特點(diǎn),如果想形成整個(gè)生態(tài)必須允許第三方的代碼接進(jìn)來(lái),構成一個(gè)序列,讓一個(gè)請求挨個(gè)被模塊共同處理。

            那 Nginx 也一樣,這些模塊會(huì )串成一個(gè)序列,一個(gè)請求會(huì )被挨個(gè)的處理。在核心模塊里有兩個(gè),分別是 Steam 和 NGX。

            請求到來(lái)

            一個(gè)連接開(kāi)始剛剛建立請求到來(lái)的時(shí)候會(huì )發(fā)生什么事情?先是操作系統內核中有一個(gè)隊列,等著(zhù)我們的進(jìn)程去系統調用,這時(shí)候因為有很多工作進(jìn)程,誰(shuí)會(huì )去調用呢,這有個(gè)負載均衡策略。

            現在有一個(gè)事件模塊,調用了 Epoll Wait 這樣的接口,Accept 建立好一個(gè)新連接,這時(shí)會(huì )分配到連接內存池,這個(gè)內存池不同于所有的內存池,它在連接剛創(chuàng )建的時(shí)候會(huì )分配,什么時(shí)候會(huì )釋放呢?

            只有這個(gè)連接關(guān)閉的時(shí)候才會(huì )去釋放。接下來(lái)就到了 NGX 模塊,這時(shí)候會(huì )加一個(gè) 60 秒的定時(shí)器。

            就是在建立好連接以后 60 秒之內沒(méi)有接到客戶(hù)端發(fā)來(lái)的請求就自動(dòng)關(guān)閉,如果 60 秒過(guò)來(lái)之后會(huì )去分配內存,讀緩沖區。什么意思呢?

            現在操作系統內核已經(jīng)收到這個(gè)請求了,但是我的應用程序處理不了,因為沒(méi)有給它讀到用戶(hù)態(tài)的內存里去,所以這時(shí)候要分配內存。

            從連接內存池這里分配,那要分配多大呢?會(huì )擴到 1K。

            收到請求

            當收到請求以后,接收 Url 和 Header,分配請求內存池,這時(shí)候 Request Pool Size 是 4K,大家發(fā)現是不是和剛才的有一個(gè) 8 倍的差距,這是因為利用態(tài)的內存是非常消耗資源的。

            再看為什么會(huì )消耗資源,首先會(huì )用狀態(tài)機解去形容,所謂狀態(tài)機解就是把它當做一個(gè)序列,一個(gè)支節一個(gè)支節往下解,如果發(fā)現換行了那就是請求行解完了。

            但如果這個(gè)請求特別長(cháng)的時(shí)候,就會(huì )去再分配更大的,剛剛 1K 不夠用了,為什么是 4 乘 8K 呢?

            就是因為當 1K 不夠了不會(huì )一次性分配 32K,而是一次性分配 8K。如果 8K 以后還沒(méi)有解析到剛才的標識符,就會(huì )分配第二個(gè) 8K。

            我之前收到的所有東西都不會(huì )釋放,只是放一個(gè)指針,指到 Url 或者指到那個(gè)協(xié)議,標識它有多長(cháng)就可以了。

            接下來(lái)解決 Header,這個(gè)流程一模一樣的沒(méi)有什么區別,這時(shí)候還會(huì )有一個(gè)不夠用的情況,當我接收完所有的 Header 以后,會(huì )把剛剛的定時(shí)器給移除,移除后接下來(lái)做 11 個(gè)階段的處理。

            也就是說(shuō)剛剛所有的外部服務(wù)器都是通過(guò)很多的模塊串成在一起處理一個(gè)請求的。

            像剛剛兩頁(yè) PPT 都在說(shuō)藍色的區域,那么請求接下來(lái) 11 個(gè)階段是什么意思呢?這個(gè)黃色的、綠色的,還有右邊這個(gè)都是在 11 階段之中。

            這 11 個(gè)階段大家也不用記,非常簡(jiǎn)單,只要掌握三個(gè)關(guān)鍵詞就可以。

            剛剛讀完 Header 要做處理,所以這時(shí)候第一階段是 Post-Read。接下來(lái)會(huì )有 Rewrite,還有 Access 和 Preaccess。

            先看左手邊,當我們下載完 Nginx 源碼編以后會(huì )有一個(gè) Referer,所有的第三方數據都會(huì )在這里呈現有序排列。

            這些序列中并不是簡(jiǎn)單的一個(gè)請求給它再給它,先是分為 11 個(gè)階段,每個(gè)階段之內大家是有序一個(gè)個(gè)往后來(lái)的,但在 11 個(gè)階段中是按階段來(lái)的。

            我把它分解一下,第一個(gè) Referer 這階段有很多模塊,后面這是有序的。

            這個(gè)圖比剛剛的圖多了兩個(gè)關(guān)鍵點(diǎn):

            • 第一到了某一個(gè)模塊可以決定繼續向這序列后的模塊執行,也可以說(shuō)直接跳到下個(gè)階段,但不能說(shuō)跳多個(gè)階段。
            • 第二是生成了向客戶(hù)端反映的響應,這時(shí)候要對響應做些處理,這里是有序的,先做縮略圖再做壓縮,所以它是有嚴格順序的。

            請求的反向代理

            請求的反向代理,反向代理這塊是我們 Nginx 的重點(diǎn)應用場(chǎng)景,因為 Nginx 會(huì )考慮一種場(chǎng)景,客戶(hù)端走的是公網(wǎng),所以網(wǎng)絡(luò )環(huán)境非常差,網(wǎng)速非常慢。

            如果簡(jiǎn)單用一個(gè)緩沖區從客戶(hù)端收一點(diǎn)發(fā)給上游服務(wù)器,那上游服務(wù)器的壓力會(huì )很大,因為上游服務(wù)器往往它的效率高,所以都是一個(gè)請求被處理完之前不會(huì )再處理下一個(gè)請求。

            Nginx 考慮到這個(gè)場(chǎng)景,它會(huì )先把整個(gè)請求全部收完以后,再向上游服務(wù)器建立連接,所以是默認第一個(gè)配置,就是 Proxy Request Buffering On,存放包體至文件,默認 Size 是 8K。

            那建立上游連接的時(shí)候會(huì )放 Time Out,60 秒,添加超時(shí)定時(shí)器,也是 60 秒的。

            發(fā)出請求(讀取文體包件),如果向上游傳一個(gè)很大的包體的話(huà),那 Sizk 就是 8K。

            默認 Proxy Limit Rate 是打開(kāi)的,我們會(huì )先把這個(gè)請求全部緩存到端來(lái),所以這時(shí)候有個(gè) 8×8K,如果關(guān)掉的話(huà),也就是從上游發(fā)一點(diǎn)就往下游發(fā)一點(diǎn)。

            知道這個(gè)流程以后,再說(shuō)這里的話(huà)大家可以感覺(jué)到這里的內存消耗還是蠻大的。

            返回響應

            返回響應,這里面其實(shí)內容蠻多的,我給大家簡(jiǎn)化一下,還是剛剛官方的那個(gè)包,這也是有順序的從下往上看,如果有大量第三方模塊進(jìn)來(lái)的話(huà),數量會(huì )非常高。

            第一個(gè)關(guān)鍵點(diǎn)是上面的 Header Filter,上面是 Write Filter,下面是 Postpone Filter,這里還有一個(gè) Copy Filter,它又分為兩類(lèi),一類(lèi)是需要處理,一類(lèi)是不需要處理的。

            OpenResty 的指令,第一代碼是在哪里執行的,第二個(gè)是 SDK。

            應用層優(yōu)化

            協(xié)議

            做應用層的優(yōu)化我們會(huì )先看協(xié)議層有沒(méi)有什么優(yōu)化,比如說(shuō)編碼方式、Header 每次都去傳用 Nginx 的架構,以至于浪費了很多的流量。我們可以改善 Http2,有很多這樣的協(xié)議會(huì )大幅度提升它的性能。

            當然如果你改善 Http2 了,會(huì )帶來(lái)其他的問(wèn)題,比如說(shuō) Http2 必須走這條路線(xiàn)。

            這條路線(xiàn)又是一個(gè)很大的話(huà)題,它涉及到安全性和性能,是互相沖突的東西。

            壓縮

            我們希望“商”越大越好,壓縮這里會(huì )有一個(gè)重點(diǎn)提出來(lái)的動(dòng)態(tài)和靜態(tài),比如說(shuō)我們用了拷貝,比如說(shuō)可以從磁盤(pán)中直接由內核來(lái)發(fā)網(wǎng)卡,但一旦做壓縮的話(huà)就不得不先把這個(gè)文件讀到 Nginx,交給后面的極內核去做一下處理。

            Keepalive 長(cháng)連接也是一樣的,它也涉及到很多東西,簡(jiǎn)單來(lái)看這也就是附用連接。

            因為連接有一個(gè)慢啟動(dòng)的過(guò)程,一開(kāi)始它的窗口是比較小,一次可能只傳送很小的 1K 的,但后面可能會(huì )傳送幾十K,所以你每次新建連接它都會(huì )重新開(kāi)始,這是很慢的。

            當然這里還涉及到一個(gè)問(wèn)題,因為 Nginx 內核它默認打開(kāi)了一個(gè)連接空閑的時(shí)候,長(cháng)連接產(chǎn)生的作用也會(huì )下降。

            提高內存使用率

            剛剛在說(shuō)具體的請求處理過(guò)程中已經(jīng)比較詳細的把這問(wèn)題說(shuō)清楚了,這里再總結一下,在我看來(lái)有一個(gè)角度,Nginx 對下游只是必須要有的這些模塊,Client Header、Buffer Size:1K,上游網(wǎng)絡(luò ) Http 包頭和包體。

            CPU 通過(guò)緩存去取儲存上東西的時(shí)候,它是一批一批取的,每一批目前是 64 字節,所以默認的是 8K。

            如果你配了 32 它會(huì )給你上升到 64;如果你配了 65 會(huì )升到 128,因為它是一個(gè)一個(gè)序列化重組的。

            所以了解這個(gè)東西以后自己再配的時(shí)候就不會(huì )再犯問(wèn)題。紅黑樹(shù)這里用的非常多,因為是和具體的模塊相關(guān)。

            限速

            大部分我們在做分公司流控的時(shí)候,主要在限什么呢?主要限 Nginx 向客戶(hù)端發(fā)送響應的速度。

            這東西非常好用,因為可以和 Nginx 定量連接在一起。這不是限上游發(fā)請求的速度,而是在限從上游接響應的速度。

            Worker 間負載均衡

            當時(shí)我在用 0.6 版本的時(shí)候那時(shí)候都在默認用這個(gè),這個(gè)“鎖”它是在用進(jìn)程間同步方式去實(shí)現負載均衡,這個(gè)負載均衡怎么實(shí)現呢?

            就是保證所有的 Worker 進(jìn)程,同一時(shí)刻只有一個(gè) Worker 進(jìn)程在處理距離,這里就會(huì )有好幾個(gè)問(wèn)題,綠色的框代表它的吞吐量,吞吐量不高,所以會(huì )導致第二個(gè)問(wèn)題 Requests,也是比較長(cháng)的,這個(gè)方差就非常的大。

            如果把這個(gè)“鎖”關(guān)掉以后,可以看到吞吐量是上升的,方差也在下降,但是它的時(shí)間在上升,為什么會(huì )出現這樣的情況?

            因為會(huì )導致一個(gè) Worker 可能會(huì )非常忙,它的連接數已經(jīng)非常高了,但是還有其他的 Worker 進(jìn)程是很閑的。

            如果用了 Requests,它會(huì )在內核層面上做負載均衡。這是一個(gè)專(zhuān)用場(chǎng)景,如果在復雜應用場(chǎng)景下開(kāi) Requests 和不開(kāi)是能看到明顯變化的。

            超時(shí)

            這里我剛剛說(shuō)了好多,它是一個(gè)紅黑樹(shù)在實(shí)現的。唯一要說(shuō)的也就是這里,Nginx 現在做四層的反向代理也很成熟了。

            像 UTP 協(xié)議是可以做反向代理的,但要把有問(wèn)題的連接迅速踢掉的話(huà),要遵循這個(gè)原則,一個(gè)請求對一個(gè)響應。

            緩存

            只要想提升性能必須要在緩存上下工夫。比如說(shuō)我以前在阿里云做云盤(pán),云盤(pán)緩存的時(shí)候就會(huì )有個(gè)概念叫空間維度緩存,在讀一塊內容的時(shí)候可能會(huì )把這內容周邊的其他內容也讀到緩存中。

            大家如果熟悉優(yōu)化的話(huà)也會(huì )知道有分支預測先把代碼讀到那空間中,這個(gè)用的比較少,基于時(shí)間維度用的比較多了。

            減少磁盤(pán) IO

            其實(shí)要做的事也非常多,優(yōu)化讀取,Sendfile 零拷貝、內存盤(pán)、SSD 盤(pán)。減少寫(xiě)入,AIO,磁盤(pán)是遠大于內存的,當它把你內存消化完的時(shí)候還會(huì )退化成一個(gè)調用。

            像 Thread Pool 只用讀文件,當退化成這種模式變多線(xiàn)程可以防止它的主進(jìn)程被阻塞住,這時(shí)候官方的博客上說(shuō)是有 9 倍的性能提升。

            系統優(yōu)化

            提升容量配置

            我們建連接的時(shí)候也有,還有些向客戶(hù)端發(fā)起連接的時(shí)候會(huì )有一個(gè)端口范圍,還有一些像對于網(wǎng)卡設備的。

            CPU緩存的親和性

            CPU緩存的親和性,這看情況了,現在用 L3 緩存差不多也 20 兆的規模,CPU 緩存的親和性是一個(gè)非常大的話(huà)題,這里就不再展開(kāi)了。

            NUMA 架構的 CPU 親和性

            把內存分成兩部分,一部分是靠近這個(gè)核,一部分靠近那個(gè)核,如果訪(fǎng)問(wèn)本核的話(huà)就會(huì )很快,靠近另一邊大概會(huì )耗費三倍的損耗。對于多核 CPU 的使用對性能提升很大的話(huà)就不要在意這個(gè)事情。

            網(wǎng)絡(luò )快速容錯

            因為 TCP 的連接最麻煩的是在建立連接和關(guān)閉連接,這里有很多參數都是在調,每個(gè)地方重發(fā),多長(cháng)時(shí)間重發(fā),重發(fā)多少次。

            這里給大家展示的是快啟動(dòng),有好幾個(gè)概念在里面,第一個(gè)概念在快速啟動(dòng)的時(shí)候是以?xún)杀兜乃俣取?/p>

            因為網(wǎng)的帶寬是有限的,當你超出網(wǎng)絡(luò )帶寬的時(shí)候其中的網(wǎng)絡(luò )設備是會(huì )被丟包的,也就是控制量在往下降,那再恢復就會(huì )比較慢。

            TCP 協(xié)議優(yōu)化

            TCP 協(xié)議優(yōu)化,本來(lái)可能差不多要四個(gè)來(lái)回才能達到每次的傳輸在網(wǎng)絡(luò )中有幾十 K,那么提前配好的話(huà)用增大初始窗口讓它一開(kāi)始就達到最大流量。

            提高資源效率

            提高資源效率,這一頁(yè)東西就挺多了,比如說(shuō)先從 CPU 看,TCP Defer Accept,如果有這個(gè)的話(huà),實(shí)際上會(huì )犧牲一些即時(shí)性,但帶來(lái)的好處是第一次建立好連接沒(méi)有內容過(guò)來(lái)的時(shí)候是不會(huì )激活 Nginx 做切換的。

            內存在說(shuō)的時(shí)候是系統態(tài)的內存,在內存大和小的時(shí)候操作系統做了一次優(yōu)化,在壓力模式和非壓力模式下為每一個(gè)連接分配的系統內存可以動(dòng)態(tài)調整。

            網(wǎng)絡(luò )設備的核心只解決一個(gè)問(wèn)題,變單個(gè)處理為批量處理,批量處理后吞吐量一定是會(huì )上升的。

            因為消耗的資源變少了,切換次數變少了,它們的邏輯是一樣的,就這些邏輯和我一直在說(shuō)的邏輯都是同一個(gè)邏輯,只是應用在不同層面會(huì )產(chǎn)生不同的效果。

            端口復用,像 Reals 是很好用的,因為它可以把端口用在上游服務(wù)連接的層面上,沒(méi)有帶來(lái)隱患。

            提升多 CPU 使用效率

            提升多 CPU 使用效率,上面很多東西都說(shuō)到了,重點(diǎn)就兩個(gè),一是 CPU 綁定,綁定以后緩存更有效。多隊列網(wǎng)卡,從硬件層面上已經(jīng)能夠做到了。

            BDP,帶寬肯定是知道的,帶寬和時(shí)延就決定了帶寬時(shí)延積,那吞吐量等于窗口或者時(shí)延。

            內存分配速度也是我們關(guān)注的重點(diǎn),當并發(fā)量很大的時(shí)候內存的分配是比較糟糕的,大家可以看到有很多它的競品。

            PCRE 的優(yōu)化,這用最新的版本就好。


            原文轉自:https://mp.weixin.qq.com/s/gM48S7y6PR99Heh_g-NdCw

            老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月
              <ruby id="h6500"><table id="h6500"></table></ruby>
              1. <ruby id="h6500"><video id="h6500"></video></ruby>
                    1. <progress id="h6500"><u id="h6500"><form id="h6500"></form></u></progress>