MattPowell介紹瞭如何在服務器端使用異步Web方法,來創建高性能的 Microsoft ASP.NET Web 服務

摘要:Matt Powell 介紹瞭如何在服務器端使用異步 Web 方法,來創建高性能的 Microsoft ASP.NET Web 服務。
  簡介

  在九月份的第三篇專欄(英文)中,我談到了利用 Microsoft? .NET Framework 的客戶端功能通過 HTTP 異步調用 Web 服務的問題。這種調用 Web 服務的方法非常有用,使用時不必鎖定您的應用程序或產生過多後臺線程。現在我們瞭解一下在服務器端提供類似功能的異步 Web 方法。異步 Web 方法在編寫 ISAPI 擴展方面具有與 HSE_STATUS_PENDING 方法類似的高性能,但不需要爲管理自己的線程池編寫代碼,同時又具有以託管代碼方式運行的所有優點。   首先我們考慮一下常規的同步 Microsoft? ASP.NET Web 方法。當您從同步 Web 方法返回時,將發送對該方法的響應。如果需要較長的時間來完成請求,則處理請求的線程會一直被佔用,直到方法調用結束。不幸的是,多數較長的調用是由較長的數據庫查詢或對另一個 Web 服務的調用等事件引起的。例如,如果您調用數據庫,當前線程會一直等待調用完成。線程無事可做,只是等待,直至聽到查詢的返回。當線程等待完成對 TCP 套接字或後端 Web 服務的調用時,也會出現類似的問題。

  讓線程處於等待狀態很不好,特別是在服務器的運行壓力很大的情況下。等待中的線程不會進行任何有效工作,例如爲其他請求提供服務。我們需要找到一種方法,能夠在服務器上開始較長的後臺進程,同時又能將當前線程返回到 ASP.NET 進程池。然後,當較長的後臺進程完成時,我們調用一個回調函數,結束對請求的處理,並通過某種方式通知 ASP.NET 請求已完成。實際上,這種功能可由 ASP.NET 使用異步 Web 方法提供。

  異步 Web 方法的工作原理

  當您使用 Web 方法編寫典型的 ASP.NET Web 服務時,Microsoft? Visual Studio? .Net 只是編譯您的代碼以創建程序集;當收到對其 Web 方法的請求時,將調用該程序集。程序集本身並不知道關於 SOAP 的任何事情。因此,當您的應用程序首次啓動時,ASMX 處理程序必須反映您的程序集,以確定提供哪些 Web 方法。對於常規的同步請求,這些操作都很簡單:找出哪些方法具有關聯的 WebMethod 屬性、基於 SOAPAction HTTP 標頭來設置調用正確方法的邏輯。

  對於異步請求,在反映過程中,ASMX 處理程序尋找具有某種簽名並將簽名識別爲異步的 Web 方法。該處理程序將尋找符合以下規則的方法對:

  BeginXXX EndXXX Web 方法,其中 XXX 是任意字符串,表示要提供的方法的名稱。
  BeginXXX 函數返回一個 IAsyncResult 接口,並分別接受 AsyncCallback 和一個對象,作爲其最後兩個輸入參數。
  EndXXX 函數接受一個 IAsyncResult 接口,作爲其唯一的參數。

  兩個方法都必須使用 WebMethod 屬性進行標識。

  如果 ASMX 處理程序發現兩個方法符合上述所有條件,則將方法 XXX 作爲常規的 Web 方法在其 WSDL 中提供。該方法將接受在 BeginXXX 的簽名中的 AsyncCallback 參數之前定義的參數作爲輸入,並返回由 EndXXX 函數返回的內容。因此,如果某個 Web 方法具有如下同步聲明:<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

  [WebMethod]
  public string LengthyProcedureint milliseconds {...}


  則異步聲明將爲:


  [WebMethod]
  public IAsyncResult BeginLengthyProcedure
              int milliseconds,
              AsyncCallback cb,
              object s {...}

  [WebMethod]
  public string EndLengthyProcedureIAsyncResult call {...}


  每個方法的 WSDL 都是相同的。

  在 ASMX 處理程序反映程序集並檢測到某個異步 Web 方法後,它必須以不同於處理同步請求的方式處理對該方法的請求。它將調用 BeginXXX 方法,而不是某個簡單方法。它將傳入的請求還原序列化到要傳遞到函數的參數中(與處理同步請求時一樣);但是它還將指針傳遞到一個內部回調函數(作爲 BeginXXX 方法的額外 AsyncCallback 參數)。

  這種方法類似於 .NET Framework Web 服務客戶端應用程序的異步編程模式。如果客戶端支持異步 Web 服務調用,則可以爲客戶端計算機釋放佔用的線程;如果服務器端支持異步 Web 服務調用,則可以釋放服務器計算機上佔用的線程。但這裏有兩個關鍵的區別。首先,不是由服務器代碼調用 BeginXXX EndXXX 函數,而是由 ASMX 處理程序調用。其次,您要爲 BeginXXX EndXXX 函數編寫代碼,而不能使用由 WSDL.EXE Visual Studio .NET 中的 Add Web Reference(添加 Web 引用)嚮導生成的代碼。但結果是相同的,即釋放線程以使其能夠執行其他進程。

  ASMX 處理程序調用服務器的 BeginXXX 函數後,會將線程返回到進程線程池,使之能夠處理接收到的任何其他請求。但是,還不能釋放請求的 HttpContextASMX 處理程序將等待,直到它傳遞給 BeginXXX 函數的回調函數被調用,它才結束處理請求。

  一旦回調函數被調用,ASMX 處理程序將調用 EndXXX 函數,使您的 Web 方法可以完成任何所要執行的處理,並且可以得到被序列化到 SOAP 響應中的返回數據。EndXXX 函數返回後將發送響應,只有此時該請求的 HttpContext 纔得到釋放。

