簡體   English   中英

C#IHttpHandler和將httpclient用於Web服務

[英]C# IHttpHandler and the use of httpclient for a web service

我的任務是維護一個C#Web服務,該服務大致可以歸結為與IIS 8結合使用的以下代碼:

public class Handler1 : IHttpHandler
{
    public void ProcessRequest(HttpContext context) 
    {
    // here we process the request and return it after fetching some data
    }
}

現在,我需要創建另一個Web服務,該Web服務又將請求另一個Web服務來獲取數據。 我所收集的是C#中的HttpClient()是新的亮點,但是要找到在我的情況下如何實現它的方法並不那么容易(或者如果這是一個好的解決方案)。

我的解決方案是這樣的

public class Handler1 : IHttpHandler
{
    public void ProcessRequest(HttpContext context) 
    {
    // make sure that the incoming request is valid
    HttpClient client = new HttpClient();
    // do stuff with ^client and request the other service
    [..]
    // return the data from the response from ^client to the first request
    }
}

我是在正確的軌道上還是這將是一場災難? 我很樂意接受有關文檔的提示或指示。

更好的解決方案示例:

public class YourApp
{
    private static HttpClient Client = new HttpClient();
    public static void Main(string[] args)
    {
        Console.WriteLine("Requests about to start!");
        for(int i = 0; i < 5; i++)
        {
            var result = Client.GetAsync("http://www.stackoverflow.com").Result;
            Console.WriteLine(result);
        }
        Console.WriteLine("Requests are finished!");
        Console.ReadLine();
    }
}

您的方法有兩個問題。

首先,就像其他人所說的那樣,一定不能為每個請求創建HttpClient,因為這將導致您的服務器為每個請求創建一個新的TCP連接, 並且直到它們達到其MaxIdleTimeout時才將其從連接池中釋放。

這是因為每個HttpClient實例(或更確切地說,每個HttpClientHandler實例)在ConnectionPool中創建一個唯一的ConnectionGroup,所以兩個HttpClient(以這種方式創建)將不會共享相同的連接。

您可以很容易地耗盡端口(因為連接保持活動了一段時間),即使沒有此端口,在停用keepalive(通常在HTTPs服務器上進行連接)時,也會遇到所有性能問題。

對於解決方案,您可以在處理程序中添加一個私有的只讀HttpClient字段,該字段將按照在代碼中完成的操作進行初始化(我相信HttpHandlers是由IIS實例化的,而不是按請求實例化的,只要它表明它是可重用的即可)。 (編輯:請注意,HttpClient實例是線程安全的)。

您會發現的另一個問題是IHttpHandler在設計上是同步的。 這意味着您必須返回task.Result在您的代碼中。 這將阻塞當前線程(我們稱其為A),出站請求的所有后續處理(發送請求的主體,讀取響應的標頭和讀取響應的主體)將使用來自線程池來處理此操作(我們將它們稱為B,C和D,盡管它們都可以是同一線程,因為時間上沒有步驟重疊)。

但是在Asp.net中,您不能安全地做到這一點。 當您的主線程A處理Asp.net入站請求時,簡單地說,它將鎖定當前上下文。 當生成線程B,C和D來處理出站請求時,它們也將嘗試獲取並鎖定當前上下文。 但是由於您的主線程A被阻塞,因此上下文尚未釋放。 這將有效導致死鎖(A鎖定上下文並等待B / C / D,B / C / D等待上下文)。

要解決此問題,我建議您改用IHttpAsyncHandler 異步過程不能被同步使用,並且HttpClient只能以異步方式使用。

但是,這對於首次使用場景可能會更具挑戰性。 從WebApi服務器的上下文中使用HttpClient會容易得多。 但是,在不知道為什么必須使用HttpHandler的情況下,我不知道這是否在可接受的范圍之內。

還有其他方法可以運行異步任務並同步等待它們(例如臨時更改CurrentContext),但是它們都非常危險。

附帶說明一下,HttpClient是新的亮點,並且可以與其他新亮點(例如“全程異步基礎結構”)一起很好地工作。 但是,嘗試在較舊的基礎結構中使用HttpClient並不容易。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM