<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

         摘要: 作者:自由飛網址:http://www.cnblogs.com/freeflying/p/4788494.html我們在上一篇《架構之路(1):目標》中設定了架構的目標,只有一個,就是可維護性。完全沒有提性能,這是故意的。似乎程序員都是急性子,或許是被windows冗長的開機時間折磨夠了,有可能是因為提升性能的效果是最顯而易見的……總之,我發現,絕大部分程序員對性能的關注和...  閱讀全文
    posted @ 2015-09-25 17:00 小馬歌 閱讀(352) | 評論 (0)編輯 收藏
     
    from:http://blog.jobbole.com/91749/

    一、越來越多的并發連接數

    現在的Web系統面對的并發連接數在近幾年呈現指數增長,高并發成為了一種常態,給Web系統帶來不小的挑戰。以最簡單粗暴的方式解決,就是增加Web系統的機器和升級硬件配置。雖然現在的硬件越來越便宜,但是一味地通過增加機器來解決并發量的增長,成本是非常高昂的。結合技術優化方案,才是更有效的解決方法。

    并發連接數為什么呈指數增長?實際上,從這幾年的用戶基數上看,這個數量并沒有出現指數增長,因此它并非主要原因。主要原因,還是web變得更復雜,交互更豐富所導致的。

    1. 頁面元素增多,交互復雜

    Web頁面元素越來越多,更為豐富。更多的資源元素,意味著更多的下載請求。Web系統的交互越來越復雜,交互場景和次數也大幅增加。以“www.qq.com”的首頁為例子,刷新一次,大概會有244個請求。并且,在頁面打開完成之后,還會有一些定時的查詢或者上報請求持續運作。

    目前的Http請求,為了減少反復的創建和銷毀連接行為,通常都建立長連接(Connection keep-alive)。一經建立,這個連接會被保持住一段時間,被后續請求復用。然而,它也帶來了另一個新的問題,連接的保持是會占用Web系統服務端資源的,如果不充分使用這個連接,會導致資源浪費。長連接被創建后,首批資源傳輸完畢,之后幾乎沒有數據交互,一直到超時時間,才會自動釋放長連接占據的系統資源。

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

    2. 主流的本瀏覽器的連接數在增加

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

    這樣自然無形增加了Web系統后端的壓力,更多的下載連接意味著占據了更多的Web服務器的資源。而在用戶訪問高峰期,自熱而然就形成了“高并發”場景。這些連接和請求,占據了服務器的大量CPU和內存等資源。尤其在資源數目超過100+的網站頁面中,使用更多的下載連接,非常有必要。

     

    二、Web前端優化,降低服務端壓力

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

    1. 減少Web請求

    常用的實現方法是通過Http協議頭中的expire或max-age來控制,將靜態內容放入瀏覽器的本地緩存,在之后的一段時間里,不再請求Web服務器,直接使用本地資源。還有HTML5中的本地存儲技術(LocalStorage),也被作為一個強大的數據本地緩存。

    這種方案緩存后,根本不發送請求到Web服務器,大幅降低服務器壓力,也帶來了良好的用戶體驗。但是,這種方案,對首次訪問的用戶無效,同時,也影響部分Web資源的實時性。

    2. 減輕Web請求

    瀏覽器的本地緩存是存在過期時間的,一旦過期,就必須重新向服務器請求。這個時候,會有兩種情形:

    (1)服務器的資源內容沒有更新,瀏覽器請求Web資源,服務器回復“可以繼續使用本地緩存”。(發生通信,但是Web服務器只需要做簡單“回復”)

    (2)服務器的文件或者內容已經更新,瀏覽器請求Web資源,Web服務器通過網絡傳輸新的資源內容。(發生通信,Web服務器需要完成復雜的傳輸工作)

    這里的協商方式是通過Http協議的Last-Modified或Etag來控制,這個時候請求服務器,如果是內容沒有發生變更的情況,服務器會返回304 Not Modified。這樣的話,就不需要每次請求Web服務器都做復雜的傳輸完整數據文件的工作,只要簡單的http應答就可以達到相同的效果。

    雖然上述請求,起到“減輕”Web服務器的壓力,但是連接仍然被建立,請求也發生了。

    3. 合并頁面請求

    如果是比較老一些的Web開發者,應該會更有印象,在ajax盛行之前。頁面大部分都是直接輸出的,并沒有這么多的ajax請求,Web后端將頁面內容完全拼湊好了,再返回給前端。那個時候,頁面靜態化,是一個挺廣泛的優化方式。后來,被交互更友好的ajax漸漸替代了,一個頁面的請求也變得越來越多。

    由于移動端的網絡(2G/3G)比起PC寬帶差很多,并且部分手機配置比較低,面對一個超過100個請求的網頁,加載的速度會緩慢很多。于是,優化的方向又重新回到合并頁面元素,減少請求數量:

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

    (2)Ajax動態內容合并請求。對于動態內容,將10次Ajax請求合并為1次的批量信息查詢。

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

    合并請求,減少了傳輸數據的次數,也就是相當于將它們從一個一個地請求,變為一次的“批量”請求。上述優化方法,到達“減輕”Web服務器壓力的目的,減少了需要建立的連接。

     

    三、 節約Web服務端的內存

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

    (1)用來維持連接的基本內存,進程初始化時,會載入一些基礎模塊到內存。

    (2)被傳輸的數據內容載入到各個緩沖區,占據的內存。

    (3)程序執行過程中,申請和使用的內存。

    如果維持一個連接,能夠盡可能少占用內存,那么我們就可以維持更多的并發連接,從而讓Web服務器支持更多的并發連接數。

    Apache(httpd)是一個成熟并且古老的Web服務,而Apache的發展和演變,一直在追求做到這一點,它試圖不斷減少服務占據的內存,以支持更大的并發量。以Apache的工作模式的演變為視角,我們一起來看看,它們是如何優化內存的問題的。

    1. prefork MPM,多進程工作模式

    prefork是Apache最成熟和穩定的工作模式,即使是現在,仍然被廣泛使用。主進程生成后,它先完成基礎的初始化工作,然后,通過fork預先產生一批的子進程(子進程會復制父進程的內存空間,不需要再做基礎的初始化工作)。然后等待服務,之所以預先生成,是為了減少頻繁創建和銷毀進程的開銷。多進程的好處,是進程之間的內存數據不會相互干擾,同時,某個進程異常終止也不會影響其他進程。但是,就內存而言,每個httpd子進程占用了很多的內存,因為子進程的內存數據是復制父進程的。我們可以粗略認為,這里存在大量的“重復數據”被放在內存中。最終,導致我們能夠生成的子進程最大數量是很有限。在面對高并發時,因為有不少Keep-alive的長連接,將這些子進程“霸占”住,很可能導致可用子進程耗盡。因此,prefork并不太適合高并發場景。

    • 優點:成熟穩定,兼容所有新老模塊。同時,不需要擔心線程安全的問題。(例如,我們常用的mod_php,將PHP編譯為Apache的子模塊,就不需要支持線程安全)
    • 缺點:一個服務進程占用很多內存。

    2. worker MPM,多進程和多線程的混合模式

    worker模式比起prefork,是使用了多進程和多線程的混合模式。它也預先fork了幾個子進程(數量很少),然后每個子進程創建一些線程(其中包括一個監聽線程)。每個請求過來,會被分配到1個線程來服務。線程比起進程會更輕量,因為線程通常會共享父進程的內存空間,因此,內存的占用會減少一些。在高并發的場景下,因為比起prefork更省內存,因此會有更多的可用線程。

    但是,它并沒有解決Keep-alive的長連接“霸占”線程的問題,只是對象變成了比較輕量的線程。

    有些人會覺得奇怪,那么這里為什么不完全使用多線程呢,還要引入多進程?因為還需要考慮穩定性,如果一個線程掛了,會導致同一個進程下其他正常的子線程都掛了。如果全部采用多線程,某個線程掛掉,就導致整個Apache服務“全軍覆沒”。而目前的工作模式,受影響的只是Apache的一部分服務,而不是整個服務。

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

    • 優點:占據更少的內存,高并發下表現更優秀。
    • 缺點:必須考慮線程安全的問題,同時鎖的引入又增加了CPU的開銷。

    3. event MPM,多進程和多線程的混合模式,引入Epoll

    這個是Apache中比較新的模式,在現在的版本(Apache 2.4.10)已經是穩定可用的模式。它和worker模式很像,最大的區別在于,它解決了keep-alive場景下,長期被占用的線程的資源浪費問題。event MPM中,會有一個專門的線程來管理這些keep-alive類型的線程,當有真實請求過來的時候,將請求傳遞給服務線程,執行完畢后,又允許它釋放。它減少了“占據”連接而又不使用的資源浪費,增強了高并發場景下的請求處理能力。因為減少了“閑等”的線程,線程的數量減少,同等場景下,內存占用會下降一些。

    event MPM在遇到某些不兼容的模塊時,會失效,將會回退到worker模式,一個工作線程處理一個請求。新版Apache官方自帶的模塊,全部是支持event MPM的。注意一點,event MPM需要Linux系統(Linux 2.6+)對EPoll的支持,才能啟用。Apache的三種模式中在真實應用場景中,event MPM是最節約內存的。

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

    雖然Apache的不斷優化,減少了內存占用,從而增加了處理高并發的能力。但是,正如前面所說,Apache是一個古老而成熟的Web服務,同時,集成很多穩定的模塊,是一個比較重的Web服務。Nginx是個比較輕量的Web服務,占據的內存天然就少于Apache。而且,Nginx通過一個進程來服務于N個連接。所使用的方式,并不是Apache的增加進程/線程來支持更多的連接。對于Nginx來說,它少創建了大量的進程/線程,減少了很多內存的開銷。

    靜態文件的QPS性能壓測結果,Nginx性能大概3倍于Apache對靜態文件的處理。PHP等動態文件的QPS,Nginx的做法通常是通過FastCGI的方式和PHP-FPM通信的方式完成,PHP作為一個與之無關的外部服務存在。而Apache通常將PHP編譯為自己的字模塊(新版的Apache也支持FastCGI)。PHP動態文件,Nginx的表現略遜于Apache。

    5. sendfile節約內存

    Apache、Nginx等不少Web服務,都帶有sendfile支持的。sendfile可以減少數據到“用戶態內存空間”(用戶緩沖區)的拷貝,進而減少內存的占用。當然,很多同學第一個反應當然是問Why?為了盡可能清楚講述這個原理,我們就先回Linux內核態和用戶態的存儲空間的交互。

    一般情況下,用戶態(也就是我們的程序所在的內存空間)是不會直接讀寫或者操作各種設備(磁盤、網絡、終端等),中間通常用內核作為“中間人”,來完成對設備的操作或者讀寫。

    以最簡單的磁盤讀寫例子,從磁盤中讀取A文件,寫入到B文件。A文件數據是從磁盤開始,然后載入到“內核緩沖區”,然后再拷貝到“用戶緩沖區”,我們才可以對數據進行處理。寫入的時候,也同理,從“用戶態緩沖區”載入到“內核緩沖區”,最后寫入到磁盤B文件。

    這樣寫文件很累吧,于是有人覺得這里可以跳過“用戶緩沖區”的拷貝。其實,這就是MMP(Memory-Mapping,內存映射)的實現,建立一個磁盤空間和內存的直接映射,數據不再復制到“用戶態緩沖區”,而是返回一個指向內存空間的指針。于是,我們之前的讀寫文件例子,就會變成,A文件數據從磁盤載入到“內核緩沖區”,然后從“內核緩沖區”復制到B文件的“內核緩沖區”,B文件再從”內核緩沖區“寫回到磁盤中。這個過程,減少了一次內存拷貝,同時也少內存占用。

    好了,回到sendfile的話題上來,簡單的說,sendfile的做法和MMP類似,就是減少數據從”內核態緩沖區“到”用戶態緩沖區“的內存拷貝。

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

    使用sendfile之后:

    這種方式,不僅節省了內存,而且還有CPU的開銷。

     

    四、節約Web服務器的CPU

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

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

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

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

    select/poll返回的是一個我們之前提交的文件描述符集合(內核將其中可讀、可寫或者異常狀態的socket文件描述符的標識位修改了),我們需要通過輪詢檢查才能獲得我們可以操作的文件描述符。在這個過程中,不斷重復執行。在實際應用場景中,大部分被我們監控的socket文件描述符,都是”空閑的“,也就是說,不能操作。我們對整個集合輪詢,就是為了找了少部分我們可以操作的socket文件描述符。于是,當我們監控的socket文件描述符越多(用戶并發連接數越來越多),這個輪詢工作,也就越來越沉重,進而導致增大了CPU的開銷。

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

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

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

    其實,有一種極端點的場景,就是我們全部文件描述符幾乎都是”活躍“的,這樣反而導致了大量回調函數的執行,又增加了CPU的開銷。但是,就Web服務的真實場景,絕大部分時候,都是連接集合中都存在很多”空閑“連接。

    3. 線程/進程的創建銷毀和上下文切換

    通常,Apache某一個時間內,是一個進程/線程服務于一個連接。于是,Apache就有很多的進程/線程,服務于很多的連接。Web服務在高峰期,會建立很多的進程/線程,也就帶來很多的上下文切換開銷。而Nginx,它通常只有1個master主進程和幾個worker子進程,然后,1個worker進程服務很多個連接,進而節省了CPU的上下文切換開銷。

    兩種模式雖然不同,但實際上不能直接出分好壞,綜合來說,各有各自的優勢,就不妄議了哈。

    4. 多線程下的鎖對CPU的開銷

    Apache中的worker和event模式,都有采用多線程。多線程因為共享父進程的內存空間,在訪問共享數據的時候,就會產生競爭,也就是線程安全問題。因此通常會引入鎖(Linux下比較常用的線程相關的鎖有互斥量metux,讀寫鎖rwlock等),成功獲取鎖的線程可以繼續執行,獲取失敗的通常選擇阻塞等待。引入鎖的機制,程序的復雜度往往增加不少,同時還有線程“死鎖”或者“餓死”的風險(多進程在訪問進程間共享資源的時候,也有同樣的問題)。

    死鎖現象(兩個線程彼此鎖住對方想要獲取的資源,相互阻塞等待,永遠無法達不到滿足條件):

    餓死現象(某個線程,一直獲取不到它想要鎖資源,永遠無法執行下一步):

    為了避免這些鎖導致的問題,就不得不加大程序的復雜度,解決方案一般有:

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

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

    在使用PHP的時候,在Apache的worker和event模式下,也必須兼容線程安全。通常,新版本的PHP官方庫是沒有線程安全方面的問題,需要關注的是第三方擴展。PHP實現線程安全,不是通過鎖的方式實現的。而是為每個線程獨立申請一份全局變量的副本,相當于線程的私人內存空間,但是這樣做相對消耗多一些內存。不過,這樣的好處,是不需要引入復雜的鎖機制實現,也避免了鎖機制對CPU的開銷。

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

     

    五、 小結

    可能有些同學看完之后,會得出結論,Nginx+PHP-FPM的工作方式,似乎是最節省系統資源的Web系統工作方式。某種程度上說,的確是可以這么說的,但是Web系統的搭建,需要從實際業務應用的角度出發,具體問題需要具體分析,尋求最合適的技術方案。

    Web服務的不斷演變和發展,努力地追求用盡可能少的系統資源,來支撐更多的用戶請求,這是一條波瀾壯闊的前進之路。這些技術方案,匯聚了很多值得學習和借鑒的解決問題的思路。

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

      safe_mode_exec_dir = D:/tmp/cmd

      但是,我更推薦不要執行任何程序,那么就可以指向我們網頁目錄:

      safe_mode_exec_dir = D:/usr/www

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

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

      safe_mode_include_dir = D:/usr/www/include/

      其實一般php腳本中包含文件都是在程序自己已經寫好了,這個可以根據具體需要設置。

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

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

      open_basedir = D:/usr/www

    (6) 關閉危險函數

      如果打開了安全模式,那么函數禁止是可以不需要的,但是我們為了安全還是考慮進去。比如,
      我們覺得不希望執行包括system()等在那的能夠執行命令的php函數,或者能夠查看php信息的
      phpinfo()等函數,那么我們就可以禁止它們:
    disable_functions = system,passthru,exec,shell_exec,popen,phpinfo,escapeshellarg,escapeshellcmd,proc_close,proc_open,dl,show_source,get_cfg_var

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

      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

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

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

      我們為了防止黑客獲取服務器中php版本的信息,可以關閉該信息斜路在http頭中:

      expose_php = Off

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

    (8) 關閉注冊全局變量

      在PHP中提交的變量,包括使用POST或者GET提交的變量,都將自動注冊為全局變量,能夠直接訪問,
      這是對服務器非常不安全的,所以我們不能讓它注冊為全局變量,就把注冊全局變量選項關閉:
      register_globals = Off
      當然,如果這樣設置了,那么獲取對應變量的時候就要采用合理方式,比如獲取GET提交的變量var,
      那么就要用$_GET['var']來進行獲取,這個php程序員要注意。

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

      SQL注入是非常危險的問題,小則網站后臺被入侵,重則整個服務器淪陷,

      所以一定要小心。php.ini中有一個設置:

      magic_quotes_gpc = Off

    這個默認是關閉的,如果它打開后將自動把用戶提交對sql的查詢進行轉換,
      比如把 ' 轉為 \'等,這對防止sql注射有重大作用。所以我們推薦設置為:
      magic_quotes_gpc = On

    (10) 錯誤信息控制

      一般php在沒有連接到數據庫或者其他情況下會有提示錯誤,一般錯誤信息中會包含php腳本當
      前的路徑信息或者查詢的SQL語句等信息,這類信息提供給黑客后,是不安全的,所以一般服務器建議禁止錯誤提示:

      display_errors = Off

      如果你卻是是要顯示錯誤信息,一定要設置顯示錯誤的級別,比如只顯示警告以上的信息:

      error_reporting = E_WARNING & E_ERROR

      當然,我還是建議關閉錯誤提示。

    (11) 錯誤日志

      建議在關閉display_errors后能夠把錯誤信息記錄下來,便于查找服務器運行的原因:

    log_errors = On

      同時也要設置錯誤日志存放的目錄,建議根apache的日志存在一起:

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

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

    【一、在服務器端配置】

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

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

    (1) 打開php的安全模式

    php的安全模式是個非常重要的內嵌的安全機制,能夠控制一些php中的函數,比如system(),

    同時把很多文件操作函數進行了權限控制,也不允許對某些關鍵文件的文件,比如/etc/passwd,

    但是默認的php.ini是沒有打開安全模式的,我們把它打開:

    safe_mode = on

    (2) 用戶組安全

    當safe_mode打開時,safe_mode_gid被關閉,那么php腳本能夠對文件進行訪問,而且相同

    組的用戶也能夠對文件進行訪問。

    建議設置為:

    safe_mode_gid = off

    如果不進行設置,可能我們無法對我們服務器網站目錄下的文件進行操作了,比如我們需要

    對文件進行操作的時候。

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

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

    safe_mode_exec_dir = D:/usr/bin

    一般情況下是不需要執行什么程序的,所以推薦不要執行系統程序目錄,可以指向一個目錄,

    然后把需要執行的程序拷貝過去,比如:

    safe_mode_exec_dir = D:/tmp/cmd

    但是,我更推薦不要執行任何程序,那么就可以指向我們網頁目錄:

    safe_mode_exec_dir = D:/usr/www

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

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

    safe_mode_include_dir = D:/usr/www/include/

    其實一般php腳本中包含文件都是在程序自己已經寫好了,這個可以根據具體需要設置。

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

    使用open_basedir選項能夠控制PHP腳本只能訪問指定的目錄,這樣能夠避免PHP腳本訪問

    不應該訪問的文件,一定程度上限制了phpshell的危害,我們一般可以設置為只能訪問網站目錄:

    open_basedir = D:/usr/www

    (6) 關閉危險函數

    如果打開了安全模式,那么函數禁止是可以不需要的,但是我們為了安全還是考慮進去。比如,

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

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

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

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

    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

    以上只是列了部分不叫常用的文件處理函數,你也可以把上面執行命令函數和這個函數結合,

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

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

    我們為了防止黑客獲取服務器中php版本的信息,可以關閉該信息斜路在http頭中:

    expose_php = Off

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

    (8) 關閉注冊全局變量

    在PHP中提交的變量,包括使用POST或者GET提交的變量,都將自動注冊為全局變量,能夠直接訪問,

    這是對服務器非常不安全的,所以我們不能讓它注冊為全局變量,就把注冊全局變量選項關閉:

    register_globals = Off

    當然,如果這樣設置了,那么獲取對應變量的時候就要采用合理方式,比如獲取GET提交的變量var,

    那么就要用$_GET['var']來進行獲取,這個php程序員要注意。

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

    SQL注入是非常危險的問題,小則網站后臺被入侵,重則整個服務器淪陷,

    所以一定要小心。php.ini中有一個設置:

    magic_quotes_gpc = Off

    這個默認是關閉的,如果它打開后將自動把用戶提交對sql的查詢進行轉換,

    比如把 ' 轉為 \'等,這對防止sql注射有重大作用。所以我們推薦設置為:

    magic_quotes_gpc = On

    (10) 錯誤信息控制

    一般php在沒有連接到數據庫或者其他情況下會有提示錯誤,一般錯誤信息中會包含php腳本當

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

    display_errors = Off

    如果你卻是是要顯示錯誤信息,一定要設置顯示錯誤的級別,比如只顯示警告以上的信息:

    error_reporting = E_WARNING & E_ERROR

    當然,我還是建議關閉錯誤提示。

    (11) 錯誤日志

    建議在關閉display_errors后能夠把錯誤信息記錄下來,便于查找服務器運行的原因:

    log_errors = On

    同時也要設置錯誤日志存放的目錄,建議根apache的日志存在一起:

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

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

    MYSQL的降權運行

    新建立一個用戶比如mysqlstart

    net user mysqlstart fuckmicrosoft /add

    net localgroup users mysqlstart /del

    不屬于任何組

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

    然后在系統服務中設置,MYSQL的服務屬性,在登錄屬性當中,選擇此用戶 mysqlstart 然后輸入密碼,確定。

    重新啟動 MYSQL服務,然后MYSQL就運行在低權限下了。

    如果是在windos平臺下搭建的apache我們還需要注意一點,apache默認運行是system權限,

    這很恐怖,這讓人感覺很不爽.那我們就給apache降降權限吧。

    net user apache fuckmicrosoft /add

    net localgroup users apache /del

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

    我們打開計算機管理器,選服務,點apache服務的屬性,我們選擇log on,選擇this account,我們填入上面所建立的賬戶和密碼,

    重啟apache服務,ok,apache運行在低權限下了。

    實際上我們還可以通過設置各個文件夾的權限,來讓apache用戶只能執行我們想讓它能干的事情,給每一個目錄建立一個單獨能讀寫的用戶。

    這也是當前很多虛擬主機提供商的流行配置方法哦,不過這種方法用于防止這里就顯的有點大材小用了。 


    【二、在PHP代碼編寫】

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

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

    posted @ 2015-09-17 10:09 小馬歌 閱讀(264) | 評論 (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 小馬歌 閱讀(345) | 評論 (0)編輯 收藏
     
    NetCat,在網絡工具中有ldquo;瑞士軍刀rdquo;美譽,其有Windows和Linux的版本。因為它短小精悍(1.84版本也不過25k,舊版本或縮

    NetCat,在網絡工具中有“瑞士軍刀”美譽,其有Windows和Linux的版本。因為它短小精悍(1.84版本也不過25k,舊版本或縮減版甚至更小)、功能實用,被設計為一個簡單、可靠的網絡工具,可通過TCP或UDP協議傳輸讀寫數據。同時,它還是一個網絡應用Debug分析器,因為它可以根據需要創建各種不同類型的網絡連接。

    一、版本
    通常的Linux發行版中都帶有NetCat(簡稱nc),甚至在拯救模式光盤中也由busybox提供了簡版的nc工具。但不同的版本,其參數的使用略有差異。
    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版本進行簡單說明。
    假設兩服務器信息:

    server1: 192.168.10.10
    server2: 192.168.10.11

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

    server2上運行: nc -l 1234 > text.txt

    server1上運行: nc 192.168.10.11 1234 < text.txt

    注:server2上的監聽要先打開


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

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

    server1上執行傳輸,即可完成從server1克隆sda硬盤到server2的任務:

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

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

    3、端口掃描
    可以執行:

    # 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頁面

    # 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命令后,輸入紅色部分的內容,然后按兩次回車,即可從對方獲得HTTP Headers內容。

    6、聊天
    nc還可以作為簡單的字符下聊天工具使用,同樣的,server2上需要啟動監聽:

    server2上啟動:# nc -lp 1234 
    server1上傳輸:# nc 192.168.10.11 1234


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

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

    server2上運行:# nc -l 1234 |tar xzvf -

    server1上運行:# tar czvf - nginx-0.6.34|nc 192.168.10.11 1234

     


    8、用nc命名操作memcached

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

    posted @ 2015-09-16 09:01 小馬歌 閱讀(504) | 評論 (0)編輯 收藏
     
         摘要: ZooKeeper是一個分布式的,開放源碼的分布式應用程序協調服務,它包含一個簡單的原語集,分布式應用程序可以基于它實現同步服務,配置維護和命名服務等。Zookeeper是hadoop的一個子項目,其發展歷程無需贅述。在分布式應用中,由于工程師不能很好地使用鎖機制,以及基于消息的協調機制不適合在某些應用中使用,因此需要有一種可靠的、可擴展的、分布式的、可配置的協調機制來統一系統的狀態。Zookee...  閱讀全文
    posted @ 2015-09-10 17:55 小馬歌 閱讀(358) | 評論 (0)編輯 收藏
     
    from:http://blog.csdn.net/zheng0518/article/details/44943357

    問題現象

    最后發現線上的zookeeper的日志zookeeper.out 文件居然有6G,后來設置下日志為滾動輸出,參考:

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

    但是改了之后,發現一天的日志量就是100多M,滾動日志一天就被沖掉了,這個不科學。

    再仔細查看下日志里的內容,發現有很多連接建立好,馬上又斷開:

    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)  
    從日志輸出的時間來看,秒連秒斷,非常詭異。

    排查問題

    用netstat查看網絡連接狀態

    到client的服務器上查看連接的狀態:

    1. netstat -antp | grep 2181  

    發現有很多TIME_WAIT狀態的連接:

    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狀態的連接是看不到進程號的。搜索研究了下netstat的參數,發現沒有辦法輸出TIME_WAIT狀態的連接的pid,只好嘗試其它的辦法。

    再用 jstack -l pid 來查看進程的線程棧,也沒有發現什么異常的東東。查看到有幾個zookeeper連接的線程,但也是正常狀態。

    再檢查了機器的IO,CPU,內存,也沒有異常的情況。

    沒找到什么有用的信息,只好再研究下netstat的參數:
    發現用 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  

    發現user是root。于是以為是非Java應用,在不斷地連接zookeeper。于是停止java程序,發現沒有TIME_WAIT連接了。
    但是確認是Java應用的問題,于是再重啟Java應用,但沒有再發現TIME_WAIT情況。很詭異。

    問題不能重現了,相當的蛋疼。忽然想到線上的應用也許也有這個問題,于是到線下zookeeper服務器上查看了下,果然發現有同樣的問題。

    用tcpdump抓包和wireshark分析

    先用tcpdump來查看下具體的網絡連接,發現的確是連接連上再斷開。于是先保存成cap文件,再用wireshark來分析:

    1. tcpdump -vv host 192.168.66.27 and port 2181 -w 2181.cap  
    但是也沒有發現什么有用信息,的確是TCP連接連上,再FIN,ACK連接斷開。

    查看應用日志,發現Tomcat webcontext沒有正常啟動

    沒辦法了,有兩種考慮,一個是用strace,二是用btrace。但是btrace好久沒用過了,不太想再去看例子文檔。

    還好,去下btrace之后,先去看了下應用的日志,發現應用報了一些ClassLoader的錯誤:

    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)  

    因為有經驗了,馬上知道這個Tomcat因為其它原因webcontext實始化失敗退出,然后后面的一些線程繼續跑時,會拋出ClassLoader,或者Class not found的異常。

    于是猜想到原因了:

    Tomcat webcontext初始化失敗,zookeeper的重連線程自動不斷重連。

    但是為什么重啟Tomcat之后,沒有重現TIME_WAIT的情況?

    再折騰了下,發現只有當zookeeper重啟后,應用才會出現大量的TIME_WAIT連接。報的是下面這個異常:

    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]  

    這個異常的原因,是某些zookeeper的類沒有加載到。

    最終原因分析

    梳理下整個流程:

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

    于是出現了zkClient反復重試連接zookeeper服務器,而且都是秒連秒斷的情況。


    總結:

    這次排查花了不少時間,有個原因是開始沒有去查看應用的日志,以為應用的是正常的,而且zookeeper.out的輸出日志很多,也有一段時間了。

    還有線上的應用比較坑爹,活動已經過期很久了,但是程序還是線上跑,也沒有人管是否出問題了。

    所以,主要精力放在各種網絡連接狀態的獲取上。對去查看應用日志比較排斥。

    還有一個原因是,問題比較詭異,有點難重現,當發現可以重現時,基本已經發現問題所在了。

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

    posted @ 2015-09-10 10:41 小馬歌 閱讀(1946) | 評論 (0)編輯 收藏
     
         摘要: from:http://www.infoq.com/cn/articles/java-profiling-with-open-source/不止一次,我們都萌發過想對運行中程序的底層狀況一探究竟的念頭。產生這種需求的原因可能是運行緩慢的服務、Java虛擬機(JVM)崩潰、掛起、死鎖、頻繁的JVM暫停、突然或持續的高CPU使用率、甚至于可怕的內存溢出(OOME)。好消息是現在已有許多工具能幫你得到J...  閱讀全文
    posted @ 2015-09-06 15:02 小馬歌 閱讀(425) | 評論 (0)編輯 收藏
     
         摘要: from:http://www.sxt.cn/u/324/blog/3188LVS一、LVS簡介    LVS是Linux Virtual Server的簡寫,意即Linux虛擬服務器,是一個虛擬服務器集群系統。本項目在1998年5月由章文嵩博士成立,是中國國內最早出現的自由軟件之一。二、LVS的分類LVS-NAT:地址轉換LVS-DR: 直接路由LVS-T...  閱讀全文
    posted @ 2015-09-02 16:07 小馬歌 閱讀(483) | 評論 (0)編輯 收藏
    僅列出標題
    共95頁: First 上一頁 9 10 11 12 13 14 15 16 17 下一頁 Last 
     
    主站蜘蛛池模板: 成人免费区一区二区三区| 亚洲一区二区免费视频| 久久久久亚洲av成人无码电影| 噜噜嘿在线视频免费观看| 国产大片线上免费观看| 免费三级毛片电影片| 无码国产精品一区二区免费| 91香蕉国产线在线观看免费 | 亚洲精品国偷自产在线| 久久久久亚洲av成人无码电影| 91麻豆精品国产自产在线观看亚洲 | 亚洲人成电影在线观看青青| 亚洲最大在线视频| 亚洲校园春色另类激情| 在线观看日本亚洲一区| 日本亚洲欧美色视频在线播放| 亚洲1区2区3区精华液| 日亚毛片免费乱码不卡一区| 一个人看的www在线免费视频 | 日韩色视频一区二区三区亚洲| 免费无码国产在线观国内自拍中文字幕| 免费一级特黄特色大片| 久久国产精品免费| 久久一本岛在免费线观看2020| 国产黄色免费网站| 24小时日本在线www免费的| 国产成人免费一区二区三区| 亚洲午夜福利精品无码| 亚洲αv久久久噜噜噜噜噜| 亚洲黄色高清视频| 亚洲国产精品无码观看久久| 国产综合激情在线亚洲第一页| 国产免费MV大全视频网站| 午夜免费福利片观看| 国产成人无码免费看视频软件| 国产一级淫片a免费播放口之| 亚洲色大成网站www永久一区 | 亚洲女人被黑人巨大进入| 久久噜噜噜久久亚洲va久| 亚洲中文字幕日本无线码| 色婷婷综合缴情综免费观看|