有許多種原因可能導致Web站點(diǎn)無(wú)法正常工作,這使得系統地檢查所有問(wèn)題變得很困難。下面將集中分析總結導致Web站點(diǎn)崩潰的最常見(jiàn)的問(wèn)題。如果可以解決這些常規問(wèn)題,那么也將有能力對付出現的一些意外情況。
磁盤(pán)已滿(mǎn)
導致系統無(wú)法正常運行的最可能的原因是磁盤(pán)已滿(mǎn)。一個(gè)好的網(wǎng)絡(luò )管理員會(huì )密切關(guān)注磁盤(pán)的使用情況,隔一定的時(shí)間,就需要將磁盤(pán)上的一些負載轉存到備份存儲介質(zhì)中(例如磁帶)。
日志文件會(huì )很快用光所有的磁盤(pán)空間。Web服務(wù)器的日志文件、SQL*Net的日志文件、JDBC日志文件,以及應用程序服務(wù)器日志文件均與內存泄漏有同等的危害??梢圆扇〈胧⑷罩疚募4嬖谂c操作系統不同的文件系統中。日志文件系統空間已滿(mǎn)時(shí)Web服務(wù)器也會(huì )被掛起,但機器自身被掛起的幾率已大大減低。
C指針錯誤
用C或C++編寫(xiě)的程序,如Web服務(wù)器API模塊,有可能導致系統的崩潰,因為只要間接引用指針(即,訪(fǎng)問(wèn)指向的內存)中出現一個(gè)錯誤,就會(huì )導致操作系統終止所有程序。另外,使用了糟糕的C指針的Java模擬量(analog)將訪(fǎng)問(wèn)一個(gè)空的對象引用。Java中的空引用通常不會(huì )導致立刻退出JVM,但是前提是程序員能夠使用異常處理方法恰當地處理錯誤。在這方面,Java無(wú)需過(guò)多的關(guān)注,但使用Java對可靠性進(jìn)行額外的度量則會(huì )對性能產(chǎn)生一些負面影響。
內存泄漏
C/C++程序還可能產(chǎn)生另一個(gè)指針問(wèn)題:丟失對已分配內存的引用。當內存是在子程序中被分配時(shí),通常會(huì )出現這種問(wèn)題,其結果是程序從子程序中返回時(shí)不會(huì )釋放內存。如此一來(lái),對已分配的內存的引用就會(huì )丟失,只要操作系統還在運行中,則進(jìn)程就會(huì )一直使用該內存。這樣的結果是,曾占用更多的內存的程序會(huì )降低系統性能,直到機器完全停止工作,才會(huì )完全清空內存。
解決方案之一是使用代碼分析工具(如Purify)對代碼進(jìn)行仔細分析,以找出可能出現的泄漏問(wèn)題。但這種方法無(wú)法找到由其他原因引起的庫中的泄漏,因為庫的源代碼是不可用的。另一種方法是每隔一段時(shí)間,就清除并重啟進(jìn)程。Apache的Web服務(wù)器就會(huì )因這個(gè)原因創(chuàng )建和清除子進(jìn)程。
雖然Java本身并無(wú)指針,但總的說(shuō)來(lái),與C程序相比,Java程序使用內存的情況更加糟糕。在Java中,對象被頻繁創(chuàng )建,而直到所有到對象的引用都消失時(shí),垃圾回收程序才會(huì )釋放內存。即使運行了垃圾回收程序,也只會(huì )將內存還給虛擬機VM,而不是還給操作系統。結果是:Java程序會(huì )用光給它們的所有堆,從不釋放。由于要保存實(shí)時(shí)(Just In Time,JIT)編譯器產(chǎn)生的代碼,Java程序的大小有時(shí)可能會(huì )膨脹為最大堆的數倍之巨。
還有一個(gè)問(wèn)題,情況與此類(lèi)似。從連接池分配一個(gè)數據庫連接,而無(wú)法將已分配的連接還回給連接池。一些連接池有活動(dòng)計時(shí)器,在維持一段時(shí)間的靜止狀態(tài)之后,計時(shí)器會(huì )釋放掉數據庫連接,但這不足以緩解糟糕的代碼快速泄漏數據庫連接所造成的資源浪費。
進(jìn)程缺乏文件描述符
如果已為一臺Web服務(wù)器或其他關(guān)鍵進(jìn)程分配了文件描述符,但它卻需要更多的文件描述符,則服務(wù)器或進(jìn)程會(huì )被掛起或報錯,直至得到了所需的文件描述符為止。文件描述符用來(lái)保持對開(kāi)放文件和開(kāi)放套接字的跟蹤記錄,開(kāi)放文件和開(kāi)放套接字是Web服務(wù)器很關(guān)鍵的組成部分,其任務(wù)是將文件復制到網(wǎng)絡(luò )連接。默認時(shí),大多數shell有64個(gè)文件描述符,這意味著(zhù)每個(gè)從shell啟動(dòng)的進(jìn)程可以同時(shí)打開(kāi)64個(gè)文件和網(wǎng)絡(luò )連接。大多數shell都有一個(gè)內嵌的ulimit命令可以增加文件描述符的數目。
線(xiàn)程死鎖
由多線(xiàn)程帶來(lái)的性能改善是以可靠性為代價(jià)的,主要是因為這樣有可能產(chǎn)生線(xiàn)程死鎖。線(xiàn)程死鎖時(shí),第一個(gè)線(xiàn)程等待第二個(gè)線(xiàn)程釋放資源,而同時(shí)第二個(gè)線(xiàn)程又在等待第一個(gè)線(xiàn)程釋放資源。我們來(lái)想像這樣一種情形:在人行道上兩個(gè)人迎面相遇,為了給對方讓道,兩人同時(shí)向一側邁出一步,雙方無(wú)法通過(guò),又同時(shí)向另一側邁出一步,這樣還是無(wú)法通過(guò)。雙方都以同樣的邁步方式堵住了對方的去路。假設這種情況一直持續下去,這樣就不難理解為何會(huì )發(fā)生死鎖現象了。
解決死鎖沒(méi)有簡(jiǎn)單的方法,這是因為使線(xiàn)程產(chǎn)生這種問(wèn)題是很具體的情況,而且往往有很高的負載。大多數軟件測試產(chǎn)生不了足夠多的負載,所以不可能暴露所有的線(xiàn)程錯誤。在每一種使用線(xiàn)程的語(yǔ)言中都存在線(xiàn)程死鎖問(wèn)題。由于使用Java進(jìn)行線(xiàn)程編程比使用C容易,所以Java程序員中使用線(xiàn)程的人數更多,線(xiàn)程死鎖也就越來(lái)越普遍了??梢栽贘ava代碼中增加同步關(guān)鍵字的使用,這樣可以減少死鎖,但這樣做也會(huì )影響性能。如果負載過(guò)重,數據庫內部也有可能發(fā)生死鎖。
如果程序使用了永久鎖,比如鎖文件,而且程序結束時(shí)沒(méi)有解除鎖狀態(tài),則其他進(jìn)程可能無(wú)法使用這種類(lèi)型的鎖,既不能上鎖,也不能解除鎖。這會(huì )進(jìn)一步導致系統不能正常工作。這時(shí)必須手動(dòng)地解鎖。
服務(wù)器超載
Netscape Web服務(wù)器的每個(gè)連接都使用一個(gè)線(xiàn)程。Netscape Enterprise Web服務(wù)器會(huì )在線(xiàn)程用完后掛起,而不為已存在的連接提供任何服務(wù)。如果有一種負載分布機制可以檢測到服務(wù)器沒(méi)有響應,則該服務(wù)器上的負載就可以分布到其它的Web服務(wù)器上,這可能會(huì )致使這些服務(wù)器一個(gè)接一個(gè)地用光所有的線(xiàn)程。這樣一來(lái),整個(gè)服務(wù)器組都會(huì )被掛起。操作系統級別可能還在不斷地接收新的連接,而應用程序(Web服務(wù)器)卻無(wú)法為這些連接提供服務(wù)。用戶(hù)可以在瀏覽器狀態(tài)行上看到connected(已連接)的提示消息,但這以后什么也不會(huì )發(fā)生。
解決問(wèn)題的一種方法是將obj.conf參數RqThrottle的值設置為線(xiàn)程數目之下的某個(gè)數值,這樣如果越過(guò)RqThrottle的值,就不會(huì )接收新的連接。那些不能連接的服務(wù)器將會(huì )停止工作,而連接上的服務(wù)器的響應速度則會(huì )變慢,但至少已連接的服務(wù)器不會(huì )被掛起。這時(shí),文件描述符至少應當被設置為與線(xiàn)程的數目相同的數值,否則,文件描述符將成為一個(gè)瓶頸。
數據庫中的臨時(shí)表不夠用
許多數據庫的臨時(shí)表(cursor)數目都是固定的,臨時(shí)表即保留查詢(xún)結果的內存區域。在臨時(shí)表中的數據都被讀取后,臨時(shí)表便會(huì )被釋放,但大量同時(shí)進(jìn)行的查詢(xún)可能耗盡數目固定的所有臨時(shí)表。這時(shí),其他的查詢(xún)就需要列隊等候,直到有臨時(shí)表被釋放時(shí)才能再繼續運行。
這是一個(gè)不容易被程序員發(fā)覺(jué)的問(wèn)題,但會(huì )在負載測試時(shí)顯露出來(lái)。但可能對于數據庫管理員(DataBase Administrator,DBA)來(lái)說(shuō),這個(gè)問(wèn)題十分明顯。
此外,還存在一些其他問(wèn)題:設置的表空間不夠用、序號限制太低,這些都會(huì )導致表溢出錯誤。這些問(wèn)題表明了一個(gè)好的DBA對用于生產(chǎn)的數據庫設置和性能進(jìn)行定期檢查的重要性。而且,大多數數據庫廠(chǎng)商也提供了監控和建模工具以幫助解決這些問(wèn)題。
另外,還有許多因素也極有可能導致Web站點(diǎn)無(wú)法工作。如:相關(guān)性、子網(wǎng)流量超載、糟糕的設備驅動(dòng)程序、硬件故障、包括錯誤文件的通配符、無(wú)意間鎖住了關(guān)鍵的表。