最近都在寫 秋色園技術原理解析 文章,今天就寫一篇散文,簡述一下伺服器記憶體太小引發的命案。
以前寫文都排版,這篇就當散文了...寫完就這樣了,當然加黑加紅還是給加了。
首先,我先上2張秋色園伺服器當前進程及記憶體的圖片:
1:進程
2:實體記憶體剩餘
看完這兩張圖片,啥感覺?記憶體窮緊張!!!!
窮緊張不打緊,打緊的是比緊張還緊張的情況發生了,什麼情況?
出事故了,應用程式集區要產生回收動作了!!!!
先看一下應用程式集區什麼情況會產生回收動作?
1:IIS應用程式集區裡的“回收”裡的配置就不說了,這些是你自己定義的。
2:你手動執行“回收”,以重啟應用程式集區。
3:你升級dll到伺服器中,新升的升級會引發應用程式集區重啟。
4:web應用程式產生“錯誤”,進程終止,引發應用程式集區重啟。
5:臨時想不出來......
出事了,出事了,出啥事了?
還不是記憶體窮緊張那點破事,為了示範一下什麼事,我決定回收一下應用程式集區給大夥!!!
這裡本機樣本回收了,大夥知道咋回事就可以了,哈哈:
看到了吧,兩個進程,這是什麼情況?
IIS啟用了新的進程來接收新的請求,同時舊的進程請求會保留繼續處理之前的請求隊列,直到處理完所有之前的請求才結束。
大體就是這麼一回事了,問題就產生在這一瞬間:
本來就沒記憶體了,舊的進程不回收,新的進程又出來,一出來就喊著要記憶體,可是系統又給不了記憶體,於是就卡在那裡,還造成CPU百分百的情況。
就在這個小間間,網站訪問就卡住了,打不開了,給人一種速度超慢的感覺。
什麼時候你感覺開啟了,估計就是舊的進程光榮退休了。
好了,升級時候的情況並不多,應用程式集區也設定了半夜才回收一次,理論上回收也不多,這種小瞬間產生的機率並不多。
可是網站不穩定的情況才出現的挺頻繁,似乎超出我設定的時候和升級的頻率。
就在這些天,我發現我基礎有點差:
web應用程式產生“錯誤”,進程終止,引發應用程式集區重啟。
以前都沒怎麼注意,現在發現了,代碼寫的不好,異常不處理好,應用程式集區就會經常性重啟,也是引發你網站慢的一個原因。
給大夥截一張圖:
大夥到自己伺服器上看這事件,如果看到一堆錯誤及警告,說明你和我一樣基礎差。
這些日誌是怎麼產生的?
其實就是系統未被捕獲的異常的,然後最終一路過五關,最後就跑這來了,跑到這來,基本上你的應用程式集區就變的很不穩定的說。
下面就隨意扯扯異常這事情
首先一點就是:
在.NET 2.0中,主線程或線程的錯誤,都會導致進程的中止,引發應用程式集區回收。
1.1版本的時候,線程的錯誤是不會引發主進程中止的。
PS:還記得我上篇文章“秋色園QBlog技術原理解析:效能最佳化篇:使用者和文章計數器方案(十七)”說到的內建線程吧,
其實隱藏說的就是這問題,線程的存取違規,經常性的引發了主進程中止,導致應用程式集區重啟。
再說一點的就:
先把日誌上的警告和異常給處理了。
最後一點的就是:
全域捕獲未處理的異常,然後作掉它,
不讓它跑到這來危害應用程式集區重啟
。[補充:作掉它並不能避免應用程式集區重啟]
基礎不好,很多天了,才偶然發現這麼點代碼:
一:AppDomain.CurrentDomain.UnhandledException 事件
public Window1() {
InitializeComponent();
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
}
void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
throw new NotImplementedException();
}
PS:在web中發現這傢伙似乎不起作用,沒深入糾結它,而且它阻止不了異常往上報,只能是收集資訊用。
二:HttpApplication.Error事件
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(context_BeginRequest);
context.Error += new EventHandler(context_Error);
}
void context_Error(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
Log.WriteLogToTxt(app.Server.GetLastError());
app.Server.ClearError();//把錯誤消滅了,不讓它往上拋
}
這裡其實要說的就是app.Server.ClearError(),為了發現這一行代碼,我糾結了好多個小時,最後很偶然才發現它,[雖然發現了它,但是作用似乎不大]。
補充:
在樓下網友:長河落魄 的疑問聲中,我測試了一下,得到以下結果:
1:主線程中產生的“錯誤”,只要不是致命的,系統日誌中僅是“警告”層級,它不會引發應用程式重啟。
2:內建線程中產生的“錯誤”,系統中產生的“錯誤”層級,它會中止進程,而且,上面的全域語句並不能捕獲到異常。
當然,這裡還有幾個疑惑:
1:應用程式集區是不是只遇到“錯誤”層級的,才會引發終止,重啟?
2:主線程中一般的錯誤都是“警告”層級,那有沒有可能會產生“錯誤”層級的錯誤呢?如果產生了,是不是一樣可攔截?這上面的清除異常的代碼,是不是就有效了?
3:多線程中的異常,沒有全域捕獲的事件了?如果有,你在哪呢?
好了,現在基本上錯誤都被記錄,一步一步對著日誌一個一個消滅了,現在基本上應用程式集區很穩定不亂重啟了,安穩了許多。
其實總結還是一句:記憶體太小,傷不起啊!