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