网站加速教程--提升性能的同时节约10倍成本
從基本HTTP協(xié)議,服務(wù)器編寫(xiě)(只講思路),到完整系統(tǒng)搭建(包括負(fù)載均衡LVS,IDC分布,DNS解析),到瀏覽器緩存的使用(重點(diǎn)講述),結(jié)合線上實(shí)例圖文講解如何以最低廉的成本構(gòu)建快速,高并發(fā),高可用,可擴(kuò)展的Web服務(wù)。最后將拿本公司一些線上產(chǎn)品做實(shí)例分析。
如果能靈活應(yīng)用這些方法,你也許會(huì)發(fā)現(xiàn)對(duì)于某些網(wǎng)站來(lái)說(shuō)節(jié)約10倍成本,其實(shí)是個(gè)很保守的說(shuō)法。
提綱:
一,服務(wù)器編寫(xiě)篇
二,系統(tǒng)架構(gòu)篇
三,Cache為王篇
四,實(shí)例分析篇
網(wǎng)站加速--服務(wù)器編寫(xiě)篇
一,如何節(jié)約CPU
二,怎樣使用內(nèi)存
三,減少磁盤(pán)I/O
四,優(yōu)化你的網(wǎng)卡
五,調(diào)整內(nèi)核參數(shù)
六,衡量Web Server的性能指標(biāo)
七,NBA js直播的發(fā)展歷程
八,新浪財(cái)經(jīng)實(shí)時(shí)行情系統(tǒng)的歷史遺留問(wèn)題 (7 byte = 10.68w RMB/year)
? -----------------------------------------------------------------------------------------
一,如何節(jié)約CPU
1,選擇一個(gè)好的I/O模型(epoll, kqueue)
3年前,我們還關(guān)心c10k問(wèn)題,隨著硬件性能的提升,那已經(jīng)不成問(wèn)題,但如果想讓PIII 900服務(wù)器支撐5w+ connections,還是需要些能耐的。
epoll最擅長(zhǎng)的事情是監(jiān)視大量閑散連接,批量返回可用描述符,這讓單機(jī)支撐百萬(wàn)connections成為可能。linux 2.6以上開(kāi)始支持epoll,freebsd上相應(yīng)的有kqueue,不過(guò)我個(gè)人偏愛(ài)linux,不太關(guān)心kqueue。
邊緣觸發(fā)ET 和 水平觸發(fā)LT 的選擇:
早期的文檔說(shuō)ET很高效,但是有些冒進(jìn)。但事實(shí)上LT使用過(guò)程中,我苦惱了將近一個(gè)月有余,一不留神CPU 利用率99%了,可能是我沒(méi)處理好。后來(lái)zhongying同學(xué)幫忙把驅(qū)動(dòng)模式改成了ET模式,ET既高效又穩(wěn)定。
簡(jiǎn)單地說(shuō),如果你有數(shù)據(jù)過(guò)來(lái)了,不去取LT會(huì)一直騷擾你,提醒你去取,而ET就告訴你一次,愛(ài)取不取,除非有新數(shù)據(jù)到來(lái),否則不再提醒。
重點(diǎn)說(shuō)下ET,非阻塞模式,
man手冊(cè)說(shuō),如果ET提示你有數(shù)據(jù)可讀的時(shí)候,你應(yīng)該連續(xù)的讀一直讀到返回 EAGAIN or EWOULDBLOCK 為止,但是在具體實(shí)現(xiàn)中,我并沒(méi)有這樣做,而是根據(jù)我的應(yīng)用做了優(yōu)化。因?yàn)楝F(xiàn)在操作系統(tǒng)絕大多數(shù)實(shí)現(xiàn)都是最大傳輸單元值為1500。? MTU:1500 - ipheader:20 - tcpheader:20 = 1460 byte .?
HTTP header,不帶cookie的話一般只有500+ byte。留512給uri,也基本夠用,還有節(jié)余。
如果請(qǐng)求的header恰巧比這大是2050字節(jié)呢?
會(huì)有兩種情況發(fā)生:1,數(shù)據(jù)緊挨著同時(shí)到達(dá),一次read就搞定。 2,分兩個(gè)ethernet frame先后到達(dá)有一定時(shí)間間隔。
我的方法是,用一個(gè)比較大的buffer比如1M去讀header,如果你很確信你的服務(wù)對(duì)象請(qǐng)求比1460小,讀一次就行。如果請(qǐng)求會(huì)很大分幾個(gè)ethernet frame先后到達(dá),也就是恰巧你剛剛read過(guò),它又來(lái)一個(gè)新數(shù)據(jù)包,ET會(huì)再次返回,再處理下就是了。
順便再說(shuō)下寫(xiě)數(shù)據(jù),一般一次可以write十幾K數(shù)據(jù)到內(nèi)核緩沖區(qū)。
所以對(duì)于很多小的數(shù)據(jù)文件服務(wù)來(lái)說(shuō),是沒(méi)有必要另外為每個(gè)connections分配發(fā)送緩沖區(qū)。
只有當(dāng)一次發(fā)送不完時(shí)候才分配一塊內(nèi)存,將數(shù)據(jù)暫存,待下次返回可寫(xiě)時(shí)發(fā)送。
這樣避免了一次內(nèi)存copy,而且節(jié)約了內(nèi)存。
選擇了epoll并不代表就就擁有了一個(gè)好的 I/O模型,用的不好,你還趕不上select,這是實(shí)話。
epoll的問(wèn)題我就說(shuō)這么多,關(guān)于描述符管理方面的細(xì)節(jié)請(qǐng)參見(jiàn)我早期的一個(gè)帖子,epoll模型的使用及其描述符耗盡問(wèn)題的探討? 大概討論了18頁(yè),我剛才把解決方法放在第一個(gè)帖子里了。如果你對(duì)epoll有感興趣,我這有 一個(gè)簡(jiǎn)單的基于epoll的web server例子 。
另外你要使用多線程,還是多進(jìn)程,這要看你更熟悉哪個(gè),各有好處。
多進(jìn)程模式,單個(gè)進(jìn)程crash了,不影響其他進(jìn)程,而且可以為每個(gè)worker分別幫定不同的cpu,讓某些cpu單獨(dú)空出來(lái)處理中斷和系統(tǒng)事物。多線程,共享數(shù)據(jù)方便,占用資源更少。進(jìn)程或線程的個(gè)數(shù),應(yīng)該固定在 (cpu核數(shù)-1) ~ 2倍cpu核數(shù)間為宜,太多了時(shí)間片輪轉(zhuǎn)時(shí)會(huì)頻繁切換,少了,達(dá)不到多核并發(fā)處理的效果。
還有如何accept也是一門(mén)學(xué)問(wèn),沒(méi)有最好,只有更適用,你需要做很多實(shí)驗(yàn),確定對(duì)自己最高效的方式。有了一個(gè)好的I/O框架,你的效率想低也不容易,這是程序?qū)崿F(xiàn)的大局。
關(guān)于更多網(wǎng)絡(luò)I/O模型的討論請(qǐng)見(jiàn) <Scalable Network Programming > 中文版。
另外,必須強(qiáng)調(diào)的是,代碼和結(jié)構(gòu)應(yīng)該簡(jiǎn)潔高效,一定要具體問(wèn)題具體分析,沒(méi)什么法則是萬(wàn)能的,要根據(jù)你的服務(wù)量身定做。
2,關(guān)閉不必要的標(biāo)準(zhǔn)輸入和標(biāo)準(zhǔn)輸出
close(0);? //stdin
close(1);? //stdout
如果你不小心,有了printf輸出調(diào)試信息,這絕對(duì)是一個(gè)性能殺手。
一個(gè)高性能的服務(wù)器不出錯(cuò)是不應(yīng)該有任何輸出的,免得耽誤干活。
這樣做,至少能為你節(jié)約兩個(gè)描述符資源。
3,避免用鎖 (i++ or ++i )
多線程編程用鎖是普遍現(xiàn)象,貌似已經(jīng)成為習(xí)慣。
但各線程最好是獨(dú)立的,不需要同步機(jī)制的。
鎖會(huì)消耗資源,而且造成排隊(duì),甚至死鎖,盡量想辦法避免。
非用不可時(shí)候,比如,實(shí)時(shí)統(tǒng)計(jì)各線程的負(fù)載情況,多個(gè)線程要對(duì)全局變量進(jìn)行寫(xiě)操作。
請(qǐng)用 ++i ,因?yàn)樗且粋€(gè)原子操作。
4,減少系統(tǒng)調(diào)用
系統(tǒng)調(diào)用是很耗的,因?yàn)樗ǔP枰@進(jìn)內(nèi)核再鉆出來(lái)。
我們應(yīng)該避免用戶空間和內(nèi)核空間的切換。
比如我要為每個(gè)請(qǐng)求打個(gè)時(shí)間戳,以計(jì)算超時(shí),我完全可以在返回一批可用描述符前只調(diào)用一次time(),而不用每個(gè)請(qǐng)求都調(diào)用一次。 time()只精確到秒,一批請(qǐng)求處理都是毫秒級(jí),所以也沒(méi)必要那么做,再說(shuō)了,計(jì)算超時(shí)誤差那么一秒有什么影響嗎?
5, Connection: close vs? Keep-Alive ?
談httpd實(shí)現(xiàn),就不能不提長(zhǎng)連接Keep-Alive 。
Keep-Alive是http 1.1中加入的,現(xiàn)在的瀏覽器99。99%應(yīng)該都是支持Keep-Alive的。
先說(shuō)下什么是Keep-Alive:
這是基于tcp的connections說(shuō)的,也就是一個(gè)描述符(fd),它并不代表獨(dú)立占用一個(gè)進(jìn)程或線程。一個(gè)線程用非阻塞模式可以保持成千上萬(wàn)個(gè)長(zhǎng)連接。
先說(shuō)一個(gè)完整的HTTP 1.0的請(qǐng)求和響應(yīng):
建立tcp連接 (syn; ack, syn2; ack2; 三個(gè)分組握手完成)
請(qǐng)求
響應(yīng)
關(guān)閉連接 (fin; ack; fin2; ack2? 四個(gè)分組關(guān)閉連接)
再說(shuō)HTTP 1.1的請(qǐng)求和響應(yīng):
建立tcp連接 (syn; ack, syn2; ack2; 三個(gè)分組握手完成)
請(qǐng)求
響應(yīng)
...
...
請(qǐng)求
響應(yīng)
關(guān)閉連接 (fin; ack; fin2; ack2? 四個(gè)分組關(guān)閉連接)
如果請(qǐng)求和響應(yīng)都只有一個(gè)分組,那么HTTP 1.0至少要傳輸11個(gè)分組(補(bǔ)充:請(qǐng)求和響應(yīng)數(shù)據(jù)還各需要一個(gè)ack確認(rèn)),才拿到一個(gè)分組的數(shù)據(jù)。而長(zhǎng)連接可以更充分的利用這個(gè)已經(jīng)建立的連接,避免的頻繁的建立和關(guān)閉連接,減少網(wǎng)絡(luò)擁塞。
我做過(guò)一個(gè)測(cè)試,在2cpu*4core服務(wù)器上,不停的accept,然后不做處理,直接close掉。一秒最多可以accept? 7w/s,這是極限。那么我要是想每秒處理10w以上的http請(qǐng)求該怎么辦呢?
目前唯一的也是最好的選擇,就是保持長(zhǎng)連接。
比如我們NBA JS直播頁(yè)面,剛打開(kāi)就會(huì)向我的js服務(wù)器發(fā)出6個(gè)http請(qǐng)求,而且隨后平均每10秒會(huì)產(chǎn)生兩個(gè)請(qǐng)求。再比如,我們很多頁(yè)面都會(huì)嵌幾個(gè)靜態(tài)池的圖片,如果每個(gè)請(qǐng)求都是獨(dú)立的(建立連接然后關(guān)閉),那對(duì)資源絕對(duì)是個(gè)浪費(fèi)。
長(zhǎng)連接是個(gè)好東西,但是選擇 Keep-Alive必須根據(jù)你的應(yīng)用決定。比如NBA JS直播,我肯定10秒內(nèi)會(huì)產(chǎn)生一個(gè)請(qǐng)求,所以超時(shí)設(shè)置為15秒,15秒還沒(méi)活動(dòng),估計(jì)是去打醬油了,資源就得被我回收。超時(shí)設(shè)置過(guò)長(zhǎng),光連接都能把你的服務(wù)器堆死。
為什么有些apache服務(wù)器,負(fù)載很高,把Keep-Alive關(guān)掉負(fù)載就減輕了呢?
apache 有兩種工作模式,prefork和worker。apache 1.x只有,prefork。
prefork比較典型,就是個(gè)進(jìn)程池,每次創(chuàng)建一批進(jìn)程,還有apache是基于select實(shí)現(xiàn)的。在用戶不是太多的時(shí)候,長(zhǎng)連接還是很有用的,可以節(jié)約分組,提升響應(yīng)速度,但是一旦超出某個(gè)平衡點(diǎn),由于為了保持很多長(zhǎng)連接,創(chuàng)建了太多的進(jìn)程,導(dǎo)致系統(tǒng)不堪重負(fù),內(nèi)存不夠了,開(kāi)始換入換出,cpu也被很多進(jìn)程吃光了,load上去了。這種情況下,對(duì)apache來(lái)說(shuō),每次請(qǐng)求重新建立連接要比保持這么多長(zhǎng)連接和進(jìn)程更劃算。
6,預(yù)處理 (預(yù)壓縮,預(yù)取lastmodify,mimetype)
預(yù)處理,原則就是,能預(yù)先知道的結(jié)果,我們絕不計(jì)算第二次。
預(yù)壓縮:我們?cè)趦扇昵熬烷_(kāi)始使用預(yù)壓縮技術(shù),以節(jié)約CPU,偉大的微軟公司在現(xiàn)在的IIS 7中也開(kāi)始使用了。所謂的預(yù)壓縮就是,從數(shù)據(jù)源頭提供的就是預(yù)先壓縮好的數(shù)據(jù),IDC同步傳輸中是壓縮狀態(tài),直到最后web server輸出都是壓縮狀態(tài),最終被用戶瀏覽器端自動(dòng)解壓。
預(yù)取lastmodify:? 文件的lastmodify時(shí)間,如果不更新,我們不應(yīng)該取第二次,別忘記了fsat這個(gè)系統(tǒng)調(diào)用是很耗的。
預(yù)取mimetype: mimetype,如果你的文件類型不超過(guò)256種,一個(gè)字節(jié)就可以標(biāo)識(shí)它,然后用數(shù)組下標(biāo)直接輸出,而且不是看到一個(gè)js文件,然后strcmp()了近百種后綴名后,才知道應(yīng)該輸出Content-Type: application/x-javascript,而且這種方法會(huì)隨文件類型增加而耗費(fèi)更多cpu資源。當(dāng)然也可以寫(xiě)個(gè)hash函數(shù)來(lái)做這事,那也至少需要一次函數(shù)調(diào)用,做些求值運(yùn)算,和分配比實(shí)際數(shù)據(jù)大幾倍的hash表。
如何更好的使用cpu一級(jí)緩存
數(shù)據(jù)分解
CPU硬親和力的設(shè)置
待補(bǔ)充。。。。
二,怎樣使用內(nèi)存
1,避免內(nèi)存copy (strcpy,memcpy)
雖然內(nèi)存速度很快,但是執(zhí)行頻率比較高的核心部分能避免copy的就盡量別使用。如果必須要copy,盡量使用memcpy替代sprintf,strcpy,因?yàn)樗魂P(guān)心你是否遇到'\0'; 內(nèi)存拷貝和http響應(yīng)又涉及到字符串長(zhǎng)度計(jì)算。如果能預(yù)先知道這個(gè)長(zhǎng)度最好用中間變量保留,增加多少直接加上去,不要用strlen()去計(jì)算,因?yàn)樗鼤?huì)數(shù)數(shù)直到遇見(jiàn)'\0'。能用sizeof()的地方就不要用strlen,因?yàn)樗莻€(gè)運(yùn)算符,在預(yù)編的時(shí)被替換為具體數(shù)字,而非執(zhí)行時(shí)計(jì)算。
2,避免內(nèi)核空間和用戶進(jìn)程空間內(nèi)存copy (sendfile, splice and tee)
sendfile: 它的威力在于,它為大家提供了一種訪問(wèn)當(dāng)前不斷膨脹的Linux網(wǎng)絡(luò)堆棧的機(jī)制。這種機(jī)制叫做“零拷貝(zero-copy)”,這種機(jī)制可以把“傳輸控制協(xié)議(TCP)”框架直接的從主機(jī)存儲(chǔ)器中傳送到網(wǎng)卡的緩存塊(network card buffers)中去,避免了兩次上下文切換。詳細(xì)參見(jiàn) <使用sendfile()讓數(shù)據(jù)傳輸?shù)玫阶顑?yōu)化> 。據(jù)同事測(cè)試說(shuō)固態(tài)硬盤(pán)SSD對(duì)于小文件的隨機(jī)讀效率很高,對(duì)于更新不是很頻繁的圖片服務(wù),讀卻很多,每個(gè)文件都不是很大的話,sendfile+SSD應(yīng)該是絕配。
splice and tee: splice背后的真正概念是暴露給用戶空間的“隨機(jī)內(nèi)核緩沖區(qū)”的概念。“也就是說(shuō),splice和tee運(yùn)行在用戶控制的內(nèi)核緩沖區(qū)上,在這個(gè)緩沖區(qū)中,splice將來(lái)自任意文件描述符的數(shù)據(jù)傳送到緩沖區(qū)中(或從緩沖區(qū)傳送到文件描述符),而tee將一個(gè)緩沖區(qū)中的數(shù)據(jù)復(fù)制到另一個(gè)緩沖區(qū)中。因此,從一個(gè)很真實(shí)(而抽象)的意義上講,splice相當(dāng)于內(nèi)核緩沖區(qū)的read/write,而tee相當(dāng)于從內(nèi)核緩沖區(qū)到另一個(gè)內(nèi)核緩沖區(qū)的memcpy。”。本人覺(jué)得這個(gè)技術(shù)用來(lái)做代理,很合適。因?yàn)閿?shù)據(jù)可以直接從一個(gè)soket到另一個(gè)soket,不需要經(jīng)用戶和內(nèi)核空間的切換。這是sendfile不支持的。詳細(xì)參見(jiàn) <linux2.6.17以上內(nèi)核中的 splice and tee> ,具體實(shí)例請(qǐng)參見(jiàn)? man 2? tee ,里面有個(gè)完整的程序。
3,如何清空一塊內(nèi)存(memset ?)
比如有一個(gè)buffer[1024*1024],我們需要把它清空然后strcat(很多情況下可以通過(guò)記錄寫(xiě)的起始位置+memcpy來(lái)代替)追加填充字符串。
其實(shí)我們沒(méi)有必要用memset(buffer,0x00,sizeof(buffer))來(lái)清空整個(gè)buffer, memset(buffer,0x00,1)就能達(dá)到目的。 我平時(shí)更喜歡用buffer[0]='\0'; 來(lái)替代,省了一次函數(shù)調(diào)用的開(kāi)銷。
4,內(nèi)存復(fù)用? (有必要為每個(gè)響應(yīng)分配內(nèi)存 ?)
對(duì)于NBA JS服務(wù)來(lái)說(shuō),我們返回的都是壓縮數(shù)據(jù),99%都不超過(guò)15k,基本一次write就全部出去了,是沒(méi)有必要為每個(gè)響應(yīng)分配內(nèi)存的,公用一個(gè)buffer就夠了。如果真的遇到大數(shù)據(jù),我先write一次,剩下的再暫存在內(nèi)存里,等待下次發(fā)送。
5,避免頻繁動(dòng)態(tài)申請(qǐng)/釋放內(nèi)存(malloc)
這個(gè)似乎不用多說(shuō),要想一個(gè)Server啟動(dòng)后成年累月的跑,就不應(yīng)該頻繁地去動(dòng)態(tài)申請(qǐng)和釋放內(nèi)存。原因很簡(jiǎn)單一,避免內(nèi)存泄露。二,避免碎片過(guò)多。三,影響效率。一般來(lái)說(shuō),都是一次申請(qǐng)一大塊內(nèi)存,然后自己寫(xiě)內(nèi)存分配算法。為http用戶分配的緩沖區(qū)生命期的特點(diǎn)是,可以隨著fd的關(guān)閉,而回收,避免漏網(wǎng)。還有Server的編寫(xiě)者應(yīng)該對(duì)自己設(shè)計(jì)的程序達(dá)到最高支撐量的時(shí)候所消耗的內(nèi)存心中有數(shù)。
6,字節(jié)對(duì)齊
先看下面的兩個(gè)結(jié)構(gòu)體有什么不同:
struct A {
??????? short size;
??????? char *ptr;
??????? int left;
} a ;
struct B {
??????? char *ptr;
??????? short size;
??????? int left;
} b ;
僅僅是一個(gè)順序的變化,結(jié)構(gòu)體B順序是合理的:
在32bit linux系統(tǒng)上,是按照32/8bit=4byte來(lái)對(duì)齊的, sizeof(a)=12 ,sizeof(b)=12 。
在64bit linux系統(tǒng)上,是按照64/8bit=8byte來(lái)對(duì)齊的, sizeof(a)=24 ,sizeof(b)=16 。
32bit機(jī)上看到的A和B結(jié)果大小是一樣的,但是如果把int改成short效果就不一樣了。
如果我想強(qiáng)制以2byte對(duì)齊,可以這樣:
#pragma pack(2)
struct A {
??????? short size;
??????? char *ptr;
??????? int left;
} a ;
#pragma pack()
注意pack()里的參數(shù),只能指定比本機(jī)支持的字節(jié)對(duì)齊標(biāo)準(zhǔn)小,而不能更大。
7,內(nèi)存安全問(wèn)題
先舉個(gè)好玩的例子,不使用a,而給a賦上值:
int main()
{
??????? char a[8];
??????? char b[8];
??????? memcpy(b,"1234567890\0",10);
??????? printf("a=%s\n",a);
??????? return 0;
}
程序輸出? a=90 。
這就是典型的溢出,如果是空閑的內(nèi)存,用點(diǎn)也就罷了,可是把別人地盤(pán)上的數(shù)據(jù)覆蓋了,就不好了。
接收的用戶數(shù)據(jù)一定要嚴(yán)格判斷,確定不會(huì)越界,不是每個(gè)人都按規(guī)矩辦事的,搞不好就掛了。
8,云風(fēng)的內(nèi)存管理理論 (sd2c大會(huì)所獲 blog & ppt)
沒(méi)有永遠(yuǎn)不變的原則
大原則變化的慢
沒(méi)有一勞永逸的解決方案
內(nèi)存訪問(wèn)很廉價(jià)但有代價(jià)
減少內(nèi)存訪問(wèn)的次數(shù)是很有意義的
隨機(jī)訪問(wèn)內(nèi)存慢于順序訪問(wèn)內(nèi)存
請(qǐng)讓數(shù)據(jù)物理上連續(xù)
集中內(nèi)存訪問(wèn)優(yōu)于分散訪問(wèn)
盡可能的將數(shù)據(jù)緊密的存放在一起
無(wú)關(guān)性內(nèi)存訪問(wèn)優(yōu)于相關(guān)性內(nèi)存訪問(wèn)
請(qǐng)考慮并行的可能性、即使你的程序本身沒(méi)有使用并行機(jī)制
控制周期性密集訪問(wèn)的數(shù)據(jù)大小
必要時(shí)采用時(shí)間換空間的方法
讀內(nèi)存快于寫(xiě)內(nèi)存
代碼也會(huì)占用內(nèi)存,所以、保持代碼的簡(jiǎn)潔
物理法則
晶體管的排列
批量回收內(nèi)存
不釋放內(nèi)存,留給系統(tǒng)去做
list map? vector (100次調(diào)用產(chǎn)生13次內(nèi)存分配和釋放)
長(zhǎng)用字符串做成hash,使用指針訪問(wèn)
直接內(nèi)存頁(yè)處理控制
三,減少磁盤(pán)I/O
這個(gè)其實(shí)就是通過(guò)盡可能的使用內(nèi)存達(dá)到性能提高和i/o減少。從系統(tǒng)的讀寫(xiě)buffer到用戶空間自己的cache,都是可以有效減少磁盤(pán)i/o的方法。用戶可以把數(shù)據(jù)暫存在自己的緩沖區(qū)里,批量讀寫(xiě)大塊數(shù)據(jù)。cache的使用是很必要的,可以自己用共享內(nèi)存的方法實(shí)現(xiàn),也可以用現(xiàn)成的BDB來(lái)實(shí)現(xiàn)。歡迎訪問(wèn)我的公益站點(diǎn) berkeleydb.net ,不過(guò)我不太歡迎那種問(wèn)了問(wèn)題就跑的人。BDB默認(rèn)的cache只有256K,可以調(diào)大這個(gè)數(shù)字,也可以純粹使用Mem Only方法。對(duì)于預(yù)先知道的結(jié)果,爭(zhēng)取不從磁盤(pán)取第二次,這樣磁盤(pán)基本就被解放出來(lái)了。BDB取數(shù)據(jù)的速度每秒大概是100w條(2CPU*2Core Xeon(R) E5410 @ 2.33GHz環(huán)境測(cè)試,單條數(shù)據(jù)幾十字節(jié)),如果你想取得更高的性能建議自己寫(xiě)。
四,優(yōu)化你的網(wǎng)卡
首先ethtool ethx 看看你的外網(wǎng)出口是不是Speed: 1000Mb/s 。
對(duì)于多核服務(wù)器,運(yùn)行top命令,然后按一下1,就能看到每個(gè)核的使用情況。如果發(fā)現(xiàn)cpuid=0的那顆使用率明顯高于其他核,那就說(shuō)明id=0的cpu將來(lái)也許會(huì)成為你的瓶頸。然后可以用mpstat(非默認(rèn)安裝)命令查看系統(tǒng)中斷分布,用cat /proc/interrupts 網(wǎng)卡中斷分布。
下面這個(gè)數(shù)據(jù)是我們已經(jīng)做過(guò)優(yōu)化了的服務(wù)器中斷分布情況:
[yangjian2@D08043466 ~]$ mpstat -P ALL 1
Linux 2.6.18-53.el5PAE (D08043466)????? 12/15/2008
01:51:27 PM? CPU?? %user?? %nice??? %sys %iowait??? %irq?? %soft? %steal?? %idle??? intr/s
01:51:28 PM? all??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00? 100.00?? 1836.00
01:51:28 PM??? 0??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00? 100.00??? 179.00
01:51:28 PM??? 1??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00? 100.00??? 198.00
01:51:28 PM??? 2??? 1.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00? 100.00??? 198.00
01:51:28 PM??? 3??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00? 100.00??? 346.00
01:51:28 PM??? 4??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00? 100.00??? 207.00
01:51:28 PM??? 5??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00? 100.00??? 167.00
01:51:28 PM??? 6??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00? 100.00??? 201.00
01:51:28 PM??? 7??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00? 100.00??? 339.00
沒(méi)優(yōu)化過(guò)的應(yīng)該是這個(gè)樣子:
yangjian2@xk-6-244-a8 ~]$ mpstat -P ALL 1
Linux 2.6.18-92.1.6.el5 (xk-6-244-a8.bta.net.cn)??????? 12/15/2008
02:05:26 PM? CPU?? %user?? %nice??? %sys %iowait??? %irq?? %soft? %steal?? %idle??? intr/s
02:05:27 PM? all??? 0.00??? 0.00??? 0.00??? 0.12??? 0.00??? 0.00??? 0.00?? 99.88?? 1593.00
02:05:27 PM??? 0??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00? 100.00?? 1590.00
02:05:27 PM??? 1??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00? 100.00????? 0.00
02:05:27 PM??? 2??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00? 100.00????? 2.00
02:05:27 PM??? 3??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00? 100.00????? 0.00
02:05:27 PM??? 4??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00? 100.00????? 0.00
02:05:27 PM??? 5??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00? 100.00????? 0.00
02:05:27 PM??? 6??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00? 100.00????? 0.00
02:05:27 PM??? 7??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00??? 0.00? 100.00????? 0.00
對(duì)于32bit的centos5,mpstat -P ALL 1表現(xiàn)跟第一種情況一樣,分布比較平均,但是一但有了訪問(wèn)量,就可以看到差距。cat /proc/interrupts 看起來(lái)更直觀些,很清楚的知道哪個(gè)網(wǎng)卡的中斷在哪個(gè)cpu上處理。
其實(shí),當(dāng)你遇到網(wǎng)卡中斷瓶頸的時(shí)候證明你的網(wǎng)站并發(fā)度已經(jīng)相當(dāng)高了,每秒三五萬(wàn)個(gè)請(qǐng)求還至于成為瓶頸。除非你的應(yīng)用程序同時(shí)也在消耗cpu0的資源。對(duì)于這種情況,建議使用多進(jìn)程模式,每個(gè)進(jìn)程用 sched_setaffinity綁定特定的cpu,把cpu0從用戶事物中解放出來(lái),專心處理系統(tǒng)事物,當(dāng)然包括中斷。這樣你的極限應(yīng)該能處理20w+ http req/s (2CPU*4Core服務(wù)器)。但是對(duì)于多線程模式來(lái)說(shuō),我們就顯得無(wú)能為力了,因?yàn)槲覀內(nèi)绻胧褂枚嗪?#xff0c;就沒(méi)法不用cpu0。目前的方法只有兩個(gè):一,轉(zhuǎn)化為多進(jìn)程,然后進(jìn)程內(nèi)再使用多線程。二,讓你的網(wǎng)卡中斷分散在多個(gè)cpu上(目前只有硬件解決方案,感謝xiaodong2提供的技術(shù)支持)。 (修正:后來(lái)仔細(xì)讀了幾遍man手冊(cè),發(fā)現(xiàn)sched_setaffinity綁定特定的cpu對(duì)于多線程也是適用的,并且實(shí)驗(yàn)通過(guò),只需要將第一個(gè)參數(shù)置為0。這對(duì)cpu0的解放是個(gè)很好的發(fā)現(xiàn)。)
將網(wǎng)卡中斷分散在多個(gè)cpu硬件解決方案: 我們新加了一塊網(wǎng)卡(前提是這個(gè)網(wǎng)卡支持中斷分布),然后通過(guò)通過(guò)linux bonding將兩個(gè)網(wǎng)卡比如eth0,eth1聯(lián)合成一個(gè)通道bond0(當(dāng)然這里還涉及到交換機(jī)的調(diào)整),然后bond0就有了2G的帶寬吞吐量。把eth0的中斷處理幫定在cpu 0-3,把eth1中斷處理幫定在cpu 4-7,這樣中斷就被分布開(kāi)了。這樣會(huì)帶來(lái)一些額外的cpu開(kāi)銷,但是跟好處相比可以忽略不計(jì)。我在網(wǎng)卡優(yōu)化過(guò)的32bit服務(wù)器上測(cè)試http請(qǐng)求處理極限為 40w+ req/s,將近提升了一倍。
五,調(diào)整內(nèi)核參數(shù)
我的內(nèi)核心參數(shù)調(diào)整原則是,哪個(gè)遇到瓶頸調(diào)哪個(gè),謹(jǐn)慎使用,不能憑想象亂調(diào)一氣。看下面例子,其中default是我們公司定做的系統(tǒng)默認(rèn)的一些參數(shù)值。add by yangjian2并非全部都要調(diào)整,我只挑幾個(gè)比較重要的參數(shù)說(shuō)明一下,更多TCP方面的調(diào)優(yōu)請(qǐng)參見(jiàn) man 7 tcp 。
#++++++++++++++++++default++++++++++++++++++++++++++++++
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_max_tw_buckets = 180000
net.ipv4.tcp_sack = 1
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_rmem = 4096??????? 87380?? 4194304
net.ipv4.tcp_wmem = 4096??????? 16384?? 4194304
#++++++++++++++++++add by yangjian2++++++++++++++++++++++
net.ipv4.tcp_max_syn_backlog = 65536
net.core.netdev_max_backlog =? 32768
net.core.somaxconn = 32768
net.core.wmem_default = 8388608
net.core.rmem_default = 8388608
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.ipv4.tcp_timestamps = 0
net.ipv4.tcp_synack_retries = 2
net.ipv4.tcp_syn_retries = 2
net.ipv4.tcp_tw_recycle = 1
#net.ipv4.tcp_tw_len = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_mem = 94500000 915000000 927000000
net.ipv4.tcp_max_orphans = 3276800
#++++++++++++++++++++++++++++++++++++++++++++++++++++++++
maxfd: 對(duì)于系統(tǒng)所能打開(kāi)的最大文件描述符fd,可以通過(guò)以root啟動(dòng)程序,setrlimit()設(shè)置maxfd后,再通過(guò)setuid()轉(zhuǎn)為普通用戶提供服務(wù),我用的 int set_max_fds(int maxfds); 函數(shù)是zhongying提供的。這比用ulimit來(lái)的方便的多,不曉得為什么那么多開(kāi)源軟件都沒(méi)這樣用。
net.ipv4.tcp_max_syn_backlog = 65536 : 這個(gè)參數(shù)可以肯定是必須要修改的,默認(rèn)值1024,我google了一下,幾乎是人云亦云,沒(méi)有說(shuō)的明白的。 要講明白得從man listen說(shuō)起,int listen(int sockfd, int backlog);? 早期的網(wǎng)絡(luò)編程都中描述,int backlog 代表未完成隊(duì)列SYN_RECV狀態(tài)+已完成隊(duì)列ESTABLISHED的和。但是這個(gè)意義在Linux 2.2以后的實(shí)現(xiàn)中已經(jīng)被改變了,int backlog只代表已完成隊(duì)列ESTABLISHED的長(zhǎng)度,在AF_INET協(xié)議族中(我們廣泛使用的就是這個(gè)),當(dāng)int backlog大于SOMAXCONN (128 in Linux 2.0 & 2.2)的時(shí)候,會(huì)被調(diào)整為常量SOMAXCONN大小。這個(gè)常量可以通過(guò)net.core.somaxconn來(lái)修改。而未完成隊(duì)列大小可以通過(guò)net.ipv4.tcp_max_syn_backlog來(lái)調(diào)整,一般遭受syn flood攻擊的網(wǎng)站,都存在大量SYN_RECV狀態(tài),所以調(diào)大tcp_max_syn_backlog值能增加抵抗syn攻擊的能力。
net.ipv4.tcp_syncookies = 1 : 當(dāng)出現(xiàn)syn等候隊(duì)列出現(xiàn)溢出時(shí)象對(duì)方發(fā)送syncookies。目的是為了防止syn flood攻擊 , 默認(rèn)值是 0。 不過(guò)man listen說(shuō)當(dāng)啟用syncookies時(shí)候,tcp_max_syn_backlog的sysctl調(diào)整將失效,和這個(gè)描述不是很符合。參見(jiàn)下面兩個(gè)描述分別是man listen和man 7 tcp:
When syncookies are enabled there is no logical maximum length and this tcp_max_syn_backlog sysctl? setting? is? ignored.
Send out? syncookies? when the syn backlog queue of a socket overflows.
但我可以肯定的說(shuō)這個(gè)選項(xiàng)對(duì)你的性能不會(huì)有提高,而且它嚴(yán)重的違背TCP協(xié)議,不允許使用TCP擴(kuò)展,除非遭受攻擊,否則不推薦使用。
net.ipv4.tcp_synack_retries = 2 : 對(duì)于遠(yuǎn)端的連接請(qǐng)求SYN,內(nèi)核會(huì)發(fā)送SYN + ACK數(shù)據(jù)報(bào),以確認(rèn)收到上一個(gè) SYN連接請(qǐng)求包。這是所謂的三次握手( threeway handshake)機(jī)制的第二個(gè)步驟。這里決定內(nèi)核在放棄連接之前所送出的 SYN+ACK 數(shù)目。如果你的網(wǎng)站SYN_RECV狀態(tài)確實(shí)挺多,為了避免syn攻擊,那么可以調(diào)節(jié)重發(fā)的次數(shù)。
net.ipv4.tcp_syn_retries = 2 : 對(duì)于一個(gè)新建連接,內(nèi)核要發(fā)送多少個(gè) SYN 連接請(qǐng)求才決定放棄。不應(yīng)該大于255,默認(rèn)值是5,對(duì)應(yīng)于180秒左右。這個(gè)對(duì)防止syn攻擊其實(shí)是沒(méi)有用處的,也沒(méi)必要調(diào)節(jié)。
net.ipv4.tcp_max_orphans = 3276800 : 這個(gè)最好不要修改,因?yàn)槊吭黾?,將消耗~64k內(nèi)存。即使報(bào)錯(cuò) TCP: too many of orphaned sockets 也有可能是由于你的net.ipv4.tcp_mem過(guò)小,導(dǎo)致的Out of socket memory,繼而引發(fā)的。
net.ipv4.tcp_wmem = 4096 16384 4194304 :? 為自動(dòng)調(diào)優(yōu)定義每個(gè)socket使用的內(nèi)存。第一個(gè)值是為socket的發(fā)送緩沖區(qū)分配的最少字節(jié)數(shù)。第二個(gè)值是默認(rèn)值(該值會(huì)被 wmem_default覆蓋),緩沖區(qū)在系統(tǒng)負(fù)載不重的情況下可以增長(zhǎng)到這個(gè)值。第三個(gè)值是發(fā)送緩沖區(qū)空間的最大字節(jié)數(shù)(該值會(huì)被wmem_max覆蓋)。
net.ipv4.tcp_rmem = 4096 87380 4194304 : 接收緩沖區(qū),原理同上。
net.ipv4.tcp_mem = 94500000 915000000 927000000 :
low:當(dāng)TCP使用了低于該值的內(nèi)存頁(yè)面數(shù)時(shí),TCP不會(huì)考慮釋放內(nèi)存。
pressure:當(dāng)TCP使用了超過(guò)該值的內(nèi)存頁(yè)面數(shù)量時(shí),TCP試圖穩(wěn)定其內(nèi)存使用,進(jìn)入pressure模式,當(dāng)內(nèi)存消耗低于low值時(shí)則退出pressure狀態(tài)。
high:允許所有tcp sockets用于排隊(duì)緩沖數(shù)據(jù)報(bào)的內(nèi)存頁(yè)數(shù)。
一般情況下這個(gè)值是在系統(tǒng)啟動(dòng)時(shí)根據(jù)系統(tǒng)內(nèi)存數(shù)量計(jì)算得到的,如果你的dmesg報(bào) Out of socket memory,你可以試著修改這個(gè)參數(shù),順便介紹3個(gè)修改方法:
1, echo "94500000 915000000 927000000" > /proc/sys/net/ipv4/tcp_wmem
2, sysctl -w "net.ipv4.tcp_mem = 94500000 915000000 927000000"
3, net.ipv4.tcp_mem = 94500000 915000000 927000000? (vi /etc/sysctl.conf 然后 sysctl -p生效)
下面命令也許能提供些信息,在你修改tcp參數(shù)時(shí)做個(gè)參考:
[sports@xk-6-244-a8 nbahttpd_beta4.0]$ cat? /proc/net/sockstat
sockets: used 1195
TCP: inuse 1177 orphan 30 tw 199 alloc 1181 mem 216
UDP: inuse 0 mem 0
RAW: inuse 0
FRAG: inuse 0 memory 0
其他我就不多說(shuō)了,知道這些基本就能解決絕大部分問(wèn)題了。
六,衡量Web Server的性能指標(biāo)
我認(rèn)為一個(gè)好的Server應(yīng)該能在有限的硬件資源上將性能發(fā)揮到極限。
Web Server的衡量指標(biāo)并非單一,要根據(jù)具體應(yīng)用類型而定。比如財(cái)經(jīng)實(shí)時(shí)圖片系統(tǒng),我們關(guān)注它每秒輸出圖片數(shù)量。NBA js直播放系統(tǒng),我們關(guān)心他的同時(shí)在線connections和當(dāng)時(shí)的每秒請(qǐng)求處理量。行情系統(tǒng),我們關(guān)心它c(diǎn)onnections和請(qǐng)求處理量的同時(shí)還要關(guān)心每個(gè)請(qǐng)求平均查詢多少支股票。但總體來(lái)說(shuō)同時(shí)在線connections和當(dāng)時(shí)的每秒請(qǐng)求處理量是兩個(gè)最重要的指標(biāo)。
對(duì)于圖片系統(tǒng)再說(shuō)一句,我覺(jué)得大圖片和小圖片是應(yīng)該區(qū)別對(duì)待的,小圖片不應(yīng)該產(chǎn)生磁盤(pán) I/O 。
Nginx是我見(jiàn)過(guò)的Web Server中性能比較高的一個(gè),他幾乎是和我的server同時(shí)誕生,可能還更早些,框架很不錯(cuò),我覺(jué)得目前版本稍微優(yōu)化下,支持10w connections不成問(wèn)題。 lighttpd也不錯(cuò),我對(duì)他的認(rèn)識(shí)還是停留在幾年前的性能測(cè)試上,它的性能會(huì)比nginx遜色一些。他們都支持epoll,sendfile,可以起多個(gè)進(jìn)程worker,worker內(nèi)部使用非阻塞,這是比較優(yōu)良的I/O的模型。Squid,Apache,都是骨灰級(jí)軟件了,好處就是支持的功能多,另許多輕量級(jí)Server望塵莫及,可是性能太一般了,祝愿他們?cè)缛罩貙?xiě)。
插點(diǎn)小插曲,我在財(cái)經(jīng)項(xiàng)目組的時(shí)候,有的同事來(lái)我們組一年多了,問(wèn)我是不是管機(jī)器的,我點(diǎn)點(diǎn)頭,后來(lái)又有比較了解我的同事說(shuō)我是系統(tǒng)管理員,我說(shuō)“恩”。其實(shí)我的主業(yè)是寫(xiě)程序的。也許是我太低調(diào)了,覺(jué)得那些陳年往事不值再提,以至于別人對(duì)我做的東西了解甚少, 今天我就高調(diào)一把,公布一些我寫(xiě)的程序的性能指標(biāo)。我們的系統(tǒng)近幾年來(lái)說(shuō)在性能上是領(lǐng)先業(yè)內(nèi)的(不爭(zhēng)世界第一,那樣壓力太大,第二就很好,也許正在看我blog的你一不留神就把我超了呢 ^-^ ),高效的原因很重要的一點(diǎn)是由于它是根據(jù)服務(wù)特點(diǎn)量身訂做的。
實(shí)驗(yàn)環(huán)境數(shù)據(jù): 我寫(xiě)了個(gè)HTTP服務(wù)框架,不使用磁盤(pán)I/O,簡(jiǎn)化了邏輯處理部分,只會(huì)輸出 "hello world!"? 程序部署在192.168.0.1上(2cup*4Core,硬件和系統(tǒng)都做過(guò)優(yōu)化),我在另外8臺(tái)同等配置服務(wù)器上同時(shí)執(zhí)行命令? ./apache/bin/ab -c 1000? -n 3000000?? -k? "http://192.168.0.1/index.html" 幾乎同時(shí)處理完畢,總合相加 40w req/s,我相信這是目前硬件水平上的極限值 。
真實(shí)環(huán)境數(shù)據(jù):2cup*4Core Mem 16G, 64bit centos5,單機(jī)23w+ connections, 3.5w req/s時(shí),CPU總量消耗 1/8,內(nèi)存消耗0.4%(相當(dāng)于正好消耗了一個(gè)Core+64M Mem)。在30w+ connections, 4.6w req/s 時(shí),CPU總量消耗 1/4,內(nèi)存消耗 0.5% 。保守地說(shuō),只要把網(wǎng)卡中斷分散一下,單機(jī)50w+ connections很easy。? 更多數(shù)據(jù)圖文參見(jiàn)“NBA js直播的發(fā)展歷程”一節(jié)。
有些人了解我是由于財(cái)經(jīng)的實(shí)時(shí)行情系統(tǒng),雖然每天處理近百億的http請(qǐng)求處理量還不錯(cuò),但那并非我的得意之作,相反我覺(jué)得那個(gè)寫(xiě)的有些粗糙,至少有一倍以上的性能提升空間。對(duì)于行情系統(tǒng),我還是很想把它做成push的,目標(biāo)仍然是單機(jī)50w+在線,無(wú)延遲推送,可惜本人js功底太爛,所以要作為一個(gè)長(zhǎng)期的地下項(xiàng)目去做,如果可能,我想一開(kāi)始就把它作為一個(gè)開(kāi)源項(xiàng)目來(lái)做。
我個(gè)人比較喜歡追求性能極限,公司對(duì)此暫時(shí)還不是很認(rèn)可,或者說(shuō)重視程度還不夠,可能是由于我們的硬件資源比較充裕吧。盡管如此,只要我認(rèn)為對(duì)企業(yè)有價(jià)值的,就依然會(huì)堅(jiān)持做下去,我的目標(biāo)是獲得業(yè)界的認(rèn)可。同時(shí)我相信中國(guó)的未來(lái)不缺乏互聯(lián)網(wǎng)用戶,當(dāng)有人燒不起錢(qián)的時(shí)候想起了我,那我就是有價(jià)值的。
這里說(shuō)的有點(diǎn)多了,不過(guò)放心,ppt我會(huì)做的相當(dāng)簡(jiǎn)單。
七,NBA js直播的發(fā)展歷程
這一節(jié)就談下這個(gè)項(xiàng)目發(fā)展過(guò)程中所遇到的瓶頸,以及如何解決的。
應(yīng)該是06年吧,當(dāng)時(shí)NBA 比賽比較火,woocall負(fù)責(zé)高速模式圖文直播放,普通模式和動(dòng)態(tài)比分?jǐn)?shù)據(jù)等都放在一群破服務(wù)器上,大概有十幾20臺(tái),這些破服務(wù)器有些扛不住了。
因?yàn)榈诙煊幸粓?chǎng)比較大的比賽,我想埋連接在線上測(cè)一下效果,于是連夜把財(cái)經(jīng)實(shí)時(shí)行情server改寫(xiě)成了NBA JS直播server. 當(dāng)時(shí)有兩臺(tái) Intel(R) Xeon(TM) CPU 3.00GHz 雙cpu的服務(wù)器,在F5后面。先啟用一臺(tái)服務(wù)器,比賽開(kāi)始前靜悄悄的,不一會(huì),迅速串到了20w connections,再往上增長(zhǎng),就慢的幾乎不可訪問(wèn), ethtool eth0? , Speed: 100Mb/s, 網(wǎng)卡出口帶寬跑滿了(那時(shí)候支持千兆的交換機(jī)還不多)。迅速把另一臺(tái)服務(wù)器啟用,后來(lái)又卡了,好象是F5處理能力不足。后來(lái)升級(jí)服務(wù)器出口帶寬為1G,當(dāng)然這需要交換機(jī)支持千兆口,更換網(wǎng)線,服務(wù)器也從F5后面轉(zhuǎn)移出來(lái),通過(guò)DNS直接輪詢。
后來(lái)又測(cè)試了幾次,等到新申請(qǐng)的Intel(R) Xeon(R) CPU 5120? @ 1.86GHz,雙核雙cpu服務(wù)器一到,就開(kāi)始大規(guī)模部署,比賽更火了,不巧的是行情也火了起來(lái),我的財(cái)經(jīng)實(shí)時(shí)圖片系統(tǒng)和行情系統(tǒng)也是帶寬殺手,同時(shí)我也成了服務(wù)器殺手,到處蹭服務(wù)器。IDC帶寬出口開(kāi)始告急,我每天開(kāi)始關(guān)注哪個(gè)機(jī)房還有富余帶寬,有的機(jī)房被我們跑的太滿,開(kāi)始有人勸我們遷移到別的機(jī)房。后來(lái) yangguang4勸我支持gzip輸出,我覺(jué)得動(dòng)態(tài)壓縮太耗費(fèi)cpu,不知道預(yù)先壓縮是否可行,寫(xiě)個(gè)程序試了一把,沒(méi)問(wèn)題,于是NBA JS直播的的帶寬一下子被砍掉了70%,而且沒(méi)浪費(fèi)一點(diǎn)我們的cpu,賺大了。
之后的兩年里NBA JS服務(wù)一直很穩(wěn)定,我?guī)缀醵紱](méi)怎么看過(guò),2007年的一場(chǎng)體育賽事中,單機(jī)達(dá)到25w+ connections,2.86w req/s ,cpu空閑30% ,見(jiàn)下圖 (2CPU*2Core 1.86GHZ服務(wù)器) 。直到奧運(yùn)期間,有場(chǎng)賽事,woocall瞬間仍給我近200w connections,網(wǎng)通的服務(wù)器被秒殺了1/3。這其實(shí)就是善意的DDOS攻擊,這些用戶如果正常進(jìn)入是沒(méi)有問(wèn)題了,瞬間扔過(guò)來(lái),超出了操作系統(tǒng)極限,系統(tǒng)掛掉了,我的服務(wù)也over了。在調(diào)整內(nèi)核參數(shù)里有講,怎么修改內(nèi)核參數(shù)提高服務(wù)器抗秒殺能力,但是不能杜絕。
下圖為2007年一場(chǎng)比賽時(shí),單機(jī)25w+ connections,2.86w req/s,的狀態(tài)(2CPU*2Core 1.86GHZ):
?
奧運(yùn)結(jié)束后,我對(duì)服務(wù)器程序和架構(gòu)做了調(diào)整,砍掉了2/3的服務(wù)器。但我沒(méi)留意的是,同樣connections,實(shí)際http請(qǐng)求增加了一倍,因?yàn)樾律狭艘粋€(gè)flash方位圖,里面增加了3個(gè)js,增加就增加吧,既然砍了就不準(zhǔn)備再恢復(fù)了。但是服務(wù)在2CPU*4Core centos5 32bit上的表現(xiàn)卻讓我很失望,跑不過(guò)2CPU*2Core centos4 32bit 。我開(kāi)始懷疑程序升級(jí)的時(shí)候是不是有什么地方?jīng)]考慮到,開(kāi)始調(diào)程序,折騰幾天沒(méi)有結(jié)果,癥狀是單機(jī)支撐12.5萬(wàn)時(shí)候沒(méi)有任何異常,內(nèi)存使用1%左右,總cpu使用了5%左右,load 0.5,但是再增加0.1w用戶server肯定崩潰,每次都是相同的表現(xiàn),我知道在什么地方卡住了。后來(lái)看了下dmesg,才恍然大悟,我是被32bit centos 5的內(nèi)核暗殺的(Out of memory: Killed process xxx)。
32位機(jī)上LowFree一般是會(huì)變化的(cat /proc/meminfo? | grep LowFree),最大不能超過(guò)880M(這個(gè)值不能改變除非用hugemem內(nèi)核),在centos4 上有內(nèi)核參數(shù)vm.lower_zone_protection(單位是M)來(lái)調(diào)節(jié)LowFree,默認(rèn)vm.lower_zone_protection=0 ,LowFree=16M,但是在centos5上這個(gè)參數(shù)貌似被取消了,改變不了。從使用經(jīng)驗(yàn)來(lái)看,也就是你能申請(qǐng)16M~880M這么大的連續(xù)內(nèi)存,但是16M以上不保證你能申請(qǐng)的到。centos4用到64M以上都沒(méi)什么問(wèn)題,centos5 用40M+ 就被OOM Killer給斃了。
本周開(kāi)始使用64bit centos5.2進(jìn)行測(cè)試,遷移很順利,沒(méi)有修改一行代碼,他們把整個(gè)16G物理內(nèi)存都作為L(zhǎng)owFree,這下可以隨便揮霍了(盡管如此我還是會(huì)節(jié)約的),前幾天的比賽,這個(gè)64bit機(jī)跑了18w connections,很安靜,未見(jiàn)異常,等有大比賽再檢驗(yàn)下,沒(méi)問(wèn)題的話就開(kāi)始大規(guī)模使用64bit系統(tǒng)。
目前看來(lái),如果成功遷移64bit系統(tǒng)似乎可以高枕無(wú)憂了,但是我還是有兩個(gè)憂慮:
1,再突然甩200w connections給我,我不敢保證能扛的住,因?yàn)楝F(xiàn)在服務(wù)器數(shù)量消減太多,需要yangguang4那邊做策略調(diào)整,在比賽結(jié)束后,平滑的把用戶丟給我,這應(yīng)該有個(gè)持續(xù)過(guò)程,而不是一秒內(nèi)的瞬間。
2,我猜這個(gè)系統(tǒng),單機(jī)支撐到30w conections的時(shí)候會(huì)遇到瓶頸,因?yàn)榫W(wǎng)卡的中斷集中在cpu0上,沒(méi)有均衡開(kāi)。我們有硬件解決方案已經(jīng)實(shí)現(xiàn)(每個(gè)服務(wù)器會(huì)多2000RMB開(kāi)銷)我只部署了一臺(tái),但是軟的還沒(méi)實(shí)現(xiàn),寄希望于xiaodong2 。
補(bǔ)充:
昨天的比賽中,一臺(tái)64bit機(jī),單機(jī)支撐30w+ connections,cpu0空閑率只剩6%,和我的預(yù)料是一致的。當(dāng)時(shí)的CPU總量被我用掉近 1/4,內(nèi)存被我用掉 0.5% 。
下圖為30w+ connections, 4.6w req/s 的時(shí)候我的程序使用的資源情況(2cpu*4Core):
下圖為cpu使用分布情況,cpu0空閑率只剩 6% (2cpu*4Core):
另外附上一個(gè)23w connections, 3.5w req/s 的時(shí)候我的程序使用的資源情況(2cpu*4Core),當(dāng)時(shí)cpu只被用掉1/8,內(nèi)存被用掉 0.4% ,cpu沒(méi)有發(fā)揮線性增加的作用,我肯定不說(shuō)能我可以支撐23w*8,但是保守地說(shuō),只要把網(wǎng)卡中斷分散一下,單機(jī)50w+ connections很easy。
?
八,新浪財(cái)經(jīng)實(shí)時(shí)行情系統(tǒng)的歷史遺留問(wèn)題 (7 byte = 10.68w RMB/year)
這點(diǎn)我還是提下吧,估計(jì)我不說(shuō),大家也想不到。
先感謝wangyun同學(xué)的大膽使用才有了今天的財(cái)經(jīng)實(shí)時(shí)行情系統(tǒng)(當(dāng)初是從一臺(tái)PIII 900服務(wù)器上發(fā)展起來(lái)的,前幾天剛被我下線)不過(guò) "hq_str_"? 這7個(gè)字節(jié)的前綴,也是他造成的,當(dāng)初他說(shuō)改抓取頁(yè)面有些麻煩,就讓我寫(xiě)死在server里,雖然意識(shí)到將來(lái)也許會(huì)有隱患,但我還是照做了。見(jiàn)下面返回?cái)?shù)據(jù):
http://hq.sinajs.cn/list=s_sz000609,s_sz000723,s_sh000001
var hq_str_s_sz000609="綿世股份,9.29,-0.05,-0.54,170945,16418"; var hq_str_s_sz000723="美錦能源,0.00,0.00,0.00,0,0"; var hq_str_s_sh000001="上證指數(shù),2031.681,-47.436,-2.28,1216967,8777380";
我算了一筆帳,行情好的時(shí)候每秒會(huì)產(chǎn)生30~40w個(gè)請(qǐng)求,一般一個(gè)請(qǐng)求會(huì)請(qǐng)求3~50只股票,保守點(diǎn)就按每個(gè)請(qǐng)求5只股票來(lái)計(jì)算,每秒會(huì)產(chǎn)生200w只股票查詢信息。由于這7個(gè)字節(jié)產(chǎn)生的帶寬為: 200w? *? 7byte? * 8bit / 1024 /1024 = 106.8 M? ,而往往我們的帶寬要按峰值來(lái)準(zhǔn)備,按1G帶寬100w RMB/year 計(jì)算,每年耗費(fèi)10.68w RMB。把這筆錢(qián)拿給我做獎(jiǎng)金,我會(huì)很happy的 ^-^ . 現(xiàn)在因?yàn)楹芏囗?yè)面都使用了行情數(shù)據(jù),想修改,代價(jià)很高。
所以設(shè)計(jì)系統(tǒng)的時(shí)候一定要考慮的稍微遠(yuǎn)一些,哪怕當(dāng)時(shí)只是一點(diǎn)點(diǎn)微不足道的地方,要考慮將來(lái)訪問(wèn)規(guī)模變大了會(huì)是什么后果。還有就是要敢于堅(jiān)持自己的原則。
網(wǎng)站加速--系統(tǒng)架構(gòu)篇
一,系統(tǒng)部署(高并發(fā),可擴(kuò)展)
二,負(fù)載均衡LVS(高可用,低成本)
三,IDC分布,DNS解析(快速)
-----------------------------------------------------------------------------------------
一,系統(tǒng)部署(高并發(fā),可擴(kuò)展)
本來(lái)想畫(huà)在手稿上然后掃描上去的,貌似方法太土,在朋友的幫助下費(fèi)了n個(gè)小時(shí)用Visio畫(huà)了個(gè),感覺(jué)很好看 ^-^ 。這一篇將主要圍繞這個(gè)圖來(lái)講述。
?
首先從數(shù)據(jù)源說(shuō)起,所謂狡兔三窟,我們數(shù)據(jù)源也是按三路設(shè)計(jì),以保證IDC內(nèi)部和不同IDC之間實(shí)現(xiàn)災(zāi)備。源頭轉(zhuǎn)發(fā)機(jī)A,B,C擁有往集群中任何一臺(tái)服務(wù)器同步數(shù)據(jù)的權(quán)限,所以他們?nèi)齻€(gè)有一個(gè)活著,數(shù)據(jù)就可以同步更新,而且可以自動(dòng)切換。從源頭轉(zhuǎn)發(fā)機(jī)到其他各IDC的數(shù)據(jù)都是雙路的,然后每個(gè)IDC的前兩臺(tái)服務(wù)器具備轉(zhuǎn)發(fā)功能,往IDC內(nèi)部其他服務(wù)器分發(fā)數(shù)據(jù),同一IDC內(nèi)部的主備轉(zhuǎn)發(fā)機(jī)可以自動(dòng)切換。這樣就實(shí)現(xiàn)了數(shù)據(jù)同步更新的高可用性。
介紹下這個(gè)集群里的角色,備機(jī)A來(lái)自行情系統(tǒng),兼任源頭轉(zhuǎn)發(fā)的異地備份。系統(tǒng)內(nèi)的另外兩個(gè)備機(jī)屬于輕負(fù)荷服務(wù)器,80端口空出來(lái),必要時(shí)候只要一啟動(dòng),就會(huì)立即自動(dòng)加入到LVS后面服役。除了A以外所有具備轉(zhuǎn)發(fā)功能的機(jī)器同時(shí)也是集群內(nèi)的普通成員,需要提訪問(wèn)供服務(wù)的。各IDC的LVS本身也是有主備的,可以實(shí)現(xiàn)自動(dòng)切換。
整個(gè)系統(tǒng)增減服務(wù)器非常方便,用戶根本感覺(jué)不到,備機(jī)的啟用更快,也就3~5秒,具備很好的擴(kuò)展性。
我們的數(shù)據(jù)從源頭上就是使用我編寫(xiě)的myzip壓縮好了的,后綴名用"*.mz" ,比如 a.js.mz ,一直到用戶的瀏覽器端才解壓。數(shù)據(jù)傳輸量小,速度快。源頭轉(zhuǎn)發(fā)機(jī)上同時(shí)運(yùn)行一個(gè)checkchange的程序,確保內(nèi)容實(shí)際更新過(guò)的文件才往其他IDC轉(zhuǎn)發(fā),這樣能有效的減少傳輸文件數(shù)量,以達(dá)到更快的更新速度。
另外,跨IDC系統(tǒng)部署,很重要的一點(diǎn)是,內(nèi)網(wǎng)連通,路由選擇,這影響數(shù)據(jù)傳輸速度的關(guān)鍵。北京的各機(jī)房間一般都有比較好的專線連通,只需要把路由打通就ok了。跨IDC的,一般都使用vpn來(lái)做內(nèi)網(wǎng)傳輸,有條件的使用專線,這個(gè)比較昂貴,省著點(diǎn)用。另外跨網(wǎng)通,電信,和移動(dòng)機(jī)房的一般都從雙線機(jī)房路由,或者說(shuō),從到不同信息服務(wù)商連通性都比較好的機(jī)房路由。總之跨IDC數(shù)據(jù)傳輸,要做到各IDC之間的傳輸速度心中有數(shù)。
最后,請(qǐng)稍微注意下系統(tǒng)的安全性,包括數(shù)據(jù)傳輸?shù)陌踩?#xff0c;和網(wǎng)絡(luò)安全性,避免遭受攻擊。
二,負(fù)載均衡LVS(高可用,低成本)
LVS 有三種模式,NAT,TUN,DR,其中DR是最高效的,下面我將主要介紹DR的應(yīng)用。更多LVS資料參見(jiàn) LVS項(xiàng)目中文文檔。 目前我們公司的LVS應(yīng)用規(guī)模在國(guó)內(nèi)應(yīng)該至少可以排前三,更多技術(shù)細(xì)節(jié)請(qǐng)咨詢我們的LVS大牛xiaodong2.
下面是DR單臂模式的系統(tǒng)結(jié)構(gòu)圖:
下面引用一下官網(wǎng)的介紹: 在VS/DR 中,調(diào)度器根據(jù)各個(gè)服務(wù)器的負(fù)載情況,動(dòng)態(tài)地選擇一臺(tái)服務(wù)器,不修改也不封裝IP報(bào)文,而是將數(shù)據(jù)幀的MAC地址改為選出服務(wù)器的MAC地址,再將修改后的數(shù)據(jù)幀在與服務(wù)器組的局域網(wǎng)上發(fā)送。因?yàn)閿?shù)據(jù)幀的MAC地址是選出的服務(wù)器,所以服務(wù)器肯定可以收到這個(gè)數(shù)據(jù)幀,從中可以獲得該IP報(bào)文。當(dāng)服務(wù)器發(fā)現(xiàn)報(bào)文的目標(biāo)地址VIP是在本地的網(wǎng)絡(luò)設(shè)備上,服務(wù)器處理這個(gè)報(bào)文,然后根據(jù)路由表將響應(yīng)報(bào)文直接返回給客戶。
我在財(cái)經(jīng)時(shí)要使用LVS的初衷只是為了解決負(fù)載的均衡性,因?yàn)镈NA輪詢各前端服務(wù)器上連接數(shù)有不小的差距,那時(shí)候我們老大阿圖對(duì)于這個(gè)項(xiàng)目給予了很大支持,還親自組織過(guò)幾次會(huì)議。話說(shuō)恰巧yingyuan做了個(gè)新技術(shù)講座,我從中發(fā)現(xiàn)LVS/DR后想讓它幫忙修改負(fù)載均衡算法,后來(lái)部署上以后發(fā)現(xiàn),不用修改,均衡的很,再后來(lái)xiaodong2接手后對(duì)性能和穩(wěn)定性做了很大的提升,我們使用兩年來(lái)沒(méi)出過(guò)問(wèn)題。另外lvs還額外帶來(lái)了兩個(gè)好處,高可用性,和可伸縮性。是可以隨時(shí)把lvs后面的一臺(tái)服務(wù)器下掉,扛走,用戶是不知道的。服務(wù)器壞了也不用著急修,也不用修改DNS(另外DNS的層層cache影響不是一時(shí)半會(huì)就能消除的)。新增加一臺(tái)服務(wù)器也是同理,最絕的就是備機(jī)的啟用可以用秒來(lái)衡量(這些F5都能實(shí)現(xiàn),代價(jià)不菲)。財(cái)經(jīng)應(yīng)用對(duì)公司內(nèi)lvs的項(xiàng)目推動(dòng)有不可磨滅的貢獻(xiàn) ,xiaodong2也這么說(shuō)地 :) 。
三,IDC分布,DNS解析(快速)
這里思路跟CDN是一致的,盡量減少主干線路上的擁塞,讓用戶就近訪問(wèn),以達(dá)到最快的數(shù)據(jù)傳輸速率。
我們要做的就是了解自己應(yīng)用的用戶分布情況,然后再結(jié)合現(xiàn)有資源以及各地網(wǎng)絡(luò)出口特征,信息服務(wù)商的特征來(lái)部署我們的服務(wù)。
1,各省市網(wǎng)絡(luò)用戶分布依次排名(數(shù)據(jù)來(lái)自cnnic2007年的統(tǒng)計(jì)):
廣東 13.4%
山東 8.2%
江蘇 7.5
浙江
四川
河北
河南
福建
上海
遼寧
北京
湖南
山西
黑龍江
2,運(yùn)營(yíng)商的網(wǎng)絡(luò)分布特點(diǎn):
網(wǎng)通:以北京為超核心的放射性結(jié)構(gòu)。山東應(yīng)該是網(wǎng)通最大的用戶,但它的網(wǎng)絡(luò)存在瓶頸,會(huì)有丟包,造成外面訪問(wèn)它慢,它訪問(wèn)別人也慢。對(duì)此我們沒(méi)有必要浪費(fèi)珍貴的主干帶寬,在濟(jì)南布個(gè)點(diǎn),同步一份數(shù)據(jù)過(guò)去就,讓他們?cè)谧约菏?nèi)訪問(wèn),訪問(wèn)速度會(huì)立刻提升n倍。
電信:以幾大省市為核心的環(huán)狀結(jié)構(gòu),省市內(nèi)部也是大環(huán)套小環(huán)。其中以廣東用戶最多,必須要部點(diǎn)的地方。記得很久以前我拿到一份數(shù)據(jù)說(shuō),上海人訪問(wèn)本IDC的數(shù)據(jù),不如訪問(wèn)廣東的速度快,不曉得現(xiàn)在是否還存在這種情況。電信有7個(gè)主要核心,分布在廣州,上海,江蘇,西安,成都,武漢,北京。
教育網(wǎng):以國(guó)內(nèi)主要的八個(gè)結(jié)點(diǎn)為核心。這八個(gè)結(jié)點(diǎn)分布在北京,西安,成都,廣州,武漢,南京,上海,和沈陽(yáng)。
3,DNS解析時(shí)候需要權(quán)衡的:
現(xiàn)在了解了這些信息,那我們開(kāi)始討論如何部署我們的服務(wù)。要考慮兩個(gè)問(wèn)題:一,要部署在哪幾個(gè)IDC。二,每個(gè)IDC部署的服務(wù)器數(shù)量。三,DNS如何按區(qū)域劃片。
要部署在哪幾個(gè)IDC ?
其實(shí)這里還涉及到規(guī)模化應(yīng)用的好處,一個(gè)小應(yīng)用就部署了N多個(gè)IDC顯然不劃算。如果我的應(yīng)用上了規(guī)模,我可以在每個(gè)省都部署上,那樣用戶體驗(yàn)將非常好,而且規(guī)模化以后會(huì)有專業(yè)人員對(duì)應(yīng)用進(jìn)行優(yōu)化。所以公司里有動(dòng)態(tài)池,和靜態(tài)池這樣的公用平臺(tái)是好事(也許將來(lái)還會(huì)有我的js池)。如果我們的服務(wù)還沒(méi)有上升到公司級(jí)別的規(guī)模,那就得考慮下取舍。
網(wǎng)通:東北三省,可以在沈陽(yáng)和哈爾濱選擇一個(gè)部署,有條件可以都部署。沈陽(yáng)到北京的速率比哈爾濱到北京的速率快一倍,而哈爾濱到沈陽(yáng)的速率,還不如到北京的快。北京,如果只讓我在網(wǎng)通部署一個(gè)點(diǎn)的話,毫無(wú)疑問(wèn)我會(huì)選擇北京,其他所有結(jié)點(diǎn)到它的速率都比較快,但是北京的帶寬比較昂貴。天津,這個(gè)點(diǎn)重要性僅次于北京,可以輻射河北,河南,江蘇,離北京也比較近,價(jià)錢(qián)便宜。太原,可以輻射到西北一帶。山東,前面已經(jīng)說(shuō)過(guò),最好要部署的。
電信:那七個(gè)核心結(jié)點(diǎn)上部署了,速度就有保證。具體覆蓋范圍。廣州覆蓋周邊幾省,上海覆蓋本地,江浙一帶。武漢覆蓋華中一帶,西安可以覆蓋西北5省,成都覆蓋西南5省。
每個(gè)IDC部署的服務(wù)器數(shù)量?
這要根據(jù)具體應(yīng)用來(lái)決定。比如財(cái)經(jīng)用戶網(wǎng)通,電信比例:3:4 而體育是 1:2 。教育網(wǎng)用戶一般占1/30左右。這里還不能單純考慮用戶分布,還要考慮IDC內(nèi)部災(zāi)備和IDC間災(zāi)備,是要有個(gè)取舍的。拿咱們的某個(gè)具體項(xiàng)目來(lái)說(shuō),教育網(wǎng),夠不上一臺(tái)服務(wù)器,但是不得不部,因?yàn)樗L問(wèn)外界實(shí)在太慢了,我就住在學(xué)校里,也為了方便自己。我把北京作為主要結(jié)點(diǎn)部署了3臺(tái),天津,其實(shí)一臺(tái)就夠了,山東一臺(tái)有點(diǎn)多。但是考慮到北京IDC一旦倒了,實(shí)力相當(dāng)?shù)腎DC可以災(zāi)備,同時(shí)考慮到,天津,和山東只有一臺(tái),idc內(nèi)部,都無(wú)法實(shí)現(xiàn)災(zāi)自動(dòng)切換。所以,我選擇天津兩臺(tái),山東不部署,以性能換安全。
DNS如何按區(qū)域劃片?
原則,就近分片,以達(dá)到最快傳輸速率。其次,考慮到各IDC間快速切換比較容易,DNS解析文件要寫(xiě)的簡(jiǎn)潔一些。另外,DNS解析有有個(gè)缺陷,每個(gè)單獨(dú)域名里寫(xiě)在最前面的那個(gè)ip,它被輪詢到的概率要比同組的服務(wù)器高10%,而且隨著同組服務(wù)器的增多,這個(gè)差距會(huì)變大。所以最解析時(shí)候,每個(gè)IDC我都把硬件性能最好的服務(wù)器ip放在最前面。
另外:
做系統(tǒng)架構(gòu)不提數(shù)據(jù)庫(kù),有點(diǎn)過(guò)不去。這塊問(wèn)題可以請(qǐng)教我們的DBA大牛zongwen同學(xué)。數(shù)據(jù)庫(kù)是我將來(lái)一年的學(xué)習(xí)重點(diǎn),爭(zhēng)取一年后在DB方面能達(dá)到我們DBA六層功力。
網(wǎng)站加速--Cache為王篇
一,Cache, 王道也
二,Cache 基本原理介紹
三,我劃分的3個(gè)刷新級(jí)別
四,我對(duì)HTTP協(xié)議做的一點(diǎn)創(chuàng)新(?maxage=6000000)
五,Yslow優(yōu)化網(wǎng)站性能的14條軍規(guī)點(diǎn)評(píng)
六,上線了 !=? Finished
七,提速度同時(shí)節(jié)約成本方法匯總
-----------------------------------------------------------------------------------------
一,Cache,王道也
我覺(jué)得系統(tǒng)架構(gòu)不應(yīng)該僅僅是搭建一個(gè)強(qiáng)硬的能承受巨大并發(fā)壓力的后臺(tái),前端頁(yè)面也是需要架構(gòu)的而且同等重要,不理解前臺(tái)的的后臺(tái)工程師是不合格的。中國(guó)人講究鋼柔相濟(jì),后臺(tái)強(qiáng)硬只能說(shuō)你內(nèi)功深厚,前端用的巧,那叫四兩撥千斤。
一般后臺(tái)工程師很少關(guān)心前端如何使用自己的資源,而前端工程師,不知道自己的一個(gè)簡(jiǎn)單的用法會(huì)對(duì)后端造成多大影響。我會(huì)給出一些數(shù)據(jù),來(lái)震撼下你的眼球。
二,Cache 基本原理介紹 (參考Caching Tutorial)
為什么使用Cache?
1,減少延遲,讓你的網(wǎng)站更快,提高用戶體驗(yàn)。
2,避免網(wǎng)絡(luò)擁塞,減少請(qǐng)求量,減少輸出帶寬。
補(bǔ)充一個(gè)cache的原則:不更新的資源就不應(yīng)該讓它再次產(chǎn)生HTTP請(qǐng)求,如果強(qiáng)制產(chǎn)生了請(qǐng)求,那么就看看能否返回304。
Cache的種類?
瀏覽器Cache,代理Cache,網(wǎng)關(guān)Cache。
后端還有 disk cache ,server cache,php cache,不過(guò)不屬于我們今天討論范圍。
Cache如何工作的?
1,如果響應(yīng)頭告訴cache別緩存它,cache不對(duì)它做緩存;
2,如果請(qǐng)求需要驗(yàn)證的或者是需要安全性的,它將不被緩存;
3,如果響應(yīng)頭里沒(méi)有ETag或Last-Modifed header這類元素,而且也沒(méi)有任何顯式的信息告訴如何對(duì)數(shù)據(jù)保鮮,則它被認(rèn)為不可緩存。
4,在下面情況下,一個(gè)緩存項(xiàng)被認(rèn)為是新鮮的(即,不需到原server上檢查就可直接發(fā)送給client):
??? 它設(shè)置了一個(gè)過(guò)期時(shí)間或age-controlling響應(yīng)頭,而且現(xiàn)在仍未過(guò)期。
??? 如果瀏覽器cache里有某個(gè)數(shù)據(jù)項(xiàng),并且被被設(shè)置為每個(gè)會(huì)話(session)過(guò)程中只檢查一次;
??? 如果一個(gè)代理cache里能找個(gè)某個(gè)數(shù)據(jù)項(xiàng),并且它是在相對(duì)較長(zhǎng)時(shí)間之前更新過(guò)的。
??? 以上情況會(huì)認(rèn)為數(shù)據(jù)是新鮮的,就直接走cache,不再查詢?cè)磗erver。
5,如果有一項(xiàng)過(guò)期了,它將會(huì)讓原server去更新它,或者告訴cache這個(gè)拷貝是否還是可用的。
怎么控制你的Cache?
Meta tags :在html頁(yè)面中指定,這個(gè)方法只被少數(shù)瀏覽器支持,Proxy一般不會(huì)讀你html的具體內(nèi)容然后再做cache決策的。
Pragma: no-cache : 一般被大家誤用在http響應(yīng)頭中,這不會(huì)產(chǎn)生任何效果。而實(shí)際它僅僅應(yīng)該用在請(qǐng)求頭中。不過(guò)google的Server: GFE/1.3 響應(yīng)中卻這樣用,難道人家也誤用了呢。
Date: 當(dāng)前主機(jī)GMT時(shí)間。
Last-Modified : 文件更新GMT時(shí)間,我在響應(yīng)頭中帶上這個(gè)元素的時(shí)候,通常瀏覽器在cache時(shí)間內(nèi)再發(fā)請(qǐng)求都會(huì)稍帶上If-Modified-Since,讓我們判斷需要重新傳輸文件內(nèi)容,還是僅僅返回個(gè)304告訴瀏覽器資源還沒(méi)更新,需要緩存策略的服務(wù)器肯定都得支持的。有了這個(gè)請(qǐng)求,head請(qǐng)求在基本沒(méi)太多用處了,除非在telnet上調(diào)試還能用上。
If-Modified-Since :? 用在請(qǐng)求頭里,見(jiàn)Last-Modified 。
Etag: 標(biāo)識(shí)資源是否發(fā)生變化,etag的生成算法各是各樣,通常是用文件的inode+size+LastModified進(jìn)行Hash后得到的,可以根據(jù)應(yīng)用選擇適合自己的。Last-Modified 只能精確到秒的更新,如果一秒內(nèi)做了多次更新,etag就能派上用場(chǎng)。貌似大家很少有這樣精確的需求,浪費(fèi)了http header的字節(jié)數(shù),建議不要使用。
Expires :? 指定緩存到期GMT的絕對(duì)時(shí)間,這個(gè)是http 1.0里就有的。這個(gè)元素有些缺點(diǎn),一,服務(wù)器和瀏覽器端時(shí)間不一致時(shí)會(huì)有問(wèn)題。二,一旦失效后如果忘記重新設(shè)置新的過(guò)期時(shí)間會(huì)導(dǎo)致cache失效。三,服務(wù)器端需要根據(jù)當(dāng)前Date時(shí)間 + 應(yīng)該cache的相對(duì)時(shí)間去計(jì)算這個(gè)值,需要cpu開(kāi)銷。 我不推薦使用。
Cache-Control:
這個(gè)是http 1.1中為了彌補(bǔ) Expires 缺陷新加入的,現(xiàn)在不支持http 1.1的瀏覽器已經(jīng)很少了。
max-age: 指定緩存過(guò)期的相對(duì)時(shí)間秒數(shù),max-ag=0或者是負(fù)值,瀏覽器會(huì)在對(duì)應(yīng)的緩存中把Expires設(shè)置為1970-01-01 08:00:00 ,雖然語(yǔ)義不夠透明,但卻是我最推薦使用的。
s-maxage: 類似于max-age,只用在共享緩存上,比如proxy.
public: 通常情況下需要http身份驗(yàn)證的情況,響應(yīng)是不可cahce的,加上public可以使它被cache。
no-cache: 強(qiáng)制瀏覽器在使用cache拷貝之前先提交一個(gè)http請(qǐng)求到源服務(wù)器進(jìn)行確認(rèn)。這對(duì)身份驗(yàn)證來(lái)說(shuō)是非常有用的,能比較好的遵守 (可以結(jié)合public進(jìn)行考慮)。它對(duì)維持一個(gè)資源總是最新的也很有用,與此同時(shí)還不完全喪失cache帶來(lái)的好處,因?yàn)樗诒镜厥怯锌截惖?#xff0c;但是在用之前都進(jìn)行了確認(rèn),這樣http請(qǐng)求并未減少,但可能會(huì)減少一個(gè)響應(yīng)體。
no-store:? 告訴瀏覽器在任何情況下都不要進(jìn)行cache,不在本地保留拷貝。
must-revalidate: 強(qiáng)制瀏覽器嚴(yán)格遵守你設(shè)置的cache規(guī)則。
proxy-revalidate: 強(qiáng)制proxy嚴(yán)格遵守你設(shè)置的cache規(guī)則。
用法舉例:? Cache-Control: max-age=3600, must-revalidate
其他一些使用cache需要注意的東西,不要使用post,不要使用ssl,因?yàn)樗麄儾豢杀籧ache,另外保持url一致。只在必要的地方,通常是動(dòng)態(tài)頁(yè)面使用cookie,因?yàn)閏oolie很難cache。至于apache如何支持cache和php怎么用header函數(shù)設(shè)置cache,暫不做介紹,網(wǎng)上資料比較多。
如何設(shè)置合理的cache時(shí)間 ?
http://image2.sinajs.cn/newchart/min/n/sz000609.gif?1230015976759
拿我分時(shí)圖舉例,我們需要的更新頻率是1分鐘。但為了每次都拿到最新的資源,我們?cè)诤竺婕恿藗€(gè)隨機(jī)數(shù),這個(gè)數(shù)在同一秒內(nèi)的多次刷新都會(huì)變化。我們的js雖然能夠很好的控制,一分鐘只請(qǐng)求一次,但是如果用戶點(diǎn)了刷新按紐呢?這樣的調(diào)用是完全cache無(wú)關(guān)的,連返回304的機(jī)會(huì)都沒(méi)有。
試想,如果很多人通過(guò)同一個(gè)代理出去的,那么所有的請(qǐng)求都會(huì)穿透代理,弄不好被網(wǎng)管封掉了。如果我們做只做一秒的cache,對(duì)直接訪問(wèn)源服務(wù)器的用戶沒(méi)太多影響,但對(duì)于代理服務(wù)器來(lái)說(shuō),他的請(qǐng)求可能會(huì)從10000 req/min 減少為 60 req/min ,這是160倍。
對(duì)于我們行情圖片這樣的情況,刷新頻率為1分鐘,比較好的做法是把后面的隨機(jī)數(shù)(num)修改為 num=t-t%60 其中t是當(dāng)前時(shí)間戳,這樣你一分鐘內(nèi)刷這個(gè)url是不變的,下一分鐘會(huì)增加1,會(huì)再次產(chǎn)生一個(gè)新請(qǐng)求。而我的max-age設(shè)置為默認(rèn)59秒,即使設(shè)置120秒其實(shí)也沒(méi)什么影響。可能你會(huì)說(shuō)萬(wàn)一趕上臨界點(diǎn)可能拿不到最新的數(shù)據(jù),其實(shí)對(duì)用戶來(lái)說(shuō),用那個(gè)多變的隨即數(shù)和我這個(gè)分鐘級(jí)的隨即數(shù),看到的效果是相同的下面我給你分析一下:如果用戶打開(kāi)了我們的分時(shí)間頁(yè)面,當(dāng)前隨即數(shù)對(duì)他來(lái)說(shuō)是新的,所以他會(huì)拿到一個(gè)當(dāng)前最新的圖片,然后他點(diǎn)了刷新按紐,用戶會(huì)產(chǎn)生http請(qǐng)求,即使url沒(méi)變,服務(wù)器有最新圖片也一定會(huì)返回,否則返回304,一分鐘后js刷新圖片,分鐘數(shù)加了1,會(huì)得到全新資源。這和那個(gè)隨時(shí)變化的隨即數(shù)效果有區(qū)別嗎?都拿到了最新的數(shù)據(jù),但是卻另外收益了cache帶來(lái)的好處,對(duì)后端減少很多壓力。
三,我劃分的3個(gè)刷新級(jí)別
名詞解釋 全新請(qǐng)求: url產(chǎn)生了變化,瀏覽器會(huì)把他當(dāng)一個(gè)新的資源(發(fā)起新的請(qǐng)求中不帶If-Modified-Since)。
1,在地址欄中輸入http://sports.sinajs.cn/today.js?maxage=11地址按回車。重復(fù)n次,直到cache時(shí)間11秒過(guò)去后,才發(fā)起請(qǐng)求,這個(gè)請(qǐng)求是全新的,不帶If-Modified-Since。
2,按F5刷新.? 發(fā)起一個(gè)全新的請(qǐng)求,然后按F5會(huì)產(chǎn)生一個(gè)帶If-Modified-Since的請(qǐng)求,如果返回304,將不再發(fā)起新的請(qǐng)求,直到第一次請(qǐng)求設(shè)置的cache過(guò)期,然后發(fā)起一個(gè)全新的請(qǐng)求。
3, ctrl+F5 ,總會(huì)發(fā)起一個(gè)全新的請(qǐng)求。
下面是按F5刷新的例子演示: http://sports.sinajs.cn/today.js?maxage=11
( 如果這個(gè)值大于瀏覽器最大cache時(shí)間maxage,將以瀏覽器最大cache為準(zhǔn))
----------------------------------------------------------發(fā)起一個(gè)全新請(qǐng)求
GET /today.js?maxage=11 HTTP/1.1
Host: sports.sinajs.cn
Connection: keep-alive
HTTP/1.x 200 OK
Server: Cloudia
Last-Modified: Mon, 24 Nov 2008 11:03:02 GMT
Cache-Control: max-age=11??? (瀏覽器會(huì)cache這個(gè)頁(yè)面內(nèi)容,然后將cache過(guò)期時(shí)間設(shè)置為當(dāng)前時(shí)間+11秒)
Content-Length: 312
Connection: Keep-Alive
---------------------------------------------------------- 按F5刷新
GET /today.js?maxage=11 HTTP/1.1
Host: sports.sinajs.cn
Connection: keep-alive
If-Modified-Since: Mon, 24 Nov 2008 11:03:02 GMT?? (按F5刷新,If-Modified-Since將上次服務(wù)器傳過(guò)來(lái)的Last-Modified時(shí)間帶過(guò)來(lái))
Cache-Control: max-age=0
HTTP/1.x 304 Not Modified??
Server: Cloudia
Connection: Keep-Alive
Cache-Control: max-age=11?? (這個(gè)max-age有些多余,瀏覽器發(fā)現(xiàn)Not Modified,將使用本地cache數(shù)據(jù),但不會(huì)重新設(shè)置本地過(guò)期時(shí)間)
----------------------------------------------------------
繼續(xù)按F5刷新n次.......
這11秒內(nèi)未產(chǎn)生http請(qǐng)求.直到11秒過(guò)去了...............
----------------------------------------------------------按F5刷新
GET /today.js?maxage=11 HTTP/1.1
Host: sports.sinajs.cn
Connection: keep-alive? (cache過(guò)期后,發(fā)起的是一個(gè)全新的請(qǐng)求,未帶)
HTTP/1.x 200 OK
Server: Cloudia
Last-Modified: Mon, 24 Nov 2008 11:03:02 GMT
Cache-Control: max-age=11
Content-Encoding: deflate
Content-Length: 312
Connection: Keep-Alive
Content-Type: application/x-javascript
----------------------------------------------------------按F5刷新
GET /today.js?maxage=11 HTTP/1.1
Host: sports.sinajs.cn
Connection: keep-alive
If-Modified-Since: Mon, 24 Nov 2008 11:03:02 GMT
Cache-Control: max-age=0
HTTP/1.x 304 Not Modified
Server: Cloudia
Connection: Keep-Alive
Cache-Control: max-age=11
----------------------------------------------------------
四,我對(duì)HTTP協(xié)議做的一點(diǎn)創(chuàng)新(?maxage=6000000)
上面看到了url后面有? ?maxage=xx? 這樣的用法,這不是一個(gè)普通的參數(shù),作用也不僅僅是看起來(lái)那么簡(jiǎn)單。他至少有以下幾個(gè)好處:
1,可以控制HTTP header的的 max-age 值。
2, 讓用戶為每個(gè)資源靈活定制精確的cache時(shí)間長(zhǎng)度。
3, 可以代表資源版本號(hào)。
首先談?wù)搶?duì)后端的影響:
服務(wù)器實(shí)現(xiàn)那塊,不用再load類似mod_expires,mod_headers 這樣額外的module,也不用去加載那些規(guī)則去比較,它屬于什么目錄,或者什么文件類型,應(yīng)該cache多少時(shí)間,這樣的操作是需要開(kāi)銷的。
再說(shuō)說(shuō)對(duì)前端的影響:
比如同一個(gè)分時(shí)行情圖片,我們的分時(shí)頁(yè)中需要1分鐘更新,而某些首頁(yè)中3分鐘更新好。不用js控制的話,那我cache應(yīng)該設(shè)置多少呢??? 有了maxage就能滿足這種個(gè)性化定制需求。
另一種情況是,我們?yōu)榱薱ache,把某個(gè)圖片設(shè)置了一個(gè)永久cache,但是由于需求,我必須更新這個(gè)圖片,那怎么讓用戶訪問(wèn)到這個(gè)更新了的圖片呢?從yahoo的資料和目前所有能找到的資料中都描述了同一種方法,更改文件名字,然后引用新的資源。 我覺(jué)得這方法太土, 改名后,老的還不能刪除,可能還有地方在用,同一資源可能要存兩份,再修改,又得改個(gè)名,存3份,不要不把inode當(dāng)資源。我就不那樣做,只需要把maxage=6000000 修改成 maxage=6000001 ,問(wèn)題就解決了。
maxage=6000000 所產(chǎn)生的威力 (內(nèi)存塊消耗減少了250倍? ,請(qǐng)求數(shù)減少了37倍) :
體育那邊要上一個(gè)新功能,一開(kāi)始動(dòng)態(tài)獲取那些數(shù)據(jù),我覺(jué)得那樣太浪費(fèi)動(dòng)態(tài)池資源,就讓他們把xml文件到轉(zhuǎn)移到我的js池上來(lái),為了方便,他們把那個(gè)84k的flash文件也放在了一起,而且是每個(gè)用戶必須訪問(wèn)的。說(shuō)實(shí)在的,我不歡迎這種大塊頭,因?yàn)樗豢蓧嚎s,按正常來(lái)說(shuō),它應(yīng)該代表一個(gè)3M的文件。我的服務(wù)器只這樣設(shè)計(jì)的,如果一次發(fā)送不完的就暫存在內(nèi)存里,每個(gè)內(nèi)存塊10k,如果不帶參數(shù)默認(rèn)maxage=120 。 我發(fā)現(xiàn),由于這個(gè)文件,10w connections的時(shí)候,我消耗了10000個(gè)內(nèi)存塊。我自己寫(xiě)的申請(qǐng)連續(xù)內(nèi)存的算法也是消耗cpu地,一個(gè)84k的文件,發(fā)送一次后,剩余的64k就應(yīng)該能裝的下,于是我把最小內(nèi)存塊大小改為64K。 這樣消耗10w conn的時(shí)候消耗1500個(gè)左右內(nèi)存快,雖然內(nèi)存消耗總量沒(méi)怎么變小,但是它能更快的拿到64K的連續(xù)內(nèi)存資源,cpu也節(jié)約下來(lái)了。接下來(lái)我讓meijun把所應(yīng)用的flash資源后面加上maxage=6000000 (大概=79天,瀏覽器端最長(zhǎng)cache能達(dá)到著個(gè)就不錯(cuò)了), 10w connections的時(shí)候,只消耗了不到40個(gè)內(nèi)存塊,也就是說(shuō)內(nèi)存塊消耗減少了250倍? ,請(qǐng)求數(shù)減少了37倍。? 35w+ connections, 5.67w req/s的時(shí)候也就消耗100塊左右,比線性增加要少很多。也就是這點(diǎn)發(fā)現(xiàn)讓我有了做這個(gè)技術(shù)分享的沖動(dòng),其他都是順便講講。
五,Yslow優(yōu)化網(wǎng)站性能的14條軍規(guī)點(diǎn)評(píng)
其中黑色部分,跟后端是緊密相連的,在我們的內(nèi)容中都已經(jīng)涉及到了,而且做了更深入的討論。蘭色部分,5,6,7是相關(guān)頁(yè)面執(zhí)行速度的,構(gòu)建前端頁(yè)面的人應(yīng)該注意的。 11屬于避免使用的方法。 紅色部分我著重說(shuō)一下:
gzip 我不推薦使用,因?yàn)橛行┰缙贗E支持的不好,它的表現(xiàn)為直接用IE訪問(wèn)沒(méi)問(wèn)題,用js嵌進(jìn)去,就不能正常解壓。這樣的用戶占比應(yīng)該在2%左右。這個(gè)問(wèn)題我跟蹤了近一個(gè)月,差點(diǎn)放棄使用壓縮。后來(lái)發(fā)現(xiàn)我以前用deflate壓縮的文件卻能正常訪問(wèn)。 改用deflate問(wèn)題解決。apache 1.x使用mod_gzip ,到了 2.x 改用cmod_deflate,不知道是否跟這個(gè)原因有關(guān)。 另外對(duì)于小文件壓縮來(lái)說(shuō),deflate 可比 gzip 省不少字節(jié)。
減少 DNS 查詢: 這里也是有個(gè)取舍的,一般瀏覽器最多只為一個(gè)域名創(chuàng)建兩個(gè)連接通道。 如果我一個(gè)頁(yè)面嵌了 image.xx.com 的很多圖片,你就會(huì)發(fā)現(xiàn),圖片從上往下一張張顯示出來(lái)這個(gè)過(guò)程。這造成了瀏覽器端的排隊(duì)。 我們可以通過(guò)增加域名提高并發(fā)度,例如 image0.xx.com ,image1.xx.com ,image2.xx.com,image3.xx.com 這樣并發(fā)度就提上去了,但是會(huì)造成很多cache失效,那很簡(jiǎn)單,假如我們對(duì)文件名相加,對(duì)4取mod,就能保證,某個(gè)圖片只能通過(guò)某個(gè)域名進(jìn)行訪問(wèn)。不過(guò),我也很反對(duì)一頁(yè)面請(qǐng)求了數(shù)十個(gè)域名,很多域名下只有一到兩個(gè)資源的做法,這樣的時(shí)間開(kāi)銷是不劃算的。
另外,我在這里再添一個(gè)第15條:錯(cuò)開(kāi)資源請(qǐng)求時(shí)間,避免瀏覽器端排隊(duì)。
隨著ajax的廣泛使用,動(dòng)態(tài)刷新無(wú)處不在,體育直播里有個(gè)頁(yè)面調(diào)用了我一個(gè)域名下的6個(gè)文件,3個(gè)js,3個(gè)xml。刷新頻率大致是兩個(gè)10秒的,兩個(gè)30秒的,兩個(gè)一次性載入的。觀察發(fā)現(xiàn)正常響應(yīng)時(shí)間都在7ms,但是每過(guò)一會(huì)就會(huì)出現(xiàn)一次在100ms以上的,我就很奇怪,服務(wù)器負(fù)載很輕呢。meijun幫我把刷新時(shí)間錯(cuò)開(kāi),11秒的,9秒的,31秒的,這樣響應(yīng)在100ms以上的概率減少了好幾倍,這就是所謂的細(xì)節(jié)決定成敗吧。
1. 盡可能的減少 HTTP 的請(qǐng)求數(shù)???? [content]
2. 使用 CDN(Content Delivery Network)???? [server]
3. 添加 Expires 頭(或者 Cache-control )???? [server]
4. Gzip 組件???? [server]
5. 將 CSS 樣式放在頁(yè)面的上方???? [css]
6. 將腳本移動(dòng)到底部(包括內(nèi)聯(lián)的)???? [javascript]
7. 避免使用 CSS 中的 Expressions???? [css]
8. 將 JavaScript 和 CSS 獨(dú)立成外部文件???? [javascript] [css]
9. 減少 DNS 查詢???? [content]
10. 壓縮 JavaScript 和 CSS (包括內(nèi)聯(lián)的)???? [javascript] [css]
11. 避免重定向???? [server]
12. 移除重復(fù)的腳本???? [javascript]
13. 配置實(shí)體標(biāo)簽(ETags)???? [css]
14. 使 AJAX 緩存???
六,上線了 !=? Finished
奧運(yùn)期間我按1500w~2000w connections在線,設(shè)計(jì)了一套備用系統(tǒng),現(xiàn)在看來(lái),如果用戶真達(dá)到了這個(gè)數(shù)目我會(huì)很危險(xiǎn),因?yàn)橛胁糠址?wù)器引入了32bit的centos 5未經(jīng)實(shí)際線上檢驗(yàn),而我當(dāng)時(shí)簡(jiǎn)單的認(rèn)為它應(yīng)該和centos 4表現(xiàn)出一樣的特性。所以現(xiàn)在未經(jīng)過(guò)完全測(cè)試的lib庫(kù)和新版本,我都很謹(jǐn)慎的使用。沒(méi)在真實(shí)環(huán)境中檢驗(yàn)過(guò),不能輕易下結(jié)論。
很多項(xiàng)目組好象不停的忙,做新項(xiàng)目,上線后又繼續(xù)下個(gè)新項(xiàng)目,然后時(shí)不時(shí)的轉(zhuǎn)過(guò)頭去修理以前的bug。如果一個(gè)項(xiàng)目上線后,用戶量持續(xù)上升,就應(yīng)該考慮優(yōu)化了,一個(gè)人訪問(wèn),和100w人訪問(wèn),微小的修改對(duì)后端影響是不能比較的,不該請(qǐng)求的資源就讓它c(diǎn)ache在用戶的硬盤(pán)上,用戶訪問(wèn)塊了,你也省資源。上線僅僅代表可以交差了而已,對(duì)于技術(shù)人員來(lái)說(shuō)持續(xù)的對(duì)一個(gè)重要項(xiàng)目進(jìn)行跟蹤和優(yōu)化是必要的。
七,提速度同時(shí)節(jié)約成本方法匯總
1,編寫(xiě)節(jié)約的HTTP服務(wù)器 (高負(fù)載下速度明顯提升,節(jié)約5~10倍服務(wù)器)
對(duì)一些重要的服務(wù)器量身定做。或者選用比較高效的開(kāi)源軟件進(jìn)行優(yōu)化。
2,不同服務(wù)混合使用? (節(jié)約1~2倍服務(wù)器)
如果我們一臺(tái)服務(wù)器只支持30w conn的話,那么剩余的75% cpu資源,95%的內(nèi)存資源,和幾乎所有的磁盤(pán)資源都可以部署動(dòng)態(tài)池系統(tǒng),我覺(jué)得DB對(duì)網(wǎng)卡中斷的消耗還是有限的,我也不用新買(mǎi)網(wǎng)卡了。
3,對(duì)于純數(shù)據(jù)部分啟用新的域名(速度有提升,上行帶寬節(jié)約1倍以上)
比如我們另外購(gòu)買(mǎi)了sinajs.cn 來(lái)做數(shù)據(jù)服務(wù),以避免cookie,節(jié)約帶寬. Cookie不但會(huì)浪費(fèi)服務(wù)器端處理能力,而且它要上行數(shù)據(jù),而通常情況上行比下行慢。
4, 使用長(zhǎng)連接 (速度明顯提升,節(jié)約帶寬2倍以上,減少網(wǎng)絡(luò)擁塞3~無(wú)數(shù)倍)
對(duì)于一次性請(qǐng)求多個(gè)資源,或在比較短的間隔內(nèi)會(huì)有后續(xù)請(qǐng)求的應(yīng)用,使用長(zhǎng)連接能明顯提升用戶體驗(yàn),減少網(wǎng)絡(luò)擁塞,減少后端服務(wù)器建立新連接的開(kāi)銷。
5,數(shù)據(jù)和呈現(xiàn)分離,靜態(tài)數(shù)據(jù)和動(dòng)態(tài)數(shù)據(jù)分離 (速度明顯提升,同時(shí)節(jié)約3倍帶寬)
div+css 數(shù)據(jù)和呈現(xiàn)分離以后,據(jù)說(shuō)文件大小能降到以前的1/3。
把頁(yè)面中引用的js文件分離出來(lái),把動(dòng)態(tài)部分和靜態(tài)部分也分離開(kāi)來(lái)。
6,使用deflate壓縮算法 (速度明顯提升,節(jié)約3.33倍帶寬)
一般來(lái)說(shuō)壓縮過(guò)的文件大小不到以前的30% 。
將上面分離出來(lái)的數(shù)據(jù)進(jìn)行壓縮(累計(jì)節(jié)約帶寬10倍)。
7, 讓用戶盡可能多的Cache你的資源 (速度明顯提升,節(jié)約3~50倍服務(wù)器資源和帶寬資源)
將上面分離出來(lái)的css和不經(jīng)常變動(dòng)的js數(shù)據(jù)部分cache住合適的時(shí)間。(理想情況,累計(jì)節(jié)約帶寬30~500倍) 。
以上改進(jìn)可以讓速度大幅度提升的同時(shí),服務(wù)器資源節(jié)約 5~20 倍 ,減少網(wǎng)絡(luò)擁塞3~無(wú)數(shù)倍, 上行帶寬節(jié)約1倍以上,下行帶寬節(jié)約30~500倍,甚至更多。
網(wǎng)站加速--實(shí)例分析篇
一,自選股分析
二,NBA比賽分析
三,播客分析
四,開(kāi)心網(wǎng)分析
-----------------------------------------------------------------------------------------
下面的圖片都是在教育網(wǎng)訪問(wèn)的情況,我故意放大了某些缺陷,這樣可以很好的模擬沒(méi)有部署服務(wù)的地區(qū)對(duì)用戶體驗(yàn)的影響。我只能針對(duì)我熟悉和了解的項(xiàng)目進(jìn)行分析,另外還有我們經(jīng)常訪問(wèn)的網(wǎng)站也會(huì)被拿來(lái)做素材分析。我們老大也說(shuō)問(wèn)題暴露出來(lái),能推動(dòng)解決的話也很好,大家別拍我。
一,自選股分析
某天我終于在教育網(wǎng)部署了一臺(tái)行情服務(wù),興致沖沖的回家一試,貌似沒(méi)啥變化,該慢還慢。打開(kāi)頁(yè)面過(guò)程持續(xù)了幾十秒,然后終于露出了行情,我再電擊每個(gè)組合的時(shí)候就出現(xiàn)了上面的一幕。看了下firebug,最慢資源排名前三依次為:高效計(jì)數(shù)服務(wù),secure-cn統(tǒng)計(jì)服務(wù),動(dòng)態(tài)池服務(wù)。
高效計(jì)數(shù)服務(wù)是早期我參與的項(xiàng)目,那時(shí)候資源有限,全部部署在了網(wǎng)通。
secure-cn統(tǒng)計(jì)服務(wù): 這個(gè)服務(wù)不慢是不正常的,到處都嵌,還不能不嵌。
動(dòng)態(tài)池?cái)?shù)據(jù)庫(kù)很牛,但在偏遠(yuǎn)地區(qū)也鞭長(zhǎng)莫及。這個(gè)缺點(diǎn)比較典型:
一,沒(méi)有在教育網(wǎng)部署。
二,沒(méi)有保持長(zhǎng)連接。
三,沒(méi)有使用cahce
四,沒(méi)有使用壓縮
五,長(zhǎng)達(dá)2.46K的http 請(qǐng)求header,捎帶大量cookie,見(jiàn)下面。
解決方法:我分析了下,下面這個(gè)數(shù)據(jù)變化很慢的,主要放一些市盈率和用戶股票列表。市盈率可以通過(guò)去年的每股收益來(lái)計(jì)算,以年計(jì),可以變通一下。用戶股票列表我也好幾個(gè)月沒(méi)更新了,大家并不是總更新。所以這部分?jǐn)?shù)據(jù)是可以被設(shè)置一個(gè)很長(zhǎng)的cache的,如果用戶更新了股票列表,我們也只需要在maxage版本號(hào)上加1就ok了。另外,用戶點(diǎn)了一個(gè)組合,接看來(lái)也都要看幾個(gè)別的組合,沒(méi)有維持長(zhǎng)連接顯然不合理的。在沒(méi)有部點(diǎn)的idc,壓縮就能明顯的提升響應(yīng)速度,這里就沒(méi)考慮。那個(gè)cookie太長(zhǎng)點(diǎn)了吧,真的用的了那么長(zhǎng)嗎。
http://vip.stock.finance.sina.com.cn/portfolio/stock.php?rn=1228707043897&pid=1245111&type=complete
----------------------------------------------------------------------------------------
GET /portfolio/stock.php?rn=1228707043897&pid=1245111&type=complete HTTP/1.1
Host: vip.stock.finance.sina.com.cn
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.0.4) Gecko/2008102920 Firefox/3.0.4
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-cn,zh;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: gb2312,utf-8;q=0.7,*;q=0.7
Keep-Alive: 300
Connection: keep-alive
Cookie: CurrentBar=attend; CurrentTab=state; CombinationSelected=154148; CommisionCookie=0; StampCookie=0; FeeCookie=0; BX=7t1oh653u6qvb&b=3&s=4k; SINA_NEWS_CUSTOMIZE_city=%u5317%u4EAC; userId=C7DHwoAi-ryCr69CGgyc3czekbyphdy5hcxQNhFcN6zCNe; FINA_VISITED_S=sh601988|-y?L,sh580989|W*JTP1,sh601988|-y?L,sh601988|-y?L,sh580989|W*JTP1,sh601988|-y?L,sh601988|-y?L,sh601988|-y?L,sh580989|W*JTP1; Iask2_visitID=10.217.21.44.177601199668733612; UNIPROCT=342-0-0:2; hold_sinabar_name=iyangjian2005997; UNIPROPATH=2:iyangjian2005997:0::1:|*|202.112.174.100.97191204115419966|pid:342-0-0-0-0|classad.sr/|st:25.906|et:1204118703312||hp:unkown|lb:1|*|; SINAPUID=10.217.21.64.250871201592749264; vjuids=-5600fbe60.117402dbc5e.0.42a2debdf9f46; VISITED_FANCHAN_SINA_ZHANGYQ=SINA_BEIJING; S_WC_USRTOK=SFyLe9; stat=0806201608589720436533; MY_STOCK_LIST_2=sh600602; visited_futures=SI%7CCL%7CGC%7CCAD%7CTRB%7Cau0812%7CCC%7CPBD%7CCF907%7CNID; SINA_FINANCE=iyangjian2005997%3A1181509184%3A2; visited_funds=000011%7Csh000011%7C159902%7C160314%7C377016%7C270005%7C202009; SINA_FINANCE_SELECT_TYPE=stock; vjuid=-12b4fad5c.1174d78e8a5.0.6099c257a27eb; vjlast=1199616063; vjlast=1199616063,1228706963,10; sina_sort_default=117; SHOW_TIP_BOX=1; FINA_V_S_2=sz000609,sz000723,sh000001,sz002242,sz002274,sz000049,sz002272,sh600432,sh601186,sh601390,sh600036,sz000625; hk_visited_stocks=HSI%7C04338%7CHSCEI%7CHSCCI; visited_cfunds=050007%7Csz161010; __utma=269849203.390390911.1226996335.1226996335.1226996335.1; __utmz=269849203.1226996335.1.1.utmccn=(direct)|utmcsr=(direct)|utmcmd=(none); SINAGLOBAL=202.112.174.100.224381203683121713; Apache=202.112.174.100.771641228691763829; SessionID=e9bc0f217040ae10439d85f422f3187a; SINA_PORTFOLIO=sz000514%2Csh600729%2Csh600438%2Csh600528%2Csh600678%2Csh600877%2Csh600039%2Csh601005%2Csh600875%2Csz001696%2Csz000628%2Csh600116
Cache-Control: max-age=0
HTTP/1.x 200 OK
Date: Mon, 08 Dec 2008 03:32:52 GMT
Server: Apache
Cache-Control: no-cache
Expires: Mon, 08 Dec 2008 03:34:52 GMT
Connection: close
Transfer-Encoding: chunked
Content-Type: text/html; charset=GBK
----------------------------------------------------------------------------------------
如果不是這幾個(gè)資源的引用,這個(gè)頁(yè)面的速度將非常快。
?
這里引用了某些未在教育網(wǎng)部署的服務(wù),導(dǎo)致半天出不了數(shù)據(jù)。
?
由于引入了mark.sina.com.cn的數(shù)據(jù)導(dǎo)致整個(gè)頁(yè)面卡在那里。引用別人數(shù)據(jù)的時(shí)候你了解過(guò)他們是怎么分布自己服務(wù)的嗎?可能稍有不慎拖垮整個(gè)頁(yè)面。
二,NBA比賽分析
?
這里的js真的有必要每次都發(fā)起請(qǐng)求嗎?連續(xù)請(qǐng)求3同域個(gè)資源,為什么不維持下長(zhǎng)連接?
?
這些圖片的304響應(yīng)為什么都在秒級(jí)以上?
三,播客分析
?
這些圖片和視頻由于解析錯(cuò)誤,教育網(wǎng)用戶被解析到廣州服務(wù)器組,導(dǎo)致不可訪問(wèn)。
四,開(kāi)心網(wǎng)分析
打開(kāi)開(kāi)心網(wǎng),看到最多的就是人物圖片,我就僅僅針對(duì)圖片進(jìn)行下分析:
1,瀏覽一個(gè)新人的頁(yè)面,大概要下載30~40張小圖片。使用單一的pic.kaixin001.com域名,不能提高并發(fā),可以考慮多域名取模。
2,圖片請(qǐng)求帶了cookie,上行帶寬浪費(fèi)點(diǎn)無(wú)所謂,但是會(huì)影響響應(yīng)速度和用戶體驗(yàn)。
3, /logo/10/51/50_105146_1.jpg ,他們?cè)O(shè)置了一個(gè)比較大的maxage,通過(guò)改名來(lái)實(shí)現(xiàn)更新大可不必,我用我的方法更好。
4,每次點(diǎn)刷新頁(yè)面,都會(huì)重新加載很多圖片,雖然很多是304,我覺(jué)得絕大部分就不應(yīng)該發(fā)這個(gè)請(qǐng)求。
5,他用的是ChinaCache的CDN,Server: nginx,我不知道ChinaCache對(duì)這個(gè)server修改到什么程度。統(tǒng)計(jì)發(fā)現(xiàn)這個(gè)人物小圖片大都在2k左右。很多才1k多。沒(méi)有必要把他們當(dāng)作圖片處理。盡量不產(chǎn)生磁盤(pán)i/o,包括fstat這樣的系統(tǒng)調(diào)用,甚至sendfile這樣的zero copy系統(tǒng)調(diào)用,我覺(jué)得都浪費(fèi). 同時(shí)還要保證圖片更新立刻被感應(yīng)到。?
其他方面還有很多可以改進(jìn)的,想讓他們的頁(yè)面響應(yīng)速度上一個(gè)等級(jí),節(jié)約更多帶寬和服務(wù)器資源并非難事。
?
轉(zhuǎn)載于:https://www.cnblogs.com/hailuo/archive/2009/11/23/1608787.html
總結(jié)
以上是生活随笔為你收集整理的网站加速教程--提升性能的同时节约10倍成本的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 2021振兴杯参赛后感(部分writeu
- 下一篇: TopCoder SRM 682 Div