在asp.net項目中,添加一個【一般處理程式】來處理請求是很自然的事,這樣會得到一個實現自IHttpHandler的類,然後只需在ProcessRequest方法中寫上處理邏輯就行了。但是這樣的一個請求處理程式(下稱ashx)是同步的,就是接待該次請求的線程會一直等待處理完才能解脫,後果就是,如果 ...
在asp.net項目中,添加一個【一般處理程式】來處理請求是很自然的事,這樣會得到一個實現自IHttpHandler的類,然後只需在ProcessRequest方法中寫上處理邏輯就行了。但是這樣的一個請求處理程式(下稱ashx)是同步的,就是接待該次請求的線程會一直等待處理完才能解脫,後果就是,如果這個ashx比較耗時,並且同時對它的請求又多的話,伺服器需要開啟若幹個線程來跑這個ashx,並且這些線程都要各自跑很久才能被收回或挪作它用,如果這樣的ashx還有不少的話,那麼對整個伺服器資源的開銷是很大的,所以有必要採用IHttpAsyncHandler來實現這種ashx,即非同步請求處理程式,非同步化以後,線程把請求接進來就完事了,反手就可以去處理其它請求,然後由別的線程或硬體來處理具體的任務~取決於任務是CPU消耗型(密集運算,如圖片處理)還是I/O型(資料庫讀寫、網路訪問等),老實說如果耗時任務總是CPU消耗型,那同步非同步在資源消耗上沒什麼區別,因為總得有個線程來跑任務,換不換線程意義不大。但總的來說非同步化沒壞處,而且萬一對任務類型評估錯誤呢。
改用IHttpAsyncHandler後,多了兩個方法BeginProcessRequest和EndProcessRequest,原有的ProcessRequest事實上已經廢棄,請求不會進入裡面,而是改為在BeginProcessRequest中處理請求,原IsReusable屬性功能不變。說回BeginProcessRequest,這是一個典型的傳統非同步方法(相對於.net 4.5後的async/await新式非同步方法來說),邏輯相比原來的同步方法ProcessRequest有點繞,首先入參除了熟悉的HttpContext外還有兩個,然後還有個IAsyncResult類型的返回值。熟悉APM(非同步編程模型)套路的朋友知道該怎麼搞,不熟悉的可參看MSDN,要點就是實例化一個實現IAsyncResult的類,在其中非同步或起線程執行邏輯,然後返回這個對象。現成的實現IAsyncResult的類在.net 4.0後有Task,但如果項目不到4.0,你還找不到一個可以拿來就用的類,如果要為每個ashx實現一個IAsyncResult,想想都蛋疼,哪怕總共只需實現一個IAsyncResult我都不情願,好在委托這個東西編譯器會為它自動生成非同步模型,於是有了下麵這個簡單的封裝:
/// <summary> /// 非同步請求處理基類 /// <para>- 子類實現ProcessRequest方法併在其中處理請求</para> /// <para>- 預設允許實例重用(IsReusable=true),子類可重寫為false</para> /// </summary> public abstract class HttpAsyncHandler : IHttpAsyncHandler { readonly Action<HttpContext> _processRequestDel; protected HttpAsyncHandler() => _processRequestDel = ProcessRequest; /// <summary> /// 處理請求 /// </summary> //總是要有個讓子類處理業務邏輯的地方,既然原來的ProcessRequest已廢棄,不如廢物利用 public abstract void ProcessRequest(HttpContext context); /// <summary> /// 多次請求是否可以重用實例。預設true /// </summary> public virtual bool IsReusable => true; public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, object extraData) => _processRequestDel.BeginInvoke(context, cb, extraData); //利用ProcessRequest委托的非同步能力 //雖然不End也不會導致非同步還沒跑完就返迴響應(HttpApplication的實現似乎保證這一點),但非同步中拋出的異常會被忽略,所以需要End暴露問題 public void EndProcessRequest(IAsyncResult result) => _processRequestDel.EndInvoke(result); }
有了這個封裝好的基類,在寫新的ashx時就可以把IHttpHandler改為HttpAsyncHandler,完了把ProcessRequest方法標成override就行,老ashx也可以經過簡單修改非同步化。舉例:
public class FooHandler : HttpAsyncHandler // 替掉IHttpHandler { //加上override public override void ProcessRequest(HttpContext context) { //在這裡寫邏輯 context.Response.Write("OK"); } }
需要註意的是IsReusable在HttpAsyncHandler中已改為true,所以如果你的ashx明確需要false,請override該屬性,如:
public override bool IsReusable => false;
對於.net 4.5及以上版本,微軟已經寫好了個HttpTaskAsyncHandler,性質一樣,只不過形式上符合新式的async/await用法,總之目的都是讓開發者可以優雅的使用非同步ashx,不必繁瑣的從IHttpAsyncHandler開始。
EOF