摘要: 作者:自由飛網(wǎng)址:http://www.cnblogs.com/freeflying/p/4788494.html我們?cè)谏弦黄都軜?gòu)之路(1):目標(biāo)》中設(shè)定了架構(gòu)的目標(biāo),只有一個(gè),就是可維護(hù)性。完全沒(méi)有提性能,這是故意的。似乎程序員都是急性子,或許是被windows冗長(zhǎng)的開機(jī)時(shí)間折磨夠了,有可能是因?yàn)樘嵘阅艿男Ч亲铒@而易見的……總之,我發(fā)現(xiàn),絕大部分程序員對(duì)性能的關(guān)注和...  閱讀全文
          posted @ 2015-09-25 17:00 小馬歌 閱讀(353) | 評(píng)論 (0)編輯 收藏
           
          from:http://blog.jobbole.com/91749/

          一、越來(lái)越多的并發(fā)連接數(shù)

          現(xiàn)在的Web系統(tǒng)面對(duì)的并發(fā)連接數(shù)在近幾年呈現(xiàn)指數(shù)增長(zhǎng),高并發(fā)成為了一種常態(tài),給Web系統(tǒng)帶來(lái)不小的挑戰(zhàn)。以最簡(jiǎn)單粗暴的方式解決,就是增加Web系統(tǒng)的機(jī)器和升級(jí)硬件配置。雖然現(xiàn)在的硬件越來(lái)越便宜,但是一味地通過(guò)增加機(jī)器來(lái)解決并發(fā)量的增長(zhǎng),成本是非常高昂的。結(jié)合技術(shù)優(yōu)化方案,才是更有效的解決方法。

          并發(fā)連接數(shù)為什么呈指數(shù)增長(zhǎng)?實(shí)際上,從這幾年的用戶基數(shù)上看,這個(gè)數(shù)量并沒(méi)有出現(xiàn)指數(shù)增長(zhǎng),因此它并非主要原因。主要原因,還是web變得更復(fù)雜,交互更豐富所導(dǎo)致的。

          1. 頁(yè)面元素增多,交互復(fù)雜

          Web頁(yè)面元素越來(lái)越多,更為豐富。更多的資源元素,意味著更多的下載請(qǐng)求。Web系統(tǒng)的交互越來(lái)越復(fù)雜,交互場(chǎng)景和次數(shù)也大幅增加。以“www.qq.com”的首頁(yè)為例子,刷新一次,大概會(huì)有244個(gè)請(qǐng)求。并且,在頁(yè)面打開完成之后,還會(huì)有一些定時(shí)的查詢或者上報(bào)請(qǐng)求持續(xù)運(yùn)作。

          目前的Http請(qǐng)求,為了減少反復(fù)的創(chuàng)建和銷毀連接行為,通常都建立長(zhǎng)連接(Connection keep-alive)。一經(jīng)建立,這個(gè)連接會(huì)被保持住一段時(shí)間,被后續(xù)請(qǐng)求復(fù)用。然而,它也帶來(lái)了另一個(gè)新的問(wèn)題,連接的保持是會(huì)占用Web系統(tǒng)服務(wù)端資源的,如果不充分使用這個(gè)連接,會(huì)導(dǎo)致資源浪費(fèi)。長(zhǎng)連接被創(chuàng)建后,首批資源傳輸完畢,之后幾乎沒(méi)有數(shù)據(jù)交互,一直到超時(shí)時(shí)間,才會(huì)自動(dòng)釋放長(zhǎng)連接占據(jù)的系統(tǒng)資源。

          除此之外,還有一些Web需求本身就需要長(zhǎng)期保持連接的,例如Web socket。

          2. 主流的本瀏覽器的連接數(shù)在增加

          面對(duì)越來(lái)越豐富的Web資源,主流瀏覽器并發(fā)連接數(shù)也在增加,同一個(gè)域下,早期的瀏覽器一般只有1-2個(gè)下載連接,而目前的主流瀏覽器通常在2-6個(gè)。增加瀏覽器并發(fā)連接數(shù)目,在需要下載資源比較多的場(chǎng)景下,可以加快頁(yè)面的加載速度。更多的連接對(duì)瀏覽器加載頁(yè)面元素是有好處的,在某些連接遭遇“網(wǎng)絡(luò)阻塞”的情況下,其他正常的下載連接可以繼續(xù)工作。

          這樣自然無(wú)形增加了Web系統(tǒng)后端的壓力,更多的下載連接意味著占據(jù)了更多的Web服務(wù)器的資源。而在用戶訪問(wèn)高峰期,自熱而然就形成了“高并發(fā)”場(chǎng)景。這些連接和請(qǐng)求,占據(jù)了服務(wù)器的大量CPU和內(nèi)存等資源。尤其在資源數(shù)目超過(guò)100+的網(wǎng)站頁(yè)面中,使用更多的下載連接,非常有必要。

           

          二、Web前端優(yōu)化,降低服務(wù)端壓力

          在緩解“高并發(fā)”的壓力,需要前端和后端的共同配合優(yōu)化,才能達(dá)到最大效果。在用戶第一線的Web前端,可以起到減少或者減輕Http請(qǐng)求的效果。

          1. 減少Web請(qǐng)求

          常用的實(shí)現(xiàn)方法是通過(guò)Http協(xié)議頭中的expire或max-age來(lái)控制,將靜態(tài)內(nèi)容放入瀏覽器的本地緩存,在之后的一段時(shí)間里,不再請(qǐng)求Web服務(wù)器,直接使用本地資源。還有HTML5中的本地存儲(chǔ)技術(shù)(LocalStorage),也被作為一個(gè)強(qiáng)大的數(shù)據(jù)本地緩存。

          這種方案緩存后,根本不發(fā)送請(qǐng)求到Web服務(wù)器,大幅降低服務(wù)器壓力,也帶來(lái)了良好的用戶體驗(yàn)。但是,這種方案,對(duì)首次訪問(wèn)的用戶無(wú)效,同時(shí),也影響部分Web資源的實(shí)時(shí)性。

          2. 減輕Web請(qǐng)求

          瀏覽器的本地緩存是存在過(guò)期時(shí)間的,一旦過(guò)期,就必須重新向服務(wù)器請(qǐng)求。這個(gè)時(shí)候,會(huì)有兩種情形:

          (1)服務(wù)器的資源內(nèi)容沒(méi)有更新,瀏覽器請(qǐng)求Web資源,服務(wù)器回復(fù)“可以繼續(xù)使用本地緩存”。(發(fā)生通信,但是Web服務(wù)器只需要做簡(jiǎn)單“回復(fù)”)

          (2)服務(wù)器的文件或者內(nèi)容已經(jīng)更新,瀏覽器請(qǐng)求Web資源,Web服務(wù)器通過(guò)網(wǎng)絡(luò)傳輸新的資源內(nèi)容。(發(fā)生通信,Web服務(wù)器需要完成復(fù)雜的傳輸工作)

          這里的協(xié)商方式是通過(guò)Http協(xié)議的Last-Modified或Etag來(lái)控制,這個(gè)時(shí)候請(qǐng)求服務(wù)器,如果是內(nèi)容沒(méi)有發(fā)生變更的情況,服務(wù)器會(huì)返回304 Not Modified。這樣的話,就不需要每次請(qǐng)求Web服務(wù)器都做復(fù)雜的傳輸完整數(shù)據(jù)文件的工作,只要簡(jiǎn)單的http應(yīng)答就可以達(dá)到相同的效果。

          雖然上述請(qǐng)求,起到“減輕”Web服務(wù)器的壓力,但是連接仍然被建立,請(qǐng)求也發(fā)生了。

          3. 合并頁(yè)面請(qǐng)求

          如果是比較老一些的Web開發(fā)者,應(yīng)該會(huì)更有印象,在ajax盛行之前。頁(yè)面大部分都是直接輸出的,并沒(méi)有這么多的ajax請(qǐng)求,Web后端將頁(yè)面內(nèi)容完全拼湊好了,再返回給前端。那個(gè)時(shí)候,頁(yè)面靜態(tài)化,是一個(gè)挺廣泛的優(yōu)化方式。后來(lái),被交互更友好的ajax漸漸替代了,一個(gè)頁(yè)面的請(qǐng)求也變得越來(lái)越多。

          由于移動(dòng)端的網(wǎng)絡(luò)(2G/3G)比起PC寬帶差很多,并且部分手機(jī)配置比較低,面對(duì)一個(gè)超過(guò)100個(gè)請(qǐng)求的網(wǎng)頁(yè),加載的速度會(huì)緩慢很多。于是,優(yōu)化的方向又重新回到合并頁(yè)面元素,減少請(qǐng)求數(shù)量:

          (1)合并HTML展示內(nèi)容。將CSS和JS直接嵌入到HTML頁(yè)面內(nèi),不通過(guò)連接的方式引入。

          (2)Ajax動(dòng)態(tài)內(nèi)容合并請(qǐng)求。對(duì)于動(dòng)態(tài)內(nèi)容,將10次Ajax請(qǐng)求合并為1次的批量信息查詢。

          (3)小圖片合并,通過(guò)CSS的偏移量技術(shù)Sprites,將很多小圖片合并為一張。這個(gè)優(yōu)化方式,在PC端的Web優(yōu)化中,也非常常見。

          合并請(qǐng)求,減少了傳輸數(shù)據(jù)的次數(shù),也就是相當(dāng)于將它們從一個(gè)一個(gè)地請(qǐng)求,變?yōu)橐淮蔚?#8220;批量”請(qǐng)求。上述優(yōu)化方法,到達(dá)“減輕”Web服務(wù)器壓力的目的,減少了需要建立的連接。

           

          三、 節(jié)約Web服務(wù)端的內(nèi)存

          前端的優(yōu)化完成,我們就需要著眼于Web服務(wù)端本身。內(nèi)存是Web服務(wù)器非常重要的資源,更多的內(nèi)存通常意味著可以同時(shí)放入更多的工作任務(wù)。就Web服務(wù)占用內(nèi)存而言,可以粗略劃分:

          (1)用來(lái)維持連接的基本內(nèi)存,進(jìn)程初始化時(shí),會(huì)載入一些基礎(chǔ)模塊到內(nèi)存。

          (2)被傳輸?shù)臄?shù)據(jù)內(nèi)容載入到各個(gè)緩沖區(qū),占據(jù)的內(nèi)存。

          (3)程序執(zhí)行過(guò)程中,申請(qǐng)和使用的內(nèi)存。

          如果維持一個(gè)連接,能夠盡可能少占用內(nèi)存,那么我們就可以維持更多的并發(fā)連接,從而讓W(xué)eb服務(wù)器支持更多的并發(fā)連接數(shù)。

          Apache(httpd)是一個(gè)成熟并且古老的Web服務(wù),而Apache的發(fā)展和演變,一直在追求做到這一點(diǎn),它試圖不斷減少服務(wù)占據(jù)的內(nèi)存,以支持更大的并發(fā)量。以Apache的工作模式的演變?yōu)橐暯牵覀円黄饋?lái)看看,它們是如何優(yōu)化內(nèi)存的問(wèn)題的。

          1. prefork MPM,多進(jìn)程工作模式

          prefork是Apache最成熟和穩(wěn)定的工作模式,即使是現(xiàn)在,仍然被廣泛使用。主進(jìn)程生成后,它先完成基礎(chǔ)的初始化工作,然后,通過(guò)fork預(yù)先產(chǎn)生一批的子進(jìn)程(子進(jìn)程會(huì)復(fù)制父進(jìn)程的內(nèi)存空間,不需要再做基礎(chǔ)的初始化工作)。然后等待服務(wù),之所以預(yù)先生成,是為了減少頻繁創(chuàng)建和銷毀進(jìn)程的開銷。多進(jìn)程的好處,是進(jìn)程之間的內(nèi)存數(shù)據(jù)不會(huì)相互干擾,同時(shí),某個(gè)進(jìn)程異常終止也不會(huì)影響其他進(jìn)程。但是,就內(nèi)存而言,每個(gè)httpd子進(jìn)程占用了很多的內(nèi)存,因?yàn)樽舆M(jìn)程的內(nèi)存數(shù)據(jù)是復(fù)制父進(jìn)程的。我們可以粗略認(rèn)為,這里存在大量的“重復(fù)數(shù)據(jù)”被放在內(nèi)存中。最終,導(dǎo)致我們能夠生成的子進(jìn)程最大數(shù)量是很有限。在面對(duì)高并發(fā)時(shí),因?yàn)橛胁簧貹eep-alive的長(zhǎng)連接,將這些子進(jìn)程“霸占”住,很可能導(dǎo)致可用子進(jìn)程耗盡。因此,prefork并不太適合高并發(fā)場(chǎng)景。

          • 優(yōu)點(diǎn):成熟穩(wěn)定,兼容所有新老模塊。同時(shí),不需要擔(dān)心線程安全的問(wèn)題。(例如,我們常用的mod_php,將PHP編譯為Apache的子模塊,就不需要支持線程安全)
          • 缺點(diǎn):一個(gè)服務(wù)進(jìn)程占用很多內(nèi)存。

          2. worker MPM,多進(jìn)程和多線程的混合模式

          worker模式比起prefork,是使用了多進(jìn)程和多線程的混合模式。它也預(yù)先f(wàn)ork了幾個(gè)子進(jìn)程(數(shù)量很少),然后每個(gè)子進(jìn)程創(chuàng)建一些線程(其中包括一個(gè)監(jiān)聽線程)。每個(gè)請(qǐng)求過(guò)來(lái),會(huì)被分配到1個(gè)線程來(lái)服務(wù)。線程比起進(jìn)程會(huì)更輕量,因?yàn)榫€程通常會(huì)共享父進(jìn)程的內(nèi)存空間,因此,內(nèi)存的占用會(huì)減少一些。在高并發(fā)的場(chǎng)景下,因?yàn)楸绕餻refork更省內(nèi)存,因此會(huì)有更多的可用線程。

          但是,它并沒(méi)有解決Keep-alive的長(zhǎng)連接“霸占”線程的問(wèn)題,只是對(duì)象變成了比較輕量的線程。

          有些人會(huì)覺(jué)得奇怪,那么這里為什么不完全使用多線程呢,還要引入多進(jìn)程?因?yàn)檫€需要考慮穩(wěn)定性,如果一個(gè)線程掛了,會(huì)導(dǎo)致同一個(gè)進(jìn)程下其他正常的子線程都掛了。如果全部采用多線程,某個(gè)線程掛掉,就導(dǎo)致整個(gè)Apache服務(wù)“全軍覆沒(méi)”。而目前的工作模式,受影響的只是Apache的一部分服務(wù),而不是整個(gè)服務(wù)。

          線程共享父進(jìn)程的內(nèi)存空間,減少了內(nèi)存的占用,卻又引起了新的問(wèn)題。就是“線程安全”,多個(gè)線程修改共享資源導(dǎo)致的“競(jìng)爭(zhēng)行為”,又強(qiáng)迫我們所使用的模塊必須支持“線程安全”。因此,它有一定程度上增加Web服務(wù)的不穩(wěn)定性。例如,mod_php所使用的PHP拓展,也同樣需要支持“線程安全”,否則,不能在該模式下使用。

          • 優(yōu)點(diǎn):占據(jù)更少的內(nèi)存,高并發(fā)下表現(xiàn)更優(yōu)秀。
          • 缺點(diǎn):必須考慮線程安全的問(wèn)題,同時(shí)鎖的引入又增加了CPU的開銷。

          3. event MPM,多進(jìn)程和多線程的混合模式,引入Epoll

          這個(gè)是Apache中比較新的模式,在現(xiàn)在的版本(Apache 2.4.10)已經(jīng)是穩(wěn)定可用的模式。它和worker模式很像,最大的區(qū)別在于,它解決了keep-alive場(chǎng)景下,長(zhǎng)期被占用的線程的資源浪費(fèi)問(wèn)題。event MPM中,會(huì)有一個(gè)專門的線程來(lái)管理這些keep-alive類型的線程,當(dāng)有真實(shí)請(qǐng)求過(guò)來(lái)的時(shí)候,將請(qǐng)求傳遞給服務(wù)線程,執(zhí)行完畢后,又允許它釋放。它減少了“占據(jù)”連接而又不使用的資源浪費(fèi),增強(qiáng)了高并發(fā)場(chǎng)景下的請(qǐng)求處理能力。因?yàn)闇p少了“閑等”的線程,線程的數(shù)量減少,同等場(chǎng)景下,內(nèi)存占用會(huì)下降一些。

          event MPM在遇到某些不兼容的模塊時(shí),會(huì)失效,將會(huì)回退到worker模式,一個(gè)工作線程處理一個(gè)請(qǐng)求。新版Apache官方自帶的模塊,全部是支持event MPM的。注意一點(diǎn),event MPM需要Linux系統(tǒng)(Linux 2.6+)對(duì)EPoll的支持,才能啟用。Apache的三種模式中在真實(shí)應(yīng)用場(chǎng)景中,event MPM是最節(jié)約內(nèi)存的。

          4. 使用比較輕量的Nginx作為Web服務(wù)器

          雖然Apache的不斷優(yōu)化,減少了內(nèi)存占用,從而增加了處理高并發(fā)的能力。但是,正如前面所說(shuō),Apache是一個(gè)古老而成熟的Web服務(wù),同時(shí),集成很多穩(wěn)定的模塊,是一個(gè)比較重的Web服務(wù)。Nginx是個(gè)比較輕量的Web服務(wù),占據(jù)的內(nèi)存天然就少于Apache。而且,Nginx通過(guò)一個(gè)進(jìn)程來(lái)服務(wù)于N個(gè)連接。所使用的方式,并不是Apache的增加進(jìn)程/線程來(lái)支持更多的連接。對(duì)于Nginx來(lái)說(shuō),它少創(chuàng)建了大量的進(jìn)程/線程,減少了很多內(nèi)存的開銷。

          靜態(tài)文件的QPS性能壓測(cè)結(jié)果,Nginx性能大概3倍于Apache對(duì)靜態(tài)文件的處理。PHP等動(dòng)態(tài)文件的QPS,Nginx的做法通常是通過(guò)FastCGI的方式和PHP-FPM通信的方式完成,PHP作為一個(gè)與之無(wú)關(guān)的外部服務(wù)存在。而Apache通常將PHP編譯為自己的字模塊(新版的Apache也支持FastCGI)。PHP動(dòng)態(tài)文件,Nginx的表現(xiàn)略遜于Apache。

          5. sendfile節(jié)約內(nèi)存

          Apache、Nginx等不少Web服務(wù),都帶有sendfile支持的。sendfile可以減少數(shù)據(jù)到“用戶態(tài)內(nèi)存空間”(用戶緩沖區(qū))的拷貝,進(jìn)而減少內(nèi)存的占用。當(dāng)然,很多同學(xué)第一個(gè)反應(yīng)當(dāng)然是問(wèn)Why?為了盡可能清楚講述這個(gè)原理,我們就先回Linux內(nèi)核態(tài)和用戶態(tài)的存儲(chǔ)空間的交互。

          一般情況下,用戶態(tài)(也就是我們的程序所在的內(nèi)存空間)是不會(huì)直接讀寫或者操作各種設(shè)備(磁盤、網(wǎng)絡(luò)、終端等),中間通常用內(nèi)核作為“中間人”,來(lái)完成對(duì)設(shè)備的操作或者讀寫。

          以最簡(jiǎn)單的磁盤讀寫例子,從磁盤中讀取A文件,寫入到B文件。A文件數(shù)據(jù)是從磁盤開始,然后載入到“內(nèi)核緩沖區(qū)”,然后再拷貝到“用戶緩沖區(qū)”,我們才可以對(duì)數(shù)據(jù)進(jìn)行處理。寫入的時(shí)候,也同理,從“用戶態(tài)緩沖區(qū)”載入到“內(nèi)核緩沖區(qū)”,最后寫入到磁盤B文件。

          這樣寫文件很累吧,于是有人覺(jué)得這里可以跳過(guò)“用戶緩沖區(qū)”的拷貝。其實(shí),這就是MMP(Memory-Mapping,內(nèi)存映射)的實(shí)現(xiàn),建立一個(gè)磁盤空間和內(nèi)存的直接映射,數(shù)據(jù)不再?gòu)?fù)制到“用戶態(tài)緩沖區(qū)”,而是返回一個(gè)指向內(nèi)存空間的指針。于是,我們之前的讀寫文件例子,就會(huì)變成,A文件數(shù)據(jù)從磁盤載入到“內(nèi)核緩沖區(qū)”,然后從“內(nèi)核緩沖區(qū)”復(fù)制到B文件的“內(nèi)核緩沖區(qū)”,B文件再?gòu)?#8221;內(nèi)核緩沖區(qū)“寫回到磁盤中。這個(gè)過(guò)程,減少了一次內(nèi)存拷貝,同時(shí)也少內(nèi)存占用。

          好了,回到sendfile的話題上來(lái),簡(jiǎn)單的說(shuō),sendfile的做法和MMP類似,就是減少數(shù)據(jù)從”內(nèi)核態(tài)緩沖區(qū)“到”用戶態(tài)緩沖區(qū)“的內(nèi)存拷貝。

          默認(rèn)的磁盤文件讀取,到傳輸給socket,流程(不使用sendfile)是:

          使用sendfile之后:

          這種方式,不僅節(jié)省了內(nèi)存,而且還有CPU的開銷。

           

          四、節(jié)約Web服務(wù)器的CPU

          對(duì)Web服務(wù)器而言,CPU是另一個(gè)非常核心的系統(tǒng)資源。雖然一般情況下,我們認(rèn)為業(yè)務(wù)程序的執(zhí)行消耗了我們主要CPU。但是,就Web服務(wù)程序而言,多線程/多進(jìn)程的上下文切換,也是比較消耗CPU資源的。一個(gè)進(jìn)程/線程通常不能長(zhǎng)期占有CPU,當(dāng)發(fā)生阻塞或者時(shí)間片用完,就無(wú)法繼續(xù)占用CPU,這個(gè)時(shí)候,就會(huì)發(fā)生上下文切換,CPU時(shí)間片從老進(jìn)程/線程切換到新的。除此之外,在并發(fā)連接數(shù)目很高的場(chǎng)景下,對(duì)這些用戶建立的連接(socket文件描述符)狀態(tài)的輪詢和檢測(cè),也是比較消耗CPU的。

          而Apache和Nginx的發(fā)展和演變,也在努力減少CPU開銷。

          1. Select/Poll(Apache早期版本的I/O多路復(fù)用)

          通常,Web服務(wù)都要維護(hù)很多個(gè)和用戶通信的socket文件描述符,I/O多路復(fù)用,其實(shí)就是為了方便對(duì)這些文件描述符的管理和檢測(cè)。Apache早期版本,是使用select的模式,簡(jiǎn)單的說(shuō),就是將這些我們關(guān)注的socket文件描述符交給內(nèi)核,讓內(nèi)核告訴我們,那些描述符可操作。Poll與select原理基本相同,因此放在一起,它們之間的區(qū)別,就不贅敘了哈。

          select/poll返回的是一個(gè)我們之前提交的文件描述符集合(內(nèi)核將其中可讀、可寫或者異常狀態(tài)的socket文件描述符的標(biāo)識(shí)位修改了),我們需要通過(guò)輪詢檢查才能獲得我們可以操作的文件描述符。在這個(gè)過(guò)程中,不斷重復(fù)執(zhí)行。在實(shí)際應(yīng)用場(chǎng)景中,大部分被我們監(jiān)控的socket文件描述符,都是”空閑的“,也就是說(shuō),不能操作。我們對(duì)整個(gè)集合輪詢,就是為了找了少部分我們可以操作的socket文件描述符。于是,當(dāng)我們監(jiān)控的socket文件描述符越多(用戶并發(fā)連接數(shù)越來(lái)越多),這個(gè)輪詢工作,也就越來(lái)越沉重,進(jìn)而導(dǎo)致增大了CPU的開銷。

          如果我們監(jiān)控的socket文件描述符,幾乎都是”活躍的“,反而使用這種模式更合適一點(diǎn)。

          2. Epoll(新版的Apache的event MPM,Nginx等支持)

          Epoll是Linux2.6開始正式支持的I/O多路復(fù)用,我們可以理解為它是對(duì)select/poll的改進(jìn)。首先,我們同樣將我們關(guān)注的socket文件描述符集合告訴給內(nèi)核,同時(shí),給它們注冊(cè)”回調(diào)函數(shù)“,如果某個(gè)socket文件準(zhǔn)備好了,就通過(guò)回調(diào)函數(shù)通知我們。于是,我們就不需要專門去輪詢整個(gè)全量的socket文件描述符集合,直接可以得到已經(jīng)可操作的socket文件描述符。那么,那些大部分”空閑“的描述符,我們就不遍歷了。即使我們監(jiān)控的socket文件描述越來(lái)越多,我們輪詢的也只是”活躍可操作“的socket文件描述符。

          其實(shí),有一種極端點(diǎn)的場(chǎng)景,就是我們?nèi)课募枋龇麕缀醵际?#8221;活躍“的,這樣反而導(dǎo)致了大量回調(diào)函數(shù)的執(zhí)行,又增加了CPU的開銷。但是,就Web服務(wù)的真實(shí)場(chǎng)景,絕大部分時(shí)候,都是連接集合中都存在很多”空閑“連接。

          3. 線程/進(jìn)程的創(chuàng)建銷毀和上下文切換

          通常,Apache某一個(gè)時(shí)間內(nèi),是一個(gè)進(jìn)程/線程服務(wù)于一個(gè)連接。于是,Apache就有很多的進(jìn)程/線程,服務(wù)于很多的連接。Web服務(wù)在高峰期,會(huì)建立很多的進(jìn)程/線程,也就帶來(lái)很多的上下文切換開銷。而Nginx,它通常只有1個(gè)master主進(jìn)程和幾個(gè)worker子進(jìn)程,然后,1個(gè)worker進(jìn)程服務(wù)很多個(gè)連接,進(jìn)而節(jié)省了CPU的上下文切換開銷。

          兩種模式雖然不同,但實(shí)際上不能直接出分好壞,綜合來(lái)說(shuō),各有各自的優(yōu)勢(shì),就不妄議了哈。

          4. 多線程下的鎖對(duì)CPU的開銷

          Apache中的worker和event模式,都有采用多線程。多線程因?yàn)楣蚕砀高M(jìn)程的內(nèi)存空間,在訪問(wèn)共享數(shù)據(jù)的時(shí)候,就會(huì)產(chǎn)生競(jìng)爭(zhēng),也就是線程安全問(wèn)題。因此通常會(huì)引入鎖(Linux下比較常用的線程相關(guān)的鎖有互斥量metux,讀寫鎖rwlock等),成功獲取鎖的線程可以繼續(xù)執(zhí)行,獲取失敗的通常選擇阻塞等待。引入鎖的機(jī)制,程序的復(fù)雜度往往增加不少,同時(shí)還有線程“死鎖”或者“餓死”的風(fēng)險(xiǎn)(多進(jìn)程在訪問(wèn)進(jìn)程間共享資源的時(shí)候,也有同樣的問(wèn)題)。

          死鎖現(xiàn)象(兩個(gè)線程彼此鎖住對(duì)方想要獲取的資源,相互阻塞等待,永遠(yuǎn)無(wú)法達(dá)不到滿足條件):

          餓死現(xiàn)象(某個(gè)線程,一直獲取不到它想要鎖資源,永遠(yuǎn)無(wú)法執(zhí)行下一步):

          為了避免這些鎖導(dǎo)致的問(wèn)題,就不得不加大程序的復(fù)雜度,解決方案一般有:

          (1)對(duì)資源的加鎖,根據(jù)約定好的順序,大家都先對(duì)共享資源X加鎖,加鎖成功之后才能加鎖共享資源Y。

          (2)如果線程占有資源X,卻加鎖資源Y失敗,則放棄加鎖,同時(shí)也釋放掉之前占有的資源X。

          在使用PHP的時(shí)候,在Apache的worker和event模式下,也必須兼容線程安全。通常,新版本的PHP官方庫(kù)是沒(méi)有線程安全方面的問(wèn)題,需要關(guān)注的是第三方擴(kuò)展。PHP實(shí)現(xiàn)線程安全,不是通過(guò)鎖的方式實(shí)現(xiàn)的。而是為每個(gè)線程獨(dú)立申請(qǐng)一份全局變量的副本,相當(dāng)于線程的私人內(nèi)存空間,但是這樣做相對(duì)消耗多一些內(nèi)存。不過(guò),這樣的好處,是不需要引入復(fù)雜的鎖機(jī)制實(shí)現(xiàn),也避免了鎖機(jī)制對(duì)CPU的開銷。

          這里順便提到一下,經(jīng)常和Nginx搭配工作的PHP-FPM(FastCGI)使用的是多進(jìn)程,因此不會(huì)有線程安全的問(wèn)題。

           

          五、 小結(jié)

          可能有些同學(xué)看完之后,會(huì)得出結(jié)論,Nginx+PHP-FPM的工作方式,似乎是最節(jié)省系統(tǒng)資源的Web系統(tǒng)工作方式。某種程度上說(shuō),的確是可以這么說(shuō)的,但是Web系統(tǒng)的搭建,需要從實(shí)際業(yè)務(wù)應(yīng)用的角度出發(fā),具體問(wèn)題需要具體分析,尋求最合適的技術(shù)方案。

          Web服務(wù)的不斷演變和發(fā)展,努力地追求用盡可能少的系統(tǒng)資源,來(lái)支撐更多的用戶請(qǐng)求,這是一條波瀾壯闊的前進(jìn)之路。這些技術(shù)方案,匯聚了很多值得學(xué)習(xí)和借鑒的解決問(wèn)題的思路。

          posted @ 2015-09-25 16:48 小馬歌 閱讀(271) | 評(píng)論 (0)編輯 收藏
           
          (1) 打開php的安全模式
            php的安全模式是個(gè)非常重要的內(nèi)嵌的安全機(jī)制,能夠控制一些php中的函數(shù),比如system(),
            同時(shí)把很多文件操作函數(shù)進(jìn)行了權(quán)限控制,也不允許對(duì)某些關(guān)鍵文件的文件,比如/etc/passwd,
            但是默認(rèn)的php.ini是沒(méi)有打開安全模式的,我們把它打開:
            safe_mode = on
          (2) 用戶組安全
            當(dāng)safe_mode打開時(shí),safe_mode_gid被關(guān)閉,那么php腳本能夠?qū)ξ募M(jìn)行訪問(wèn),而且相同
            組的用戶也能夠?qū)ξ募M(jìn)行訪問(wèn)。
            建議設(shè)置為:
            safe_mode_gid = off
            如果不進(jìn)行設(shè)置,可能我們無(wú)法對(duì)我們服務(wù)器網(wǎng)站目錄下的文件進(jìn)行操作了,比如我們需要
            對(duì)文件進(jìn)行操作的時(shí)候。
          (3) 安全模式下執(zhí)行程序主目錄
            如果安全模式打開了,但是卻是要執(zhí)行某些程序的時(shí)候,可以指定要執(zhí)行程序的主目錄:
            safe_mode_exec_dir = D:/usr/bin
            一般情況下是不需要執(zhí)行什么程序的,所以推薦不要執(zhí)行系統(tǒng)程序目錄,可以指向一個(gè)目錄,
            然后把需要執(zhí)行的程序拷貝過(guò)去,比如:

            safe_mode_exec_dir = D:/tmp/cmd

            但是,我更推薦不要執(zhí)行任何程序,那么就可以指向我們網(wǎng)頁(yè)目錄:

            safe_mode_exec_dir = D:/usr/www

          (4) 安全模式下包含文件

            如果要在安全模式下包含某些公共文件,那么就修改一下選項(xiàng):

            safe_mode_include_dir = D:/usr/www/include/

            其實(shí)一般php腳本中包含文件都是在程序自己已經(jīng)寫好了,這個(gè)可以根據(jù)具體需要設(shè)置。

          (5) 控制php腳本能訪問(wèn)的目錄

            使用open_basedir選項(xiàng)能夠控制PHP腳本只能訪問(wèn)指定的目錄,這樣能夠避免PHP腳本訪問(wèn)
            不應(yīng)該訪問(wèn)的文件,一定程度上限制了phpshell的危害,我們一般可以設(shè)置為只能訪問(wèn)網(wǎng)站目錄:

            open_basedir = D:/usr/www

          (6) 關(guān)閉危險(xiǎn)函數(shù)

            如果打開了安全模式,那么函數(shù)禁止是可以不需要的,但是我們?yōu)榱税踩€是考慮進(jìn)去。比如,
            我們覺(jué)得不希望執(zhí)行包括system()等在那的能夠執(zhí)行命令的php函數(shù),或者能夠查看php信息的
            phpinfo()等函數(shù),那么我們就可以禁止它們:
          disable_functions = system,passthru,exec,shell_exec,popen,phpinfo,escapeshellarg,escapeshellcmd,proc_close,proc_open,dl,show_source,get_cfg_var

            如果你要禁止任何文件和目錄的操作,那么可以關(guān)閉很多文件操作

            disable_functions = chdir,chroot,dir,getcwd,opendir,readdir,scandir,fopen,unlink,delete,copy,mkdir,   rmdir,rename,file,file_get_contents,fputs,fwrite,chgrp,chmod,chown

            以上只是列了部分不叫常用的文件處理函數(shù),你也可以把上面執(zhí)行命令函數(shù)和這個(gè)函數(shù)結(jié)合,
            就能夠抵制大部分的phpshell了。

          (7) 關(guān)閉PHP版本信息在http頭中的泄漏

            我們?yōu)榱朔乐购诳瞳@取服務(wù)器中php版本的信息,可以關(guān)閉該信息斜路在http頭中:

            expose_php = Off

            比如黑客在 telnet www.12345.com 80 的時(shí)候,那么將無(wú)法看到PHP的信息。

          (8) 關(guān)閉注冊(cè)全局變量

            在PHP中提交的變量,包括使用POST或者GET提交的變量,都將自動(dòng)注冊(cè)為全局變量,能夠直接訪問(wèn),
            這是對(duì)服務(wù)器非常不安全的,所以我們不能讓它注冊(cè)為全局變量,就把注冊(cè)全局變量選項(xiàng)關(guān)閉:
            register_globals = Off
            當(dāng)然,如果這樣設(shè)置了,那么獲取對(duì)應(yīng)變量的時(shí)候就要采用合理方式,比如獲取GET提交的變量var,
            那么就要用$_GET['var']來(lái)進(jìn)行獲取,這個(gè)php程序員要注意。

          (9) 打開magic_quotes_gpc來(lái)防止SQL注入

            SQL注入是非常危險(xiǎn)的問(wèn)題,小則網(wǎng)站后臺(tái)被入侵,重則整個(gè)服務(wù)器淪陷,

            所以一定要小心。php.ini中有一個(gè)設(shè)置:

            magic_quotes_gpc = Off

          這個(gè)默認(rèn)是關(guān)閉的,如果它打開后將自動(dòng)把用戶提交對(duì)sql的查詢進(jìn)行轉(zhuǎn)換,
            比如把 ' 轉(zhuǎn)為 \'等,這對(duì)防止sql注射有重大作用。所以我們推薦設(shè)置為:
            magic_quotes_gpc = On

          (10) 錯(cuò)誤信息控制

            一般php在沒(méi)有連接到數(shù)據(jù)庫(kù)或者其他情況下會(huì)有提示錯(cuò)誤,一般錯(cuò)誤信息中會(huì)包含php腳本當(dāng)
            前的路徑信息或者查詢的SQL語(yǔ)句等信息,這類信息提供給黑客后,是不安全的,所以一般服務(wù)器建議禁止錯(cuò)誤提示:

            display_errors = Off

            如果你卻是是要顯示錯(cuò)誤信息,一定要設(shè)置顯示錯(cuò)誤的級(jí)別,比如只顯示警告以上的信息:

            error_reporting = E_WARNING & E_ERROR

            當(dāng)然,我還是建議關(guān)閉錯(cuò)誤提示。

          (11) 錯(cuò)誤日志

            建議在關(guān)閉display_errors后能夠把錯(cuò)誤信息記錄下來(lái),便于查找服務(wù)器運(yùn)行的原因:

          log_errors = On

            同時(shí)也要設(shè)置錯(cuò)誤日志存放的目錄,建議根apache的日志存在一起:

            error_log = D:/usr/local/apache2/logs/php_error.log

            注意:給文件必須允許apache用戶的和組具有寫的權(quán)限。
          posted @ 2015-09-17 10:14 小馬歌 閱讀(258) | 評(píng)論 (0)編輯 收藏
           

          【一、在服務(wù)器端配置】

                 安全,PHP代碼編寫是一方面,PHP的配置更是非常關(guān)鍵。

          我們php手手工安裝的,php的默認(rèn)配置文件在 /usr/local/apache2/conf/php.ini,我們最主要就是要配置php.ini中的內(nèi)容,讓我們執(zhí)行 php能夠更安全。整個(gè)PHP中的安全設(shè)置主要是為了防止phpshell和SQL Injection的攻擊,一下我們慢慢探討。我們先使用任何編輯工具打開 /etc/local/apache2/conf/php.ini,如果你是采用其他方式安裝,配置文件可能不在該目錄。

          (1) 打開php的安全模式

          php的安全模式是個(gè)非常重要的內(nèi)嵌的安全機(jī)制,能夠控制一些php中的函數(shù),比如system(),

          同時(shí)把很多文件操作函數(shù)進(jìn)行了權(quán)限控制,也不允許對(duì)某些關(guān)鍵文件的文件,比如/etc/passwd,

          但是默認(rèn)的php.ini是沒(méi)有打開安全模式的,我們把它打開:

          safe_mode = on

          (2) 用戶組安全

          當(dāng)safe_mode打開時(shí),safe_mode_gid被關(guān)閉,那么php腳本能夠?qū)ξ募M(jìn)行訪問(wèn),而且相同

          組的用戶也能夠?qū)ξ募M(jìn)行訪問(wèn)。

          建議設(shè)置為:

          safe_mode_gid = off

          如果不進(jìn)行設(shè)置,可能我們無(wú)法對(duì)我們服務(wù)器網(wǎng)站目錄下的文件進(jìn)行操作了,比如我們需要

          對(duì)文件進(jìn)行操作的時(shí)候。

          (3) 安全模式下執(zhí)行程序主目錄

          如果安全模式打開了,但是卻是要執(zhí)行某些程序的時(shí)候,可以指定要執(zhí)行程序的主目錄:

          safe_mode_exec_dir = D:/usr/bin

          一般情況下是不需要執(zhí)行什么程序的,所以推薦不要執(zhí)行系統(tǒng)程序目錄,可以指向一個(gè)目錄,

          然后把需要執(zhí)行的程序拷貝過(guò)去,比如:

          safe_mode_exec_dir = D:/tmp/cmd

          但是,我更推薦不要執(zhí)行任何程序,那么就可以指向我們網(wǎng)頁(yè)目錄:

          safe_mode_exec_dir = D:/usr/www

          (4) 安全模式下包含文件

          如果要在安全模式下包含某些公共文件,那么就修改一下選項(xiàng):

          safe_mode_include_dir = D:/usr/www/include/

          其實(shí)一般php腳本中包含文件都是在程序自己已經(jīng)寫好了,這個(gè)可以根據(jù)具體需要設(shè)置。

          (5) 控制php腳本能訪問(wèn)的目錄

          使用open_basedir選項(xiàng)能夠控制PHP腳本只能訪問(wèn)指定的目錄,這樣能夠避免PHP腳本訪問(wèn)

          不應(yīng)該訪問(wèn)的文件,一定程度上限制了phpshell的危害,我們一般可以設(shè)置為只能訪問(wèn)網(wǎng)站目錄:

          open_basedir = D:/usr/www

          (6) 關(guān)閉危險(xiǎn)函數(shù)

          如果打開了安全模式,那么函數(shù)禁止是可以不需要的,但是我們?yōu)榱税踩€是考慮進(jìn)去。比如,

          我們覺(jué)得不希望執(zhí)行包括system()等在那的能夠執(zhí)行命令的php函數(shù),或者能夠查看php信息的

          phpinfo()等函數(shù),那么我們就可以禁止它們:

          disable_functions = system,passthru,exec,shell_exec,popen,phpinfo

          如果你要禁止任何文件和目錄的操作,那么可以關(guān)閉很多文件操作

          disable_functions = chdir,chroot,dir,getcwd,opendir,readdir,scandir,fopen,unlink,delete,copy,mkdir, rmdir,rename,file,file_get_contents,fputs,fwrite,chgrp,chmod,chown

          以上只是列了部分不叫常用的文件處理函數(shù),你也可以把上面執(zhí)行命令函數(shù)和這個(gè)函數(shù)結(jié)合,

          就能夠抵制大部分的phpshell了。

          (7) 關(guān)閉PHP版本信息在http頭中的泄漏

          我們?yōu)榱朔乐购诳瞳@取服務(wù)器中php版本的信息,可以關(guān)閉該信息斜路在http頭中:

          expose_php = Off

          比如黑客在 telnet www.12345.com 80 的時(shí)候,那么將無(wú)法看到PHP的信息。

          (8) 關(guān)閉注冊(cè)全局變量

          在PHP中提交的變量,包括使用POST或者GET提交的變量,都將自動(dòng)注冊(cè)為全局變量,能夠直接訪問(wèn),

          這是對(duì)服務(wù)器非常不安全的,所以我們不能讓它注冊(cè)為全局變量,就把注冊(cè)全局變量選項(xiàng)關(guān)閉:

          register_globals = Off

          當(dāng)然,如果這樣設(shè)置了,那么獲取對(duì)應(yīng)變量的時(shí)候就要采用合理方式,比如獲取GET提交的變量var,

          那么就要用$_GET['var']來(lái)進(jìn)行獲取,這個(gè)php程序員要注意。

          (9) 打開magic_quotes_gpc來(lái)防止SQL注入

          SQL注入是非常危險(xiǎn)的問(wèn)題,小則網(wǎng)站后臺(tái)被入侵,重則整個(gè)服務(wù)器淪陷,

          所以一定要小心。php.ini中有一個(gè)設(shè)置:

          magic_quotes_gpc = Off

          這個(gè)默認(rèn)是關(guān)閉的,如果它打開后將自動(dòng)把用戶提交對(duì)sql的查詢進(jìn)行轉(zhuǎn)換,

          比如把 ' 轉(zhuǎn)為 \'等,這對(duì)防止sql注射有重大作用。所以我們推薦設(shè)置為:

          magic_quotes_gpc = On

          (10) 錯(cuò)誤信息控制

          一般php在沒(méi)有連接到數(shù)據(jù)庫(kù)或者其他情況下會(huì)有提示錯(cuò)誤,一般錯(cuò)誤信息中會(huì)包含php腳本當(dāng)

          前的路徑信息或者查詢的SQL語(yǔ)句等信息,這類信息提供給黑客后,是不安全的,所以一般服務(wù)器建議禁止錯(cuò)誤提示:

          display_errors = Off

          如果你卻是是要顯示錯(cuò)誤信息,一定要設(shè)置顯示錯(cuò)誤的級(jí)別,比如只顯示警告以上的信息:

          error_reporting = E_WARNING & E_ERROR

          當(dāng)然,我還是建議關(guān)閉錯(cuò)誤提示。

          (11) 錯(cuò)誤日志

          建議在關(guān)閉display_errors后能夠把錯(cuò)誤信息記錄下來(lái),便于查找服務(wù)器運(yùn)行的原因:

          log_errors = On

          同時(shí)也要設(shè)置錯(cuò)誤日志存放的目錄,建議根apache的日志存在一起:

          error_log = D:/usr/local/apache2/logs/php_error.log

          注意:給文件必須允許apache用戶的和組具有寫的權(quán)限。

          MYSQL的降權(quán)運(yùn)行

          新建立一個(gè)用戶比如mysqlstart

          net user mysqlstart fuckmicrosoft /add

          net localgroup users mysqlstart /del

          不屬于任何組

          如果MYSQL裝在d:\mysql ,那么,給 mysqlstart 完全控制 的權(quán)限

          然后在系統(tǒng)服務(wù)中設(shè)置,MYSQL的服務(wù)屬性,在登錄屬性當(dāng)中,選擇此用戶 mysqlstart 然后輸入密碼,確定。

          重新啟動(dòng) MYSQL服務(wù),然后MYSQL就運(yùn)行在低權(quán)限下了。

          如果是在windos平臺(tái)下搭建的apache我們還需要注意一點(diǎn),apache默認(rèn)運(yùn)行是system權(quán)限,

          這很恐怖,這讓人感覺(jué)很不爽.那我們就給apache降降權(quán)限吧。

          net user apache fuckmicrosoft /add

          net localgroup users apache /del

          ok.我們建立了一個(gè)不屬于任何組的用戶apche。

          我們打開計(jì)算機(jī)管理器,選服務(wù),點(diǎn)apache服務(wù)的屬性,我們選擇log on,選擇this account,我們填入上面所建立的賬戶和密碼,

          重啟apache服務(wù),ok,apache運(yùn)行在低權(quán)限下了。

          實(shí)際上我們還可以通過(guò)設(shè)置各個(gè)文件夾的權(quán)限,來(lái)讓apache用戶只能執(zhí)行我們想讓它能干的事情,給每一個(gè)目錄建立一個(gè)單獨(dú)能讀寫的用戶。

          這也是當(dāng)前很多虛擬主機(jī)提供商的流行配置方法哦,不過(guò)這種方法用于防止這里就顯的有點(diǎn)大材小用了。 


          【二、在PHP代碼編寫】

                 雖然國(guó)內(nèi)很多PHP程序員仍在依靠addslashes防止SQL注入,還是建議大家加強(qiáng)中文防止SQL注入的檢查。addslashes的問(wèn)題在于黑客可以用0xbf27來(lái)代替單引號(hào),而addslashes只是將0xbf27修改為0xbf5c27,成為一個(gè)有效的多字節(jié)字符,其中的0xbf5c仍會(huì)被看作是單引號(hào),所以addslashes無(wú)法成功攔截。
                 當(dāng)然addslashes也不是毫無(wú)用處,它是用于單字節(jié)字符串的處理,多字節(jié)字符還是用mysql_real_escape_string吧。
                 另外對(duì)于php手冊(cè)中g(shù)et_magic_quotes_gpc的舉例:
          if (!get_magic_quotes_gpc()) {
          $lastname = addslashes($_POST[‘lastname’]);
          } else {
          $lastname = $_POST[‘lastname’];
          }

          最好對(duì)magic_quotes_gpc已經(jīng)開放的情況下,還是對(duì)$_POST[’lastname’]進(jìn)行檢查一下。
          再說(shuō)下mysql_real_escape_string和mysql_escape_string這2個(gè)函數(shù)的區(qū)別:
          mysql_real_escape_string 必須在(PHP 4 >= 4.3.0, PHP 5)的情況下才能使用。否則只能用 mysql_escape_string ,兩者的區(qū)別是:mysql_real_escape_string 考慮到連接的
          當(dāng)前字符集,而mysql_escape_string 不考慮。
          總結(jié)一下:
          * addslashes() 是強(qiáng)行加\;
          * mysql_real_escape_string()  會(huì)判斷字符集,但是對(duì)PHP版本有要求;
          * mysql_escape_string不考慮連接的當(dāng)前字符集。
          -------------------------------------------------------------------------------------------------
          在PHP編碼的時(shí)候,如果考慮到一些比較基本的安全問(wèn)題,首先一點(diǎn):
          1. 初始化你的變量
          為什么這么說(shuō)呢?我們看下面的代碼:
          PHP代碼   
             <?php     
              if ($admin)     
              {     
              echo '登陸成功!';     
              include('admin.php');     
              }     
              else     
              {     
              echo '你不是管理員,無(wú)法進(jìn)行管理!';     
              }     
              ?>
               好,我們看上面的代碼好像是能正常運(yùn)行,沒(méi)有問(wèn)題,那么加入我提交一個(gè)非法的參數(shù)過(guò)去呢,那么效果會(huì)如何呢?比如我們的這個(gè)頁(yè)是http://daybook.diandian.com/login.php,那么我們提交:http://daybook.diandian.com/login.php?admin=1,呵呵,你想一些,我們是不是直接就是管理員了,直接進(jìn)行管理。
               當(dāng)然,可能我們不會(huì)犯這么簡(jiǎn)單錯(cuò)的錯(cuò)誤,那么一些很隱秘的錯(cuò)誤也可能導(dǎo)致這個(gè)問(wèn)題,比如phpwind論壇有個(gè)漏洞,導(dǎo)致能夠直接拿到管理員權(quán)限,就是因?yàn)橛袀€(gè)$skin變量沒(méi)有初始化,導(dǎo)致了后面一系列問(wèn)題。那么我們?nèi)绾伪苊馍厦娴膯?wèn)題呢?首先,從php.ini入手,把php.ini里面的register_global =off,就是不是所有的注冊(cè)變量為全局,那么就能避免了。但是,我們不是服務(wù)器管理員,只能從代碼上改進(jìn)了,那么我們?nèi)绾胃倪M(jìn)上面的代碼呢?我們改寫如下:
          PHP代碼      
              <?php     
              $admin = 0; // 初始化變量     
              if ($_POST['admin_user'] && $_POST['admin_pass'])     
              {     
              // 判斷提交的管理員用戶名和密碼是不是對(duì)的相應(yīng)的處理代碼     
              // ...     
              $admin = 1;     
              }     
              else     
              {     
              $admin = 0;     
              }     
              if ($admin)     
              {     
              echo '登陸成功!';     
              include('admin.php');     
              }     
              else     
              {     
              echo '你不是管理員,無(wú)法進(jìn)行管理!';     
              }     
              ?>
              那么這時(shí)候你再提交http://daybook.diandian.com/login.php?admin=1就不好使了,因?yàn)槲覀冊(cè)谝婚_始就把變量初始化為 $admin = 0 了,那么你就無(wú)法通過(guò)這個(gè)漏洞獲取管理員權(quán)限。
          2. 防止SQL Injection (sql注射)
              SQL 注射應(yīng)該是目前程序危害最大的了,包括最早從asp到php,基本上都是國(guó)內(nèi)這兩年流行的技術(shù),基本原理就是通過(guò)對(duì)提交變量的不過(guò)濾形成注入點(diǎn)然后使惡意用戶能夠提交一些sql查詢語(yǔ)句,導(dǎo)致重要數(shù)據(jù)被竊取、數(shù)據(jù)丟失或者損壞,或者被入侵到后臺(tái)管理。
              那么我們既然了解了基本的注射入侵的方式,那么我們?nèi)绾稳シ婪赌兀窟@個(gè)就應(yīng)該我們從代碼去入手了。
             我們知道Web上提交數(shù)據(jù)有兩種方式,一種是get、一種是post,那么很多常見的sql注射就是從get方式入手的,而且注射的語(yǔ)句里面一定是包含一些sql語(yǔ)句的,因?yàn)闆](méi)有sql語(yǔ)句,那么如何進(jìn)行,sql語(yǔ)句有四大句:select 、update、delete、insert,那么我們?nèi)绻谖覀兲峤坏臄?shù)據(jù)中進(jìn)行過(guò)濾是不是能夠避免這些問(wèn)題呢?
          于是我們使用正則就構(gòu)建如下函數(shù):
          PHP代碼
              <?php          
              function inject_check($sql_str)     
              {     
              return eregi('select|insert|update|delete|'|     
              function verify_id($id=null)     
              {     
              if (!$id) { exit('沒(méi)有提交參數(shù)!'); } // 是否為空判斷     
              elseif (inject_check($id)) { exit('提交的參數(shù)非法!'); } // 注射判斷     
              elseif (!is_numeric($id)) { exit('提交的參數(shù)非法!'); } // 數(shù)字判斷     
              $id = intval($id); // 整型化         
              return $id;     
              }     
              ?>
               呵呵,那么我們就能夠進(jìn)行校驗(yàn)了,于是我們上面的程序代碼就變成了下面的:
          PHP代碼     
              <?php     
              if (inject_check($_GET['id']))     
              {     
              exit('你提交的數(shù)據(jù)非法,請(qǐng)檢查后重新提交!');     
              }     
              else     
              {     
              $id = verify_id($_GET['id']); // 這里引用了我們的過(guò)濾函數(shù),對(duì)$id進(jìn)行過(guò)濾     
              echo '提交的數(shù)據(jù)合法,請(qǐng)繼續(xù)!';     
              }     
              ?>
              好,問(wèn)題到這里似乎都解決了,但是我們有沒(méi)有考慮過(guò)post提交的數(shù)據(jù),大批量的數(shù)據(jù)呢?
          比如一些字符可能會(huì)對(duì)數(shù)據(jù)庫(kù)造成危害,比如 ' _ ', ' %',這些字符都有特殊意義,那么我們?nèi)绻M(jìn)行控制呢?還有一點(diǎn),就是當(dāng)我們的php.ini里面的magic_quotes_gpc = off的時(shí)候,那么提交的不符合數(shù)據(jù)庫(kù)規(guī)則的數(shù)據(jù)都是不會(huì)自動(dòng)在前面加' '的,那么我們要控制這些問(wèn)題,于是構(gòu)建如下函數(shù):
          PHP代碼      
              <?php        
              function str_check( $str )     
              {     
              if (!get_magic_quotes_gpc()) // 判斷magic_quotes_gpc是否打開     
              {     
              $str = addslashes($str); // 進(jìn)行過(guò)濾     
              }     
              $str = str_replace("_", "\_", $str); // 把 '_'過(guò)濾掉     
              $str = str_replace("%", "\%", $str); // 把' % '過(guò)濾掉     
                   
              return $str;     
              }     
              ?>
              我們又一次的避免了服務(wù)器被淪陷的危險(xiǎn)。
              最后,再考慮提交一些大批量數(shù)據(jù)的情況,比如發(fā)貼,或者寫文章、新聞,我們需要一些函數(shù)來(lái)幫我們過(guò)濾和進(jìn)行轉(zhuǎn)換,再上面函數(shù)的基礎(chǔ)上,我們構(gòu)建如下函數(shù):
          PHP代碼  
              <?php      
              function post_check($post)     
              {     
              if (!get_magic_quotes_gpc()) // 判斷magic_quotes_gpc是否為打開     
              {     
              $post = addslashes($post); // 進(jìn)行magic_quotes_gpc沒(méi)有打開的情況對(duì)提交數(shù)據(jù)的過(guò)濾     
              }     
              $post = str_replace("_", "\_", $post); // 把 '_'過(guò)濾掉     
              $post = str_replace("%", "\%", $post); // 把' % '過(guò)濾掉     
              $post = nl2br($post); // 回車轉(zhuǎn)換     
              $post= htmlspecialchars($post); // html標(biāo)記轉(zhuǎn)換        
              return $post;     
              }     
              ?>
              呵呵,基本到這里,我們把一些情況都說(shuō)了一遍,其實(shí)我覺(jué)得自己講的東西還很少,至少我才只講了兩方面,再整個(gè)安全中是很少的內(nèi)容了,考慮下一次講更多,包括php安全配置,apache安全等等,讓我們的安全正的是一個(gè)整體,作到最安全。
              最后在告訴你上面表達(dá)的:1. 初始化你的變量 2. 一定記得要過(guò)濾你的變量

          posted @ 2015-09-17 10:09 小馬歌 閱讀(266) | 評(píng)論 (0)編輯 收藏
           
          from:https://zookeeper.apache.org/doc/r3.4.6/zookeeperAdmin.html#sc_zkCommands

          ZooKeeper responds to a small set of commands. Each command is composed of four letters. You issue the commands to ZooKeeper via telnet or nc, at the client port.

          Three of the more interesting commands: "stat" gives some general information about the server and connected clients, while "srvr" and "cons" give extended details on server and connections respectively.

          conf

          New in 3.3.0: Print details about serving configuration.

          cons

          New in 3.3.0: List full connection/session details for all clients connected to this server. Includes information on numbers of packets received/sent, session id, operation latencies, last operation performed, etc...

          crst

          New in 3.3.0: Reset connection/session statistics for all connections.

          dump

          Lists the outstanding sessions and ephemeral nodes. This only works on the leader.

          envi

          Print details about serving environment

          ruok

          Tests if server is running in a non-error state. The server will respond with imok if it is running. Otherwise it will not respond at all.

          A response of "imok" does not necessarily indicate that the server has joined the quorum, just that the server process is active and bound to the specified client port. Use "stat" for details on state wrt quorum and client connection information.

          srst

          Reset server statistics.

          srvr

          New in 3.3.0: Lists full details for the server.

          stat

          Lists brief details for the server and connected clients.

          wchs

          New in 3.3.0: Lists brief information on watches for the server.

          wchc

          New in 3.3.0: Lists detailed information on watches for the server, by session. This outputs a list of sessions(connections) with associated watches (paths). Note, depending on the number of watches this operation may be expensive (ie impact server performance), use it carefully.

          wchp

          New in 3.3.0: Lists detailed information on watches for the server, by path. This outputs a list of paths (znodes) with associated sessions. Note, depending on the number of watches this operation may be expensive (ie impact server performance), use it carefully.

          mntr

          New in 3.4.0: Outputs a list of variables that could be used for monitoring the health of the cluster.

          $ echo mntr | nc localhost 2185  zk_version  3.4.0 zk_avg_latency  0 zk_max_latency  0 zk_min_latency  0 zk_packets_received 70 zk_packets_sent 69 zk_outstanding_requests 0 zk_server_state leader zk_znode_count   4 zk_watch_count  0 zk_ephemerals_count 0 zk_approximate_data_size    27 zk_followers    4                   - only exposed by the Leader zk_synced_followers 4               - only exposed by the Leader zk_pending_syncs    0               - only exposed by the Leader zk_open_file_descriptor_count 23    - only available on Unix platforms zk_max_file_descriptor_count 1024   - only available on Unix platforms 

          The output is compatible with java properties format and the content may change over time (new keys added). Your scripts should expect changes.

          ATTENTION: Some of the keys are platform specific and some of the keys are only exported by the Leader.

          The output contains multiple lines with the following format:

          key \t value

          Here's an example of the ruok command:

          $ echo ruok | nc 127.0.0.1 5111 imok
          posted @ 2015-09-16 09:10 小馬歌 閱讀(350) | 評(píng)論 (0)編輯 收藏
           
          NetCat,在網(wǎng)絡(luò)工具中有l(wèi)dquo;瑞士軍刀rdquo;美譽(yù),其有Windows和Linux的版本。因?yàn)樗绦【罚?.84版本也不過(guò)25k,舊版本或縮

          NetCat,在網(wǎng)絡(luò)工具中有“瑞士軍刀”美譽(yù),其有Windows和Linux的版本。因?yàn)樗绦【罚?.84版本也不過(guò)25k,舊版本或縮減版甚至更小)、功能實(shí)用,被設(shè)計(jì)為一個(gè)簡(jiǎn)單、可靠的網(wǎng)絡(luò)工具,可通過(guò)TCP或UDP協(xié)議傳輸讀寫數(shù)據(jù)。同時(shí),它還是一個(gè)網(wǎng)絡(luò)應(yīng)用Debug分析器,因?yàn)樗梢愿鶕?jù)需要?jiǎng)?chuàng)建各種不同類型的網(wǎng)絡(luò)連接。

          一、版本
          通常的Linux發(fā)行版中都帶有NetCat(簡(jiǎn)稱nc),甚至在拯救模式光盤中也由busybox提供了簡(jiǎn)版的nc工具。但不同的版本,其參數(shù)的使用略有差異。
          NetCat 官方地址:


          引用[root@hatest1 ~]# cat /etc/asianux-release
          Asianux release 2.0 (Trinity SP2)
          [root@hatest1 ~]# cat /etc/redflag-release
          Red Flag DC Server release 5.0 (Trinity SP2)
          [root@hatest1 ~]# type -a nc
          nc is /usr/bin/nc
          [root@hatest1 ~]# rpm -q nc
          nc-1.10-22

          建議在使用前,先用man nc看看幫助。這里以紅旗DC Server 5.0上的1.10版本進(jìn)行簡(jiǎn)單說(shuō)明。
          假設(shè)兩服務(wù)器信息:

          server1: 192.168.10.10
          server2: 192.168.10.11

          二、常見使用
          1、遠(yuǎn)程拷貝文件
          從server1拷貝文件到server2上。需要先在server2上,,用nc激活監(jiān)聽,

          server2上運(yùn)行: nc -l 1234 > text.txt

          server1上運(yùn)行: nc 192.168.10.11 1234 < text.txt

          注:server2上的監(jiān)聽要先打開


          2、克隆硬盤或分區(qū)
          操作與上面的拷貝是雷同的,只需要由dd獲得硬盤或分區(qū)的數(shù)據(jù),然后傳輸即可。
          克隆硬盤或分區(qū)的操作,不應(yīng)在已經(jīng)mount的的系統(tǒng)上進(jìn)行。所以,需要使用安裝光盤引導(dǎo)后,進(jìn)入拯救模式(或使用Knoppix工具光盤)啟動(dòng)系統(tǒng)后,在server2上進(jìn)行類似的監(jiān)聽動(dòng)作:

           nc -l -p 1234 | dd of=/dev/sda

          server1上執(zhí)行傳輸,即可完成從server1克隆sda硬盤到server2的任務(wù):

           dd if=/dev/sda | nc192.168.10.11 1234

          ※ 完成上述工作的前提,是需要落實(shí)光盤的拯救模式支持服務(wù)器上的網(wǎng)卡,并正確配置IP。

          3、端口掃描
          可以執(zhí)行:

          # nc -v -w 2 192.168.10.11 -z 21-24
          nc: connect to 192.168.10.11 port 21 (tcp) failed: Connection refused
          Connection to 192.168.10.11 22 port [tcp/ssh] succeeded!
          nc: connect to 192.168.10.11 port 23 (tcp) failed: Connection refused
          nc: connect to 192.168.10.11 port 24 (tcp) failed: Connection refused 
          -z后面跟的是要掃描的端口


          4、保存Web頁(yè)面

          # while true; do nc -l -p 80 -q 1 < somepage.html; done


          5、模擬HTTP Headers

          引用[root@hatest1 ~]# nc 80
          GET / HTTP/1.1
          Host: ispconfig.org
          Referrer: mypage.com
          User-Agent: my-browser

          HTTP/1.1 200 OK
          Date: Tue, 16 Dec 2008 07:23:24 GMT
          Server: Apache/2.2.6 (Unix) DAV/2 mod_mono/1.2.1 mod_python/3.2.8 Python/2.4.3 mod_perl/2.0.2 Perl/v5.8.8
          Set-Cookie: PHPSESSID=bbadorbvie1gn037iih6lrdg50; path=/
          Expires: 0
          Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
          Pragma: no-cache
          Cache-Control: private, post-check=0, pre-check=0, max-age=0
          Set-Cookie: oWn_sid=xRutAY; expires=Tue, 23-Dec-2008 07:23:24 GMT; path=/
          Vary: Accept-Encoding
          Transfer-Encoding: chunked
          Content-Type: text/html
          [......]

          在nc命令后,輸入紅色部分的內(nèi)容,然后按兩次回車,即可從對(duì)方獲得HTTP Headers內(nèi)容。

          6、聊天
          nc還可以作為簡(jiǎn)單的字符下聊天工具使用,同樣的,server2上需要啟動(dòng)監(jiān)聽:

          server2上啟動(dòng):# nc -lp 1234 
          server1上傳輸:# nc 192.168.10.11 1234


          這樣,雙方就可以相互交流了。使用Ctrl+D正常退出。

          7、傳輸目錄
          從server1拷貝nginx-0.6.34目錄內(nèi)容到server2上。需要先在server2上,用nc激活監(jiān)聽,

          server2上運(yùn)行:# nc -l 1234 |tar xzvf -

          server1上運(yùn)行:# tar czvf - nginx-0.6.34|nc 192.168.10.11 1234

           


          8、用nc命名操作memcached

          1)存儲(chǔ)數(shù)據(jù):printf “set key 0 10 6rnresultrn” |nc 192.168.10.11 11211
          2)獲取數(shù)據(jù):printf “get keyrn” |nc 192.168.10.11 11211
          3)刪除數(shù)據(jù):printf “delete keyrn” |nc 192.168.10.11 11211
          4)查看狀態(tài):printf “statsrn” |nc 192.168.10.11 11211
          5)模擬top命令查看狀態(tài):watch “echo stats” |nc 192.168.10.11 11211
          6)清空緩存:printf “flush_allrn” |nc 192.168.10.11 11211 (小心操作,清空了緩存就沒(méi)了)

          posted @ 2015-09-16 09:01 小馬歌 閱讀(507) | 評(píng)論 (0)編輯 收藏
           
               摘要: ZooKeeper是一個(gè)分布式的,開放源碼的分布式應(yīng)用程序協(xié)調(diào)服務(wù),它包含一個(gè)簡(jiǎn)單的原語(yǔ)集,分布式應(yīng)用程序可以基于它實(shí)現(xiàn)同步服務(wù),配置維護(hù)和命名服務(wù)等。Zookeeper是hadoop的一個(gè)子項(xiàng)目,其發(fā)展歷程無(wú)需贅述。在分布式應(yīng)用中,由于工程師不能很好地使用鎖機(jī)制,以及基于消息的協(xié)調(diào)機(jī)制不適合在某些應(yīng)用中使用,因此需要有一種可靠的、可擴(kuò)展的、分布式的、可配置的協(xié)調(diào)機(jī)制來(lái)統(tǒng)一系統(tǒng)的狀態(tài)。Zookee...  閱讀全文
          posted @ 2015-09-10 17:55 小馬歌 閱讀(359) | 評(píng)論 (0)編輯 收藏
           
          from:http://blog.csdn.net/zheng0518/article/details/44943357

          問(wèn)題現(xiàn)象

          最后發(fā)現(xiàn)線上的zookeeper的日志zookeeper.out 文件居然有6G,后來(lái)設(shè)置下日志為滾動(dòng)輸出,參考:

          http://blog.csdn.net/hengyunabc/article/details/19006911

          但是改了之后,發(fā)現(xiàn)一天的日志量就是100多M,滾動(dòng)日志一天就被沖掉了,這個(gè)不科學(xué)。

          再仔細(xì)查看下日志里的內(nèi)容,發(fā)現(xiàn)有很多連接建立好,馬上又?jǐn)嚅_:

          1. 2014-11-24 15:38:33,348 [myid:3] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@1001] - Closed socket connection for client /10.0.0.3:47772 (no session established for client)  
          2. 2014-11-24 15:38:33,682 [myid:3] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxnFactory@197] - Accepted socket connection from /10.0.0.3:32119  
          3. 2014-11-24 15:38:33,682 [myid:3] - WARN  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@349] - caught end of stream exception  
          4. EndOfStreamException: Unable to read additional data from client sessionid 0x0, likely client has closed socket  
          5.         at org.apache.zookeeper.server.NIOServerCnxn.doIO(NIOServerCnxn.java:220)  
          6.         at org.apache.zookeeper.server.NIOServerCnxnFactory.run(NIOServerCnxnFactory.java:208)  
          7.         at java.lang.Thread.run(Thread.java:745)  
          8. 2014-11-24 15:38:33,682 [myid:3] - INFO  [NIOServerCxn.Factory:0.0.0.0/0.0.0.0:2181:NIOServerCnxn@1001] - Closed socket connection for client /10.0.0.0:32119 (no session established for client)  
          從日志輸出的時(shí)間來(lái)看,秒連秒斷,非常詭異。

          排查問(wèn)題

          用netstat查看網(wǎng)絡(luò)連接狀態(tài)

          到client的服務(wù)器上查看連接的狀態(tài):

          1. netstat -antp | grep 2181  

          發(fā)現(xiàn)有很多TIME_WAIT狀態(tài)的連接:

          1. tcp        0      0 10.0.0.3:44269         10.0.1.77:2181         TIME_WAIT   -                     
          2. tcp        0      0 10.0.0.3:43646         10.0.1.77:2181         TIME_WAIT   -                     
          3. tcp        0      0 10.0.0.3:44184         10.0.1.77:2181         TIME_WAIT   -                     
          4. tcp        0      0 10.0.0.3:44026         10.0.1.77:2181         TIME_WAIT   -                     
          5. tcp        0      0 10.0.0.3:43766         10.0.1.77:2181         TIME_WAIT   -    

          但是TIME_WAIT狀態(tài)的連接是看不到進(jìn)程號(hào)的。搜索研究了下netstat的參數(shù),發(fā)現(xiàn)沒(méi)有辦法輸出TIME_WAIT狀態(tài)的連接的pid,只好嘗試其它的辦法。

          再用 jstack -l pid 來(lái)查看進(jìn)程的線程棧,也沒(méi)有發(fā)現(xiàn)什么異常的東東。查看到有幾個(gè)zookeeper連接的線程,但也是正常狀態(tài)。

          再檢查了機(jī)器的IO,CPU,內(nèi)存,也沒(méi)有異常的情況。

          沒(méi)找到什么有用的信息,只好再研究下netstat的參數(shù):
          發(fā)現(xiàn)用 netstat -ae 輸出了一些信息:

          1. tcp        0      0 10.0.0.3:41772     10.0.1.77:eforward     TIME_WAIT   root       0            
          2. tcp        0      0 10.0.0.3:41412     10.0.1.77:eforward     TIME_WAIT   root       0            
          3. tcp        0      0 10.0.0.3:24226     10.0.1.77:2181         TIME_WAIT   root       0            
          4. tcp        0      0 10.0.0.3:24623     10.0.1.77:2181         TIME_WAIT   root       0  

          發(fā)現(xiàn)user是root。于是以為是非Java應(yīng)用,在不斷地連接zookeeper。于是停止java程序,發(fā)現(xiàn)沒(méi)有TIME_WAIT連接了。
          但是確認(rèn)是Java應(yīng)用的問(wèn)題,于是再重啟Java應(yīng)用,但沒(méi)有再發(fā)現(xiàn)TIME_WAIT情況。很詭異。

          問(wèn)題不能重現(xiàn)了,相當(dāng)?shù)牡疤邸:鋈幌氲骄€上的應(yīng)用也許也有這個(gè)問(wèn)題,于是到線下zookeeper服務(wù)器上查看了下,果然發(fā)現(xiàn)有同樣的問(wèn)題。

          用tcpdump抓包和wireshark分析

          先用tcpdump來(lái)查看下具體的網(wǎng)絡(luò)連接,發(fā)現(xiàn)的確是連接連上再斷開。于是先保存成cap文件,再用wireshark來(lái)分析:

          1. tcpdump -vv host 192.168.66.27 and port 2181 -w 2181.cap  
          但是也沒(méi)有發(fā)現(xiàn)什么有用信息,的確是TCP連接連上,再FIN,ACK連接斷開。

          查看應(yīng)用日志,發(fā)現(xiàn)Tomcat webcontext沒(méi)有正常啟動(dòng)

          沒(méi)辦法了,有兩種考慮,一個(gè)是用strace,二是用btrace。但是btrace好久沒(méi)用過(guò)了,不太想再去看例子文檔。

          還好,去下btrace之后,先去看了下應(yīng)用的日志,發(fā)現(xiàn)應(yīng)用報(bào)了一些ClassLoader的錯(cuò)誤:

          1. Nov 24, 2014 7:32:43 PM org.apache.catalina.loader.WebappClassLoader loadClass  
          2. INFO: Illegal access: this web application instance has been stopped already.  Could not load org.apache.zookeeper.ClientCnxnSocketNIO.  The eventual following stack trace is caused by an err  
          3. or thrown for debugging purposes as well as to attempt to terminate the thread which caused the illegal access, and has no functional impact.  
          4. java.lang.IllegalStateException  
          5.         at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1564)  
          6.         at org.apache.catalina.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1523)  
          7.         at ch.qos.logback.classic.spi.PackagingDataCalculator.loadClass(PackagingDataCalculator.java:198)  
          8.         at ch.qos.logback.classic.spi.PackagingDataCalculator.bestEffortLoadClass(PackagingDataCalculator.java:226)  
          9.         at ch.qos.logback.classic.spi.PackagingDataCalculator.computeBySTEP(PackagingDataCalculator.java:132)  
          10.         at ch.qos.logback.classic.spi.PackagingDataCalculator.populateUncommonFrames(PackagingDataCalculator.java:107)  
          11.         at ch.qos.logback.classic.spi.PackagingDataCalculator.populateFrames(PackagingDataCalculator.java:99)  

          因?yàn)橛薪?jīng)驗(yàn)了,馬上知道這個(gè)Tomcat因?yàn)槠渌騱ebcontext實(shí)始化失敗退出,然后后面的一些線程繼續(xù)跑時(shí),會(huì)拋出ClassLoader,或者Class not found的異常。

          于是猜想到原因了:

          Tomcat webcontext初始化失敗,zookeeper的重連線程自動(dòng)不斷重連。

          但是為什么重啟Tomcat之后,沒(méi)有重現(xiàn)TIME_WAIT的情況?

          再折騰了下,發(fā)現(xiàn)只有當(dāng)zookeeper重啟后,應(yīng)用才會(huì)出現(xiàn)大量的TIME_WAIT連接。報(bào)的是下面這個(gè)異常:

          1. 2014-11-24 19:42:44,399 [Thread-3-SendThread(192.168.90.147:4181)] WARN  org.apache.zookeeper.ClientCnxn - Session 0x149c21809731325 for server 192.168.90.147/192.168.90.147:4181, unexpected error, closing socket connection and attempting reconnect  
          2. java.lang.NoClassDefFoundError: org/apache/zookeeper/proto/SetWatches  
          3.         at org.apache.zookeeper.ClientCnxn$SendThread.primeConnection(ClientCnxn.java:867) ~[zookeeper-3.4.5.jar:3.4.5-1392090]  
          4.         at org.apache.zookeeper.ClientCnxnSocketNIO.doTransport(ClientCnxnSocketNIO.java:352) ~[zookeeper-3.4.5.jar:3.4.5-1392090]  

          這個(gè)異常的原因,是某些zookeeper的類沒(méi)有加載到。

          最終原因分析

          梳理下整個(gè)流程:

          1. Tomcat啟動(dòng),初始化webcontext;
          2. 初始化spring, spring初始某些些bean,這些bean包括了zookeeper的連接相關(guān)的bean;
          3. 這時(shí)zkClient(獨(dú)立線程)已經(jīng)連接上服務(wù)器了,但是classloader沒(méi)有加載到org/apache/zookeeper/proto/SetWatches類;
          4. spring初始化失敗,導(dǎo)致Tomcat webcontext初始化也失敗,應(yīng)用在掛起狀態(tài),但zkClient線程還是正常的;
          5. zookeeper服務(wù)器重啟,zkClient開始重連,連接上zookeeper服務(wù)器;
          6. zkClient觸發(fā)watch的一些代碼,ClassLoader嘗試加載org/apache/zookeeper/proto/SetWatches類,但是發(fā)現(xiàn)找不到類,于是拋出異常;
          7. zkClient捕獲到異常,認(rèn)為重連失敗,close掉connection,休眠幾秒之后,再次重連;

          于是出現(xiàn)了zkClient反復(fù)重試連接zookeeper服務(wù)器,而且都是秒連秒斷的情況。


          總結(jié):

          這次排查花了不少時(shí)間,有個(gè)原因是開始沒(méi)有去查看應(yīng)用的日志,以為應(yīng)用的是正常的,而且zookeeper.out的輸出日志很多,也有一段時(shí)間了。

          還有線上的應(yīng)用比較坑爹,活動(dòng)已經(jīng)過(guò)期很久了,但是程序還是線上跑,也沒(méi)有人管是否出問(wèn)題了。

          所以,主要精力放在各種網(wǎng)絡(luò)連接狀態(tài)的獲取上。對(duì)去查看應(yīng)用日志比較排斥。

          還有一個(gè)原因是,問(wèn)題比較詭異,有點(diǎn)難重現(xiàn),當(dāng)發(fā)現(xiàn)可以重現(xiàn)時(shí),基本已經(jīng)發(fā)現(xiàn)問(wèn)題所在了。

          排查問(wèn)題還是要耐心收集信息,再分析判斷。

          posted @ 2015-09-10 10:41 小馬歌 閱讀(1953) | 評(píng)論 (0)編輯 收藏
           
               摘要: from:http://www.infoq.com/cn/articles/java-profiling-with-open-source/不止一次,我們都萌發(fā)過(guò)想對(duì)運(yùn)行中程序的底層狀況一探究竟的念頭。產(chǎn)生這種需求的原因可能是運(yùn)行緩慢的服務(wù)、Java虛擬機(jī)(JVM)崩潰、掛起、死鎖、頻繁的JVM暫停、突然或持續(xù)的高CPU使用率、甚至于可怕的內(nèi)存溢出(OOME)。好消息是現(xiàn)在已有許多工具能幫你得到J...  閱讀全文
          posted @ 2015-09-06 15:02 小馬歌 閱讀(428) | 評(píng)論 (0)編輯 收藏
           
               摘要: from:http://www.sxt.cn/u/324/blog/3188LVS一、LVS簡(jiǎn)介    LVS是Linux Virtual Server的簡(jiǎn)寫,意即Linux虛擬服務(wù)器,是一個(gè)虛擬服務(wù)器集群系統(tǒng)。本項(xiàng)目在1998年5月由章文嵩博士成立,是中國(guó)國(guó)內(nèi)最早出現(xiàn)的自由軟件之一。二、LVS的分類LVS-NAT:地址轉(zhuǎn)換LVS-DR: 直接路由LVS-T...  閱讀全文
          posted @ 2015-09-02 16:07 小馬歌 閱讀(489) | 評(píng)論 (0)編輯 收藏
          僅列出標(biāo)題
          共95頁(yè): First 上一頁(yè) 9 10 11 12 13 14 15 16 17 下一頁(yè) Last 
           
          主站蜘蛛池模板: 来安县| 安庆市| 民勤县| 大石桥市| 盐源县| 泰顺县| 屏东县| 滕州市| 临夏市| 营口市| 濮阳县| 盐津县| 遵化市| 蛟河市| 黄骅市| 武乡县| 左云县| 郴州市| 甘孜县| 南岸区| 石渠县| 定南县| 佛学| 赤城县| 泸定县| 岱山县| 霍州市| 吴堡县| 江陵县| 庄河市| 合山市| 徐水县| 三门峡市| 荃湾区| 张家界市| 宁河县| 游戏| 达拉特旗| 永兴县| 富顺县| 夹江县|