充分發揮異步在 ASP.NET 中的強大優點

做者:Brij Bhushan Mishrahtml

最近幾年,異步編程受到極大關注,主要是出於兩個關鍵緣由:首先,它有助於提供更好的用戶體驗,由於不會阻塞 UI 線程,避免了處理結束前出現 UI 界面掛起。其次,它有助於大幅擴展系統,並且無需添加額外硬件。web

可是,編寫合適的異步代碼來管理線程自己是項乏味的工做。雖然如此,其巨大好處讓許多新舊技術紛紛開始使用異步編程。微軟自發布了 .NET 4.0之後也對其投入頗多,隨後在 .NET 4.5中引入了 async 和 await 關鍵字,使異步編程變得史無前例地簡單。數據庫

可是,ASP.NET 中的異步功能自一開始就可使用,只是歷來沒有獲得應有的重視。並且,考慮到 ASP.NET 和 IIS 處理請求的方式,異步體現的優點可能更明顯。經過異步,咱們很容易就能夠大幅提升 ASP.NET 應用程序的擴展性。隨着新的編程結構引入,如 async 和 await 關鍵字,咱們也應該學會使用異步編程的強大功能。編程

在本篇博文中,咱們將討論一下 IIS 和 ASP.NET 處理請求的方式,而後看看 ASP.NET 中哪些地方可使用異步,最後再討論幾個最能體現異步優點的場景。瀏覽器

##請求是如何處理的?服務器

每一個 ASP.NET 請求都要先經過 IIS,而後再由 ASP.NET 處理程序進行最終處理。 首先IIS 接收請求,初步處理後,發送給ASP.NET(必須是一個ASP.NET請求),而後由ASP.NET進行實際處理並生成響應,以後該響應經過IIS發回給客戶。在IIS上,有一些工做進程負責從隊列中取出請求,並執行IIS 模塊,而後再將該請求發送到ASP.NET 隊列。可是,ASP.NET自己不建立任何線程,也沒有處理請求的線程池,而是經過使用CLR 線程池,從中獲取線程來處理請求。所以,IIS 模塊調用ThreadPool.QueueUserWorkItem,將請求排入隊列,供CLR 工做線程處理。咱們都知道,CLR線程池是由CLR管理,而且可以自動調整(也就是說,它根據須要建立和銷燬進程)。這裏還要記住,建立和銷燬線程是項很繁重的任務,這就是爲何CLR線程池容許使用同一個線程處理多個任務。下面來看一個描述請求處理過程的圖示。網絡

在上圖中能夠看到,請求首先由 HTTP.sys接收,並添加到相應內核級應用程序池隊列。而後,一個IIS工做線程從隊列中取出請求,處理後將其傳到ASP.NET 隊列。注意,該請求若是不是一個ASP.NET請求,將從 IIS 自動返回。最後,從CLR線程池中分配一個線程,負責處理該請求。異步

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

全部請求大體能夠分爲兩類:async

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

CPU Bound 類請求,須要 CPU 時間,並且是在同一進程中執行;而 I/O Bound 類請求,自己具備阻塞性,須要依賴其餘模塊執行 I/O 操做並返回響應。阻塞性請求是提升應用程序可伸縮性的主要障礙,並且大多數web應用程序中,在等待 I/O 操做的過程當中浪費了大量時間。 所以如下場景適合使用異步:

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

    a. 數據庫訪問

    b. 讀/寫文件

    c. Web 服務調用

    d. 訪問網絡資源

  2. 事件驅動的請求,好比SignalR

  3. 須要從多個數據源獲取數據的場景

做爲示例,這裏建立一個簡單的同步頁面,而後再將它轉換成異步頁面。 本示例設置了1000ms的延遲(以模擬一些繁重的數據庫或web服務調用等),並且還使用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;

    }

如今將該頁面轉換成異步頁面,這裏主要涉及三個步驟:

  1. 在頁面指令中添加Async = true,將該頁面轉換成異步頁面,以下所示:

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

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

2.將此方法轉換成異步方法。在這裏把Thread.Sleep 與 client.DownloadString 轉換成異步方法以下所示:

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.如今能夠直接在 Page_Load (頁面加載)上調用此方法,使其異步,以下所示:

protected async void Page_Load(object sender, EventArgs e)

    {
        await AsyncWork();
    }

可是這裏的 Page_Load 返回的類型是async void,這種狀況不管如何都應該避免。咱們知道,Page_Load 是整個頁面生命週期的一部分,若是咱們把它設置成異步,可能會出現一些異常狀況和事件,好比生命週期已經執行完畢而頁面加載仍在運行。 所以,強烈建議你們使用 RegisterAsyncTask 方法註冊異步任務,這些異步任務會在生命週期的恰當時間執行,能夠避免出現任何問題。

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

如今,頁面已經轉換成了異步頁,它就再也不是一個阻塞性請求。

筆者在 IIS8.5 上部署了同步頁面和異步頁面,並使用突發負載對二者進行了測試。測試結果發現,相同的機器配置,同步頁面在2-3秒內只能提取1000個請求,而異步頁面可以爲2200多個請求提供服務。此後,開始收到超時(Timeout)或服務器不可用(Server Not Available)的錯誤。雖然二者的平均請求處理時間沒有多大差異,可是經過異步頁面,能夠處理兩倍以上的請求。這足以證實異步編程功能強大,因此應該充分利用它的優點。

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

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

##結論 本篇博文中,咱們討論了異步編程,並且發現,新推出的async 和 await關鍵字,使異步編程變得十分簡單。咱們討論的話題包括 IIS和ASP.NET如何處理請求,以及在哪些場景中異步的做用最明顯。另外,咱們還建立了一個簡單示例,討論了異步頁面的優點。最後咱們還補充了幾個ASP.NET中可使用異步的地方。

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

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

本文轉自 OneAPM 官方博客

相關文章
相關標籤/搜索