程序员修神之路--提高网站的吞吐量
點(diǎn)擊上方藍(lán)色字體,關(guān)注我們
菜菜哥,有個(gè)事你還得幫我呀
呦西,YY妹子,最近天這么熱了,你怎么還穿這么多?
苦笑一下.....前幾天寫了幾個(gè)接口,領(lǐng)導(dǎo)讓提高一下接口吞吐量
這是你技術(shù)提高的大好機(jī)會(huì)呀
可吞吐量是什么呀?怎么提高呢?
來(lái),湊近一點(diǎn),哥給你解釋一番
吞吐量定義百科
吞吐量是指對(duì)網(wǎng)絡(luò)、設(shè)備、端口、虛電路或其他設(shè)施,單位時(shí)間內(nèi)成功地傳送數(shù)據(jù)的數(shù)量(以比特、字節(jié)、分組等測(cè)量)。
????????以上的定義比較寬泛,定義到網(wǎng)站或者接口的吞吐量是這樣的:吞吐量是指系統(tǒng)在單位時(shí)間內(nèi)處理請(qǐng)求的數(shù)量。這里有一個(gè)注意點(diǎn)就是單位時(shí)間內(nèi),對(duì)于網(wǎng)站的吞吐量這個(gè)單位時(shí)間一般定義為1秒,也就是說(shuō)網(wǎng)站在一秒之內(nèi)能處理多少http(https/tcp)請(qǐng)求。與吞吐量對(duì)應(yīng)的衡量網(wǎng)站性能的還有響應(yīng)時(shí)間、并發(fā)數(shù)、QPS每秒查詢率。
響應(yīng)時(shí)間是一個(gè)系統(tǒng)最重要的指標(biāo)之一,它的數(shù)值大小直接反應(yīng)了系統(tǒng)的快慢。響應(yīng)時(shí)間是指執(zhí)行一個(gè)請(qǐng)求從開始到最后收到響應(yīng)數(shù)據(jù)所花費(fèi)的總體時(shí)間。
并發(fā)數(shù)是指系統(tǒng)同時(shí)能處理的請(qǐng)求數(shù)量,這個(gè)也是反應(yīng)了系統(tǒng)的負(fù)載能力。
每秒查詢率(QPS)是對(duì)一個(gè)特定的查詢服務(wù)器在規(guī)定時(shí)間內(nèi)所處理流量多少的衡量標(biāo)準(zhǔn),在因特網(wǎng)上,作為域名系統(tǒng)服務(wù)器的機(jī)器的性能經(jīng)常用每秒查詢率來(lái)衡量。對(duì)應(yīng)fetches/sec,即每秒的響應(yīng)請(qǐng)求數(shù),也即是最大吞吐能力。
????????我們以高速收費(fèi)站為例子也許更直觀一些,吞吐量就是一天之內(nèi)通過(guò)的車輛數(shù),響應(yīng)時(shí)間就是車速,并發(fā)數(shù)就是高速上同時(shí)奔跑的汽車數(shù)。由此可見其實(shí)以上幾個(gè)指標(biāo)是有內(nèi)在聯(lián)系的。比如:響應(yīng)時(shí)間縮短,在一定程度上可以提高吞吐量。
其實(shí)以上幾個(gè)指標(biāo)主要反映了兩個(gè)概念:
1. 系統(tǒng)在單位時(shí)間之內(nèi)能做多少事情
2. 系統(tǒng)做一件事情需要的時(shí)間
????????服務(wù)器級(jí)別增加網(wǎng)站吞吐量也是諸多措施中最容易并且是效果最好的,如果一個(gè)網(wǎng)站能通過(guò)增加少量的服務(wù)器來(lái)提高吞吐量,菜菜覺(jué)得是應(yīng)該優(yōu)先采用的。畢竟一臺(tái)服務(wù)器的費(fèi)用相比較一個(gè)程序員費(fèi)用來(lái)說(shuō)要低的多。但是有一個(gè)前提,就是你的服務(wù)器是系統(tǒng)的瓶頸,網(wǎng)站系統(tǒng)之后的其他系統(tǒng)并非瓶頸。如果你的系統(tǒng)的瓶頸在DB或者其他服務(wù),盲目的增加服務(wù)器并不能解決你的問(wèn)題。
????????通過(guò)增加服務(wù)器來(lái)解決你的網(wǎng)站瓶頸,意味著你的網(wǎng)站需要做負(fù)載均衡,如果沒(méi)有運(yùn)維相關(guān)人員,你可能還得需要研究負(fù)載均衡的方案,比如LVS,Nginx,F5等。我曾經(jīng)面試過(guò)很多入道不久的同學(xué),就提高吞吐量問(wèn)題,如果沒(méi)有回答上用負(fù)載均衡方案的基本都pass了,不要說(shuō)別的,這個(gè)方案就是一個(gè)基礎(chǔ),就好比學(xué)習(xí)一個(gè)語(yǔ)言,你連最基本的語(yǔ)法都不會(huì),我憑什么讓你通過(guò)。有噴的同學(xué)可以留言哦
????????當(dāng)一個(gè)請(qǐng)求到達(dá)服務(wù)器并且正確的被服務(wù)器接收之后,最終執(zhí)行這個(gè)請(qǐng)求的載體是一個(gè)線程。當(dāng)一個(gè)線程被cpu載入執(zhí)行其指令的時(shí)候,在同步的狀態(tài)下,當(dāng)前線程會(huì)阻塞在那里等待cpu結(jié)果,如果cpu執(zhí)行的是比較慢的IO操作,線程會(huì)一直被阻塞閑置很長(zhǎng)時(shí)間,這里的很長(zhǎng)是對(duì)比cpu的速度而言,如果你想有一個(gè)直觀的速度對(duì)比,可以去查看菜菜以前的文章:
高并發(fā)下為什么更喜歡進(jìn)程內(nèi)緩存
????????當(dāng)一個(gè)新的請(qǐng)求到來(lái)的時(shí)候,如果沒(méi)有新的線程去領(lǐng)取這個(gè)任務(wù)并執(zhí)行,要么會(huì)發(fā)生異常,要么創(chuàng)建新的線程。線程是一種很稀缺的資源,不可能無(wú)限制的創(chuàng)建。這種情況下我們就要把線程這種資源充分利用起來(lái),不要讓線程停下來(lái)。這也是程序推薦采用異步的原因,試想,一個(gè)線程不停的在工作,遇到比較慢的IO不會(huì)去等待結(jié)果,而是接著處理下一個(gè)請(qǐng)求,當(dāng)IO的結(jié)果返回來(lái)得到通知的時(shí)候,線程再去取IO結(jié)果,豈不是能在相同時(shí)間內(nèi)處理更多的請(qǐng)求。
????????還有一點(diǎn),盡量減少線程上線文在cpu的切換,因?yàn)榫€程上線文切換的成本也是比較大的,在線程切換的時(shí)候,cpu需要把當(dāng)前線程的上下文信息記錄下來(lái)用以下次調(diào)用的時(shí)候使用,然后把新線程的上下文信息載入然后執(zhí)行。這個(gè)過(guò)程相對(duì)于cpu的執(zhí)行速度而言,要慢很多。
????????不要拿Golang反駁以上觀點(diǎn),golang的協(xié)程雖然是用戶級(jí)別比線程更小的載體,但是最終和Cpu進(jìn)行交互的還是線程。
????????在講cpu級(jí)別之前,如果有一定的網(wǎng)絡(luò)模型的基礎(chǔ),也許會(huì)好一些。這里大體闡述一下,現(xiàn)代操作系統(tǒng)都采用虛擬尋址的方式,它的尋址空間(虛擬存儲(chǔ)空間)為4G(2的32次方)。操作系統(tǒng)將虛擬空間分為兩類:內(nèi)核空間和用戶空間。內(nèi)核空間獨(dú)立于用戶空間,有訪問(wèn)受保護(hù)的內(nèi)存空間、IO設(shè)備的權(quán)限(所有的用戶空間共享)。用戶空間就是我們的應(yīng)用程序運(yùn)行的空間,其實(shí)用戶空間并沒(méi)有操作各種IO設(shè)備的權(quán)限,像我們平時(shí)讀取一個(gè)文件,本質(zhì)上是委托內(nèi)核空間去執(zhí)行讀取指令的,內(nèi)核空間讀取到數(shù)據(jù)之后再把數(shù)據(jù)復(fù)制到程序運(yùn)行的空間,最后應(yīng)用程序再把數(shù)據(jù)返回調(diào)用方。
????????通過(guò)上圖大體可以看出,內(nèi)核會(huì)為每個(gè)I/O設(shè)備維護(hù)一個(gè)buffer(同一個(gè)文件描述符讀和寫的buffer不同),應(yīng)用程序發(fā)出一個(gè)IO操作的指令其實(shí)通過(guò)了內(nèi)核空間和用戶空間兩個(gè)部分,并且發(fā)生了數(shù)據(jù)的復(fù)制操作。這個(gè)過(guò)程其實(shí)主要包含兩個(gè)步驟:
1. 用戶進(jìn)程發(fā)出操作指令并等待數(shù)據(jù)
2. 內(nèi)核把數(shù)據(jù)返回給用戶進(jìn)程(buffer的復(fù)制操作)
????????根據(jù)這兩個(gè)操作的不同表現(xiàn),所以IO模型有了同步阻塞,同步非阻塞,異步阻塞,異步非阻塞的概念,但是這里并非此文的重點(diǎn),所以不在展開詳細(xì)介紹。
????????利用cpu提高系統(tǒng)吞吐量主要目標(biāo)是提高單位時(shí)間內(nèi)cpu運(yùn)行的指令數(shù),避免cpu做一些無(wú)用功:
cpu負(fù)責(zé)把buffer的數(shù)據(jù)copy到應(yīng)用程序空間,應(yīng)用程序再把數(shù)據(jù)返回給調(diào)用方,假如這個(gè)過(guò)程發(fā)生的是一次Socket操作,應(yīng)用程序在得到IO返回?cái)?shù)據(jù)之后,還需要網(wǎng)卡把數(shù)據(jù)返回給client端,這個(gè)過(guò)程又需要把剛剛得到的buffer數(shù)據(jù)再次通過(guò)內(nèi)核發(fā)送至網(wǎng)卡,通過(guò)網(wǎng)絡(luò)傳送出去。由此可見cpu把buffer數(shù)據(jù)copy到應(yīng)用程序空間這個(gè)過(guò)程完全沒(méi)有必要,在內(nèi)核空間完全可以把buffer數(shù)據(jù)直接傳輸至網(wǎng)卡,這也是零拷貝技術(shù)要解決的問(wèn)題。具體的零拷貝技術(shù)在這里不再展開。
????????至于網(wǎng)絡(luò)傳輸級(jí)別,由于協(xié)議大部分是Tcp/ip,所以在協(xié)議傳輸方面優(yōu)化的手段比較少,但是應(yīng)用程序級(jí)別協(xié)議可以選擇壓縮率更好的,比如采用grpc會(huì)比單純的http協(xié)議要好很多,http2 要比http 1.1要好很多。另外一方面網(wǎng)卡盡量加大傳輸速率,比如千兆網(wǎng)卡要比百兆網(wǎng)卡速度更快。由于網(wǎng)絡(luò)傳輸比較偏底層,所以人工干預(yù)的切入點(diǎn)會(huì)少很多。
????????大部分程序員都是工作在應(yīng)用層,針對(duì)應(yīng)用級(jí)別代碼能提高吞吐量的建議:
1加大應(yīng)用的進(jìn)程數(shù),增加并發(fā)數(shù),特別在進(jìn)程數(shù)是瓶頸的情況下
2優(yōu)化線程調(diào)用,盡量池化。
3應(yīng)用的代碼異步化,特別是異步非阻塞式編程對(duì)于提高吞吐量效果特別明顯
4充分利用多核cpu優(yōu)勢(shì),實(shí)現(xiàn)并行編程。
5減少每個(gè)調(diào)用的響應(yīng)時(shí)間,縮短調(diào)用鏈。例如通過(guò)加索引的方式來(lái)減少訪問(wèn)一次數(shù)據(jù)庫(kù)的時(shí)間
希望大家有所收獲 --菜菜
恭喜 安淺? ?QI 兩位喜提上周抽獎(jiǎng)Golang書籍。
互聯(lián)網(wǎng)之路,菜菜與君一同成長(zhǎng)
長(zhǎng)按識(shí)別二維碼關(guān)注
你點(diǎn)的每個(gè)在看,我都認(rèn)真當(dāng)成了喜總結(jié)
以上是生活随笔為你收集整理的程序员修神之路--提高网站的吞吐量的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 为什么我们要做单元测试?(二)
- 下一篇: 一份.NET 容器化的调查小结