互聯(lián)網(wǎng)時代,高并發(fā)是一個老生常談的話題。無論對于一個Web站點還是App應(yīng)用,高峰時能承載的并發(fā)請求都是衡量一個系統(tǒng)性能的關(guān)鍵標(biāo)志。像阿里雙十一頂住了上億的峰值請求、訂單也確實體現(xiàn)了阿里的技術(shù)水平(當(dāng)然有錢也是一個原因)。 那么,何為系統(tǒng)負載能力?怎么衡量?相關(guān)因素有哪些?又如何優(yōu)化呢? 一. 衡量指標(biāo)用什么來衡量一個系統(tǒng)的負載能力呢?有一個概念叫做每秒請求數(shù)(Requests per second),指的是每秒能夠成功處理請求的數(shù)目。比如說,你可以配置Tomcat服務(wù)器的maxConnection為無限大,但是受限于服務(wù)器系統(tǒng)或者硬件限制,很多請求是不會在一定的時間內(nèi)得到響應(yīng)的,這并不作為一個成功的請求,其中成功得到響應(yīng)的請求數(shù)即為每秒請求數(shù),反應(yīng)出系統(tǒng)的負載能力。 通常的,對于一個系統(tǒng),增加并發(fā)用戶數(shù)量時每秒請求數(shù)量也會增加。然而,我們最終會達到這樣一個點,此時并發(fā)用戶數(shù)量開始“壓倒”服務(wù)器。如果繼續(xù)增加并發(fā)用戶數(shù)量,每秒請求數(shù)量開始下降,而反應(yīng)時間則會增加。這個并發(fā)用戶數(shù)量開始“壓倒”服務(wù)器的臨界點非常重要,此時的并發(fā)用戶數(shù)量可以認為是當(dāng)前系統(tǒng)的最大負載能力。 二. 相關(guān)因素一般的,和系統(tǒng)并發(fā)訪問量相關(guān)的幾個因素如下:
其中,帶寬和硬件配置是決定系統(tǒng)負載能力的決定性因素。這些只能依靠擴展和升級提高。我們需要重點關(guān)注的是在一定帶寬和硬件配置的基礎(chǔ)上,怎么使系統(tǒng)的負載能力達到最大。 2.1 帶寬毋庸置疑,帶寬是決定系統(tǒng)負載能力的一個至關(guān)重要的因素,就好比水管一樣,細的水管同一時間通過的水量自然就少(這個比喻解釋帶寬可能不是特別合適)。一個系統(tǒng)的帶寬首先就決定了這個系統(tǒng)的負載能力,其單位為Mbps,表示數(shù)據(jù)的發(fā)送速度。 2.2 硬件配置系統(tǒng)部署所在的服務(wù)器的硬件決定了一個系統(tǒng)的最大負載能力,也是上限。一般說來,以下幾個配置起著關(guān)鍵作用:
很多系統(tǒng)的架構(gòu)設(shè)計、系統(tǒng)優(yōu)化,最終都會加上這么一句:使用SSD存儲解決了這些問題。 可見,硬件配置是決定一個系統(tǒng)的負載能力的最關(guān)鍵因素。 2.3 系統(tǒng)配置一般來說,目前后端系統(tǒng)都是部署在Linux主機上的。所以拋開Win系列不談,對于Linux系統(tǒng)來說一般有以下配置關(guān)系著系統(tǒng)的負載能力。
2.3.1 文件描述符數(shù)限制
通過讀取/proc/sys/fs/file-nr可以看到當(dāng)前使用的文件描述符總數(shù)。另外,對于文件描述符的配置,需要注意以下幾點:
2.3.2 進程/線程數(shù)限制
2.3.3 TCP內(nèi)核參數(shù)在一臺服務(wù)器CPU和內(nèi)存資源額定有限的情況下,最大的壓榨服務(wù)器的性能,是最終的目的。在節(jié)省成本的情況下,可以考慮修改Linux的內(nèi)核TCP/IP參數(shù),來最大的壓榨服務(wù)器的性能。如果通過修改內(nèi)核參數(shù)也無法解決的負載問題,也只能考慮升級服務(wù)器了,這是硬件所限,沒有辦法的事。
使用上面的命令,可以得到當(dāng)前系統(tǒng)的各個狀態(tài)的網(wǎng)絡(luò)連接的數(shù)目。如下:
這里,TIME_WAIT的連接數(shù)是需要注意的一點。此值過高會占用大量連接,影響系統(tǒng)的負載能力。需要調(diào)整參數(shù),以盡快的釋放time_wait連接。 一般TCP相關(guān)的內(nèi)核參數(shù)在/etc/sysctl.conf文件中。為了能夠盡快釋放time_wait狀態(tài)的連接,可以做以下配置:
這里需要注意的一點就是當(dāng)打開了tcp_tw_recycle,就會檢查時間戳,移動環(huán)境下的發(fā)來的包的時間戳有些時候是亂跳的,會把帶了“倒退”的時間戳的包當(dāng)作是“recycle的tw連接的重傳數(shù)據(jù),不是新的請求”,于是丟掉不回包,造成大量丟包。另外,當(dāng)前面有LVS,并且采用的是NAT機制時,開啟tcp_tw_recycle會造成一些異常,可見:http://www./?p=416。如果這種情況下仍然需要開啟此選項,那么可以考慮設(shè)置net.ipv4.tcp_timestamps=0,忽略掉報文的時間戳即可。 此外,還可以通過優(yōu)化tcp/ip的可使用端口的范圍,進一步提升負載能力,如下:
2.4 應(yīng)用服務(wù)器配置說到應(yīng)用服務(wù)器配置,這里需要提到應(yīng)用服務(wù)器的幾種工作模式,也叫并發(fā)策略。
前三者是傳統(tǒng)應(yīng)用服務(wù)器Apache和Tomcat采用的方式,最后一種是Nginx采用的方式。當(dāng)然這里需要注意的是應(yīng)用服務(wù)器和nginx這種做反向代理服務(wù)器(暫且忽略nginx+cgi做應(yīng)用服務(wù)器的功能)的區(qū)別。應(yīng)用服務(wù)器是需要處理應(yīng)用邏輯的,有時候是耗cup資源的;而反向代理主要用作IO,是IO密集型的應(yīng)用。使用事件驅(qū)動的這種網(wǎng)絡(luò)模型,比較適合IO密集型應(yīng)用,而并不適合CPU密集型應(yīng)用。對于后者,多進程/線程則是一個更好地選擇。 當(dāng)然,由于Nginx采用的基于事件驅(qū)動的多路IO復(fù)用的模型,其作為反向代理服務(wù)器時,可支持的并發(fā)是非常大的。淘寶Tengine團隊曾有一個測試結(jié)果是“24G內(nèi)存機器上,處理并發(fā)請求可達200萬”。 2.4.1 Nginx/TengineNgixn是目前使用最廣泛的反向代理軟件,而Tengine是阿里開源的一個加強版Nginx,其基本實現(xiàn)了Nginx收費版本的一些功能,如:主動健康檢查、session sticky等。對于Nginx的配置,需要注意的有這么幾點:
典型配置可見:https://github.com/superhj1987/awesome-config/blob/master/nginx/nginx.conf 2.4.2 TomcatTomcat的關(guān)鍵配置總體上有兩大塊:jvm參數(shù)配置和connector參數(shù)配置。
對于tomcat這里有一個爭論就是:使用大內(nèi)存Tomcat好還是多個小的Tomcat集群好?(針對64位服務(wù)器以及Tomcat來說) 其實,這個要根據(jù)業(yè)務(wù)場景區(qū)別對待的。通常,大內(nèi)存Tomcat有以下問題:
因此,如果可以保證一定程度上程序的對象大部分都是朝生夕死的,老年代不會發(fā)生gc,那么使用大內(nèi)存Tomcat也是可以的。但是在伸縮性和高可用卻比不上使用小內(nèi)存(相對來說)Tomcat集群。 使用小內(nèi)存Tomcat集群則有以下優(yōu)勢:
2.4.3 數(shù)據(jù)庫
MySQL是目前最常用的關(guān)系型數(shù)據(jù)庫,支持復(fù)雜的查詢。但是其負載能力一般,很多時候一個系統(tǒng)的瓶頸就發(fā)生在MySQL這一點,當(dāng)然有時候也和SQL語句的效率有關(guān)。比如,牽扯到聯(lián)表的查詢一般說來效率是不會太高的。影響數(shù)據(jù)庫性能的因素一般有以下幾點:
拋開以上因素,當(dāng)數(shù)據(jù)量單表突破千萬甚至百萬時(和具體的數(shù)據(jù)有關(guān)),需要對mysql數(shù)據(jù)庫進行優(yōu)化,一種常見的方案就是分表:
此外,對于數(shù)據(jù)庫,可以使用讀寫分離的方式提高性能,尤其是對那種讀頻率遠大于寫頻率的業(yè)務(wù)場景。這里一般采用master/slave的方式實現(xiàn)讀寫分離,前面用程序控制或者加一個Proxy層??梢赃x擇使用MySQL Proxy,編寫lua腳本來實現(xiàn)基于Proxy的MySQL讀寫分離;也可以通過程序來控制,根據(jù)不同的SQL語句選擇相應(yīng)的數(shù)據(jù)庫來操作,這個也是筆者公司目前在用的方案。由于此方案和業(yè)務(wù)強綁定,是很難有一個通用的方案的,其中比較成熟的是阿里的TDDL,但是由于未全部開源且對其他組件有依賴性,不推薦使用。 現(xiàn)在很多大的公司對這些分表、主從分離、分布式都基于MySQL做了自己的二次開發(fā),形成了自己公司的一套分布式數(shù)據(jù)庫系統(tǒng)。比如阿里的Cobar、網(wǎng)易的DDB、360的Atlas等。當(dāng)然,很多大公司也研發(fā)了自己的MySQL分支,比較出名的就是姜承堯帶領(lǐng)研發(fā)的InnoSQL。
當(dāng)然,對于系統(tǒng)中并發(fā)很高并且訪問很頻繁的數(shù)據(jù),關(guān)系型數(shù)據(jù)庫還是不能妥妥應(yīng)對。這時候就需要緩存數(shù)據(jù)庫出馬以隔離對MySQL的訪問,防止MySQL崩潰。 其中,Redis是目前用的比較多的緩存數(shù)據(jù)庫(當(dāng)然,也有直接把Redis當(dāng)做數(shù)據(jù)庫使用的)。Redis是單線程基于內(nèi)存的數(shù)據(jù)庫,讀寫性能遠遠超過MySQL。一般情況下,對Redis做讀寫分離主從同步就可以應(yīng)對大部分場景的應(yīng)用。但是這樣的方案缺少HA,尤其對于分布式應(yīng)用,是不可接受的。目前,Redis集群的實現(xiàn)方案有以下幾個:
2.5 系統(tǒng)架構(gòu)影響性能的系統(tǒng)架構(gòu)一般會有這幾方面:
2.5.1 負載均衡負載均衡在服務(wù)端領(lǐng)域中是一個很關(guān)鍵的技術(shù)。可以分為以下兩種:
其中,硬件負載均衡的性能無疑是最優(yōu)的,其中以F5為代表。但是,與高性能并存的是其成本的昂貴。所以對于很多初創(chuàng)公司來說,一般是選用軟件負載均衡的方案。 軟件負載均衡中又可以分為四層負載均衡和七層負載均衡。上文在應(yīng)用服務(wù)器配置部分講了nginx的反向代理功能即七層的一種成熟解決方案,主要針對的是七層http協(xié)議(雖然最新的發(fā)布版本已經(jīng)支持四層負載均衡)。對于四層負載均衡,目前應(yīng)用最廣泛的是lvs。其是阿里的章文嵩博士帶領(lǐng)的團隊所研發(fā)的一款linux下的負載均衡軟件,本質(zhì)上是基于iptables實現(xiàn)的。分為三種工作模式:
三種模式各有優(yōu)缺點,目前還有阿里開源的一個FULL NAT是在NAT原來的DNAT上加入了SNAT的功能。 此外,haproxy也是一款常用的負載均衡軟件。但限于對此使用較少,在此不做講述。 2.5.2 同步 or 異步對于一個系統(tǒng),很多業(yè)務(wù)需要面對使用同步機制或者是異步機制的選擇。比如,對于一篇帖子,一個用戶對其分享后,需要記錄用戶的分享記錄。如果你使用同步模式(分享的同時記錄此行為),那么響應(yīng)速度肯定會受到影響。而如果你考慮到分享過后,用戶并不會立刻去查看自己的分享記錄,犧牲這一點時效性,可以先完成分享的動作,然后異步記錄此行為,會提高分享請求的響應(yīng)速度(當(dāng)然,這里可能會有事務(wù)準(zhǔn)確性的問題)。有時候在某些業(yè)務(wù)邏輯上,在充分理解用戶訴求的基礎(chǔ)上,是可以犧牲某些特性來滿足用戶需求的。 這里值得一提的是,很多時候?qū)τ谝粋€業(yè)務(wù)流程,是可以拆開劃分為幾個步驟的,然后有些步驟完全可以異步并發(fā)執(zhí)行,能夠極大提高處理速度。 2.5.3 28原則對于一個系統(tǒng),20%的功能會帶來80%的流量。這就是28原則的意思,當(dāng)然也是我自己的一種表述。因此在設(shè)計系統(tǒng)的時候,對于80%的功能,其面對的請求壓力是很小的,是沒有必要進行過度設(shè)計的。但是對于另外20%的功能則是需要設(shè)計再設(shè)計、reivew再review,能夠做負載均衡就做負載均衡,能夠緩存就緩存,能夠做分布式就分布式,能夠把流程拆開異步化就異步化。 當(dāng)然,這個原則適用于生活中很多事物。 三. 一般架構(gòu)一般的Java后端系統(tǒng)應(yīng)用架構(gòu)如下圖所示:LVS+Nginx+Tomcat+MySql/DDB+Redis/Codis 其中,虛線部分是數(shù)據(jù)庫層,采用的是主從模式。也可以使用redis cluster(codis等)以及mysql cluster(Cobar等)來替換。 作者:颯然Hang,架構(gòu)師/后端工程師,working@中華萬年歷 |
|