簡單的異步 Web 方法

  爲舉例說明異步 Web 方法,我從一個名爲 LengthyProcedure 的簡單同步 Web 方法開始,其代碼如下所示。然後我們再看一看如何異步完成相同的任務。LengthyProcedure 只佔用給定的毫秒數。

[WebService]
public class SyncWebService : System.Web.Services.WebService
{
  [WebMethod]
  public string LengthyProcedureint milliseconds
  {
    System.Threading.Thread.Sleepmilliseconds;
    return "成功";
  }
}


  現在我們將 LengthyProcedure 轉換爲異步 Web 方法。我們必須創建如前所述的 BeginLengthyProcedure 函數和 EndLengthyProcedure 函數。請記住,我們的 BeginLengthyProcedure 調用需要返回一個 IAsyncResult 接口。這裏,我打算使用一個委託以及該委託上的 BeginInvoke 方法,讓我們的 BeginLengthyProcedure 調用進行異步方法調用。傳遞到 BeginLengthyProcedure 的回調函數將被傳遞到委託上的 BeginInvoke 方法,從 BeginInvoke 返回的 IAsyncResult 將被 BeginLengthyProcedure 方法返回。

  當委託完成時,將調用 EndLengthyProcedure 方法。我們將調用委託上的 EndInvoke 方法,以傳入 IAsyncResult,並將其作爲 EndLengthyProcedure 調用的輸入。返回的字符串將是從該 Web 方法返回的字符串。下面是其代碼:


[WebService]
public class AsyncWebService : System.Web.Services.WebService
{
  public delegate string LengthyProcedureAsyncStub
    int milliseconds;

  public string LengthyProcedureint milliseconds
  {
    System.Threading.Thread.Sleepmilliseconds;
    return "成功";
  }

  public class MyState
  {
    public object previousState;
    public LengthyProcedureAsyncStub asyncStub;
  }

  [ System.Web.Services.WebMethod ]
  public IAsyncResult BeginLengthyProcedureint milliseconds,
    AsyncCallback cb, object s
  {
    LengthyProcedureAsyncStub stub
      = new LengthyProcedureAsyncStubLengthyProcedure;
    MyState ms = new MyState();
    ms.previousState = s;
    ms.asyncStub = stub;
    return stub.BeginInvokemilliseconds, cb, ms;
  }

  [ System.Web.Services.WebMethod ]
  public string EndLengthyProcedureIAsyncResult call
  {
    MyState ms = MyStatecall.AsyncState;
    return ms.asyncStub.EndInvokecall;
  }
}


  何時採用異步 Web 方法

  在確定是否適合在您的應用程序中採用異步 Web 方法時,有幾個問題需要考慮。首先,調用的 BeginXXX 函數必須返回一個 IAsyncResult 接口。IAsyncResult 是從多個異步 I/O 操作返回的,這些操作包括訪問數據流、進行 Microsoft? Windows? 套接字調用、執行文件 I/O、與其他硬件設備交互、調用異步方法,當然也包括調用其他 Web 服務。您可以從這些異步操作中得到 IAsyncResult,以便從 BeginXXX 函數返回它。您也可以創建自己的類以實現 IAsyncResult 接口,但隨後可能需要以某種方式包裝前面提到的某個 I/O 操作。

  對於前面提到的大多數異步操作,使用異步 Web 方法包裝後端異步調用很有意義,可以使 Web 服務代碼更有效。但使用委託進行異步方法調用時除外。委託會導致異步方法調用佔用進程線程池中的某個線程。不幸的是,ASMX 處理程序爲進入的請求提供服務時同樣要使用這些線程。所以與對硬件或網絡資源執行真正 I/O 操作的調用不同,使用委託的異步方法調用在執行時仍將佔用其中一個進程線程。您也可以佔用原來的線程,同步運行您的 Web 方法。

  下面的示例顯示了一個調用後端 Web 服務的異步 Web 方法。它已經使用 WebMethod 屬性標識了 BeginGetAge EndGetAge 方法,以便異步運行。此異步 Web 方法的代碼調用名爲 UserInfoQuery 的後端 Web 方法,以獲得它需要返回的信息。對 UserInfoQuery 的調用被異步執行,並被傳遞到 AsyncCallback 函數,後者被傳遞到 BeginGetAge 方法。這將導致當後端請求完成時,調用內部回調函數。然後,回調函數將調用 EndGetAge 方法以完成請求。此示例中的代碼比前面示例中的代碼簡單得多,並且還具有另外一個優點,即沒有在與爲中間層 Web 方法請求提供服務的相同線程池中啓動後端處理。


