充分發(fā)揮異步在 ASP.NET 中的強大優(yōu)勢

作者:Brij Bhushan Mishra

最近幾年,異步編程受到極大關(guān)注,主要是出于兩個關(guān)鍵原因:首先,它有助于提供更好的用戶體驗,因為不會阻塞 UI 線程,避免了處理結(jié)束前出現(xiàn) UI 界面掛起。其次,它有助于大幅擴展系統(tǒng),而且無需添加額外硬件。

但是,編寫合適的異步代碼來管理線程本身是項乏味的工作。雖然如此,其巨大好處讓許多新舊技術(shù)紛紛開始使用異步編程。微軟自發(fā)布了 .NET 4.0以后也對其投入頗多,隨后在 .NET 4.5中引入了 async 和 await 關(guān)鍵字,使異步編程變得前所未有地簡單。

但是,ASP.NET 中的異步功能自一開始就可以使用,只是從來沒有得到應(yīng)有的重視。而且,考慮到 ASP.NET 和 IIS 處理請求的方式,異步體現(xiàn)的優(yōu)勢可能更明顯。通過異步,我們很容易就可以大幅提高 ASP.NET 應(yīng)用程序的擴展性。隨著新的編程結(jié)構(gòu)引入,如 async 和 await 關(guān)鍵字,我們也應(yīng)該學(xué)會使用異步編程的強大功能。

在本篇博文中,我們將討論一下 IIS 和 ASP.NET 處理請求的方式,然后看看 ASP.NET 中哪些地方可以使用異步,最后再討論幾個最能體現(xiàn)異步優(yōu)勢的場景。

請求是如何處理的?

每個 ASP.NET 請求都要先通過 IIS,然后再由 ASP.NET 處理程序進(jìn)行最終處理。 首先IIS 接收請求,初步處理后,發(fā)送給ASP.NET(必須是一個ASP.NET請求),然后由ASP.NET進(jìn)行實際處理并生成響應(yīng),之后該響應(yīng)通過IIS發(fā)回給客戶。在IIS上,有一些工作進(jìn)程負(fù)責(zé)從隊列中取出請求,并執(zhí)行IIS 模塊,然后再將該請求發(fā)送到ASP.NET 隊列。但是,ASP.NET本身不創(chuàng)建任何線程,也沒有處理請求的線程池,而是通過使用CLR 線程池,從中獲取線程來處理請求。因此,IIS 模塊調(diào)用ThreadPool.QueueUserWorkItem,將請求排入隊列,供CLR 工作線程處理。我們都知道,CLR線程池是由CLR管理,并且能夠自動調(diào)整(也就是說,它根據(jù)需要創(chuàng)建和銷毀進(jìn)程)。這里還要記住,創(chuàng)建和銷毀線程是項很繁重的任務(wù),這就是為什么CLR線程池允許使用同一個線程處理多個任務(wù)。下面來看一個描述請求處理過程的圖示。

在上圖中可以看到,請求首先由 HTTP.sys接收,并添加到相應(yīng)內(nèi)核級應(yīng)用程序池隊列。然后,一個IIS工作線程從隊列中取出請求,處理后將其傳到ASP.NET 隊列。注意,該請求如果不是一個ASP.NET請求,將從 IIS 自動返回。最后,從CLR線程池中分配一個線程,負(fù)責(zé)處理該請求。

ASP.NET中異步的使用場景是?

所有請求大致可以分為兩類:

  1. CPU Bound 類
  2. I/O Bound 類

CPU Bound 類請求,需要 CPU 時間,而且是在同一進(jìn)程中執(zhí)行;而 I/O Bound 類請求,本身具有阻塞性,需要依賴其他模塊執(zhí)行 I/O 操作并返回響應(yīng)。阻塞性請求是提高應(yīng)用程序可伸縮性的主要障礙,而且大多數(shù)web應(yīng)用程序中,在等待 I/O 操作的過程中浪費了大量時間。 因此以下場景適合使用異步:

  1. I/O Bound 類請求,包括:

    a. 數(shù)據(jù)庫訪問

    b. 讀/寫文件

    c. Web 服務(wù)調(diào)用

    d. 訪問網(wǎng)絡(luò)資源

  2. 事件驅(qū)動的請求,比如SignalR

  3. 需要從多個數(shù)據(jù)源獲取數(shù)據(jù)的場景

作為示例,這里創(chuàng)建一個簡單的同步頁面,然后再將它轉(zhuǎn)換成異步頁面。 本示例設(shè)置了1000ms的延遲(以模擬一些繁重的數(shù)據(jù)庫或web服務(wù)調(diào)用等),而且還使用WebClient下載了一個頁面,如下所示:

    protected void Page_Load(object sender, EventArgs e)

    {

        System.Threading.Thread.Sleep(1000);

        WebClient client = new WebClient();

        string downloadedContent = client.DownloadString("https://msdn.microsoft.com/en-us/library/hh873175%28v=vs.110%29.aspx");

        dvcontainer.InnerHtml = downloadedContent;

    }

現(xiàn)在將該頁面轉(zhuǎn)換成異步頁面,這里主要涉及三個步驟:

  1. 在頁面指令中添加Async = true,將該頁面轉(zhuǎn)換成異步頁面,如下所示:

     <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Home.aspx.cs" Inherits="AsyncTest.Home" Async="true" AsyncTimeout="3000" %>
    

這里還添加了 AsyncTimeout (可選項),請根據(jù)需求選擇。

2.將此方法轉(zhuǎn)換成異步方法。在這里把Thread.Sleep 與 client.DownloadString 轉(zhuǎn)換成異步方法如下所示:

    private async Task AsyncWork()

    {

        await Task.Delay(1000);

        WebClient client = new WebClient();

        string downloadedContent = await client.DownloadStringTaskAsync("https://msdn.microsoft.com/en-us/library/hh873175%28v=vs.110%29.aspx ");

        dvcontainer.InnerHtml = downloadedContent;
 
    }

3.現(xiàn)在可以直接在 Page_Load (頁面加載)上調(diào)用此方法,使其異步,如下所示:

    protected async void Page_Load(object sender, EventArgs e)

    {
        await AsyncWork();
    }

但是這里的 Page_Load 返回的類型是async void,這種情況無論如何都應(yīng)該避免。我們知道,Page_Load 是整個頁面生命周期的一部分,如果我們把它設(shè)置成異步,可能會出現(xiàn)一些異常情況和事件,比如生命周期已經(jīng)執(zhí)行完畢而頁面加載仍在運行。 因此,強烈建議大家使用 RegisterAsyncTask 方法注冊異步任務(wù),這些異步任務(wù)會在生命周期的恰當(dāng)時間執(zhí)行,可以避免出現(xiàn)任何問題。

    protected void Page_Load(object sender, EventArgs e)
    {
        RegisterAsyncTask(new PageAsyncTask(AsyncWork));
    } 

現(xiàn)在,頁面已經(jīng)轉(zhuǎn)換成了異步頁,它就不再是一個阻塞性請求。

筆者在 IIS8.5 上部署了同步頁面和異步頁面,并使用突發(fā)負(fù)載對兩者進(jìn)行了測試。測試結(jié)果發(fā)現(xiàn),相同的機器配置,同步頁面在2-3秒內(nèi)只能提取1000個請求,而異步頁面能夠為2200多個請求提供服務(wù)。此后,開始收到超時(Timeout)或服務(wù)器不可用(Server Not Available)的錯誤。雖然兩者的平均請求處理時間沒有多大差別,但是通過異步頁面,可以處理兩倍以上的請求。這足以證明異步編程功能強大,所以應(yīng)該充分利用它的優(yōu)勢。

ASP.NET中還有幾個地方也可以引入異步:

  1. 編寫異步模塊
  2. 使用IHttpAsyncHandler 或 HttpTaskAsyncHandler 編寫異步HTTP處理程序
  3. 使用web sockets 或 SignalR

結(jié)論

本篇博文中,我們討論了異步編程,而且發(fā)現(xiàn),新推出的async 和 await關(guān)鍵字,使異步編程變得十分簡單。我們討論的話題包括 IIS和ASP.NET如何處理請求,以及在哪些場景中異步的作用最明顯。另外,我們還創(chuàng)建了一個簡單示例,討論了異步頁面的優(yōu)勢。最后我們還補充了幾個ASP.NET中可以使用異步的地方。

本文系 OneAPM 工程師編譯呈現(xiàn)。OneAPM 能助您輕松鎖定 .NET 應(yīng)用性能瓶頸,通過強大的 Trace 記錄逐層分析,直至鎖定行級問題代碼。以用戶角度展示系統(tǒng)響應(yīng)速度,以地域和瀏覽器維度統(tǒng)計用戶使用情況。想閱讀更多技術(shù)文章,請訪問 OneAPM 官方博客

原文地址:http://www.infragistics.com/community/blogs/brijmishra/archive/2015/10/28/leveraging-the-power-of-asynchrony-in-asp-net.aspx

本文轉(zhuǎn)自 OneAPM 官方博客

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容