[WebService]
public class GetMyInfo : System.Web.Services.WebService
{
  [WebMethod]
  public IAsyncResult BeginGetAgeAsyncCallback cb, Object state
  {
    // 調用異步 Web 服務調用。
    localhost.UserInfoQuery proxy
      = new localhost.UserInfoQuery();
    return proxy.BeginGetUserInfo"用戶名",
                   cb,
                   proxy;
  }

  [WebMethod]
  public int EndGetAgeIAsyncResult res
  {
    localhost.UserInfoQuery proxy
      = localhost.UserInfoQueryres.AsyncState;
    int age = proxy.EndGetUserInfores.age;
    // 在此對 Web 服務的結果進行其他
    // 處理。
    return age;
  }
}


  發生在 Web 方法中的最常見的 I/O 操作類型之一是對 SQL 數據庫的調用。不幸的是,目前 Microsoft? ADO.NET 尚未定義一個完好的異步調用機制;而只是將 SQL 調用包裝到異步委託調用中對提高效率沒有什麼幫助。雖然有時可以選擇緩存結果,但是也應當考慮使用 Microsoft SQL Server 2000 Web Services Toolkit(英文)將您的數據庫發佈爲 Web 服務。這樣您就可以利用 .NET Framework 中的支持,異步調用 Web 服務以查詢或更新數據庫。

  通過 Web 服務調用訪問 SQL 時,需要注意衆多的後端資源。如果您使用了 TCP 套接字與 Unix 計算機通信,或者通過專用的數據庫驅動程序訪問其他一些可用的 SQL 平臺,甚至具有使用 DCOM 訪問的資源,您都可以考慮使用衆多的 Web 服務工具包將這些資源發佈爲 Web 服務。

  使用這種方法的優點之一是您可以利用客戶端 Web 服務結構的優勢,例如使用 .NET Framework 的異步 Web 服務調用。這樣您將免費獲得異步調用能力,而您的客戶端訪問機制會與異步 Web 方法高效率地配合工作。

  使用異步 Web 方法聚合數據

  現在,許多 Web 服務都訪問後端的多個資源併爲前端的 Web 服務聚合信息。儘管調用多個後端資源會增加異步 Web 方法模型的複雜性,但最終還是能夠顯著提高效率。
  假設您的 Web 方法調用兩個後端 Web 服務:服務 A 和服務 B。從您的 BeginXXX 函數,您可以異步調用服務 A 和服務 B。您應該向每個異步調用傳遞自己的回調函數。在從服務 A 和服務 B 接收到結果後,爲觸發 Web 方法的完成,您提供的回調函數將驗證所有的請求都已完成,在返回的數據上進行所有的處理,然後調用傳遞到 BeginXXX 函數的回調函數。這將觸發對 EndXXX 函數的調用,此函數的返回將導致異步 Web 方法的完成。

  小結

  異步 Web 方法在 ASP.NET Web 服務中提供了一個有效的機制,可以調用後端服務,而不會導致佔用卻不利用進程線程池中的寶貴線程。通過結合對後端資源的異步請求,服務器可以使用自己的 Web 方法使同時處理的請求數目達到最大。您應該考慮使用此方法開發高性能的 Web 服務應用程序。

http://msd2d.com/newsletter_tip.aspx?section=dotnet&id=887a74f8-e8dc-4b67-8072-bc45da89facb

 

  定義AsyncCallback 委託

private AsyncCallback CallbackHandler;

初始化必須在窗體的實例函數裏(最好構造函數)

CallbackHandler = new AsyncCallback(MyCallback);

(自己做的實例就是因爲在這樣定義

private AsyncCallback CallbackHandler= new AsyncCallback(MyCallback);而編譯通不過找了好長的原因

但是現在還是不明白引起這種問題的原理

二利用代理的beginInvoke方法調用代理並且綁定一個回調函數

TargetHandler.BeginInvoke( CallbackHandler, Nothing)

這裏代理的返回值會作爲參數傳送給AsyncCallback委託CallbackHandler

MyCallback的定義必須如下

 private void MyCallback(IAsyncResult Iar)

獲得 返回值得方法爲 =GetCustomersHandler.EndInvoke(Iar);

函數MyCallback處理需要做的事情

總之異步調用是通過FCL委託AsyncCallback來實現的

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章