向本地主机发出许多Web请求时,HttpClient会挂在GetAsync上

时间:2018-10-12 09:09:32

标签: asp.net asp.net-web-api2 authorization dotnet-httpclient

致电api/Values/Test?times={x}时遇到以下问题。当x为1、2,...时不会发生此问题,但是Web请求达到9、10等时将超时。 如您所见,Test端点将向EndPoint x次发出Web请求(无意义的方法体,它们在实践中将做更多有意义的事情):

public class ValuesController:  System.Web.Http.ApiController
{
    [HttpGet]
    public async Task<object> Test(int times)
    {
        var tasks = new List<Task<int>>();
        for (var i = 0; i < times; i++)
        {
            //Make web request to the `EndPoint` method below
            var task = new ApiRequestBuilder<int>("https://localhost:44301/api/Values/EndPoint")
                .GetAsync();
            tasks.Add(task);
        }
        return await Task.WhenAll(tasks.ToArray());
    } 

    [HttpGet]
    [CustomAuthorise]//This makes a web request to `AuthEndPoint` (below)
    public int EndPoint()
    {
        return 0;
    }

    [HttpGet]
    public bool AuthEndPoint()
    {
        return true;
    }
}

NB 参见以下ApiRequestBuilder是什么)

但是,问题不是EndPoint被打x次,而是CustomAuthorise属性,它发出了另一个Web请求,这就是问题所在:

public class CustomAuthoriseAttribute : System.Web.Http.AuthorizeAttribute
{
    public override async Task OnAuthorizationAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
    {
        var result = await this.AuthoriseAsync(actionContext);
        if (!result)
        {
            actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.Forbidden);
        }
    }

    public virtual async Task<bool> AuthoriseAsync(HttpActionContext context)
    {
        //***********HANGING HERE************
        //var result= await new ApiRequestBuilder<bool>("https://www.example.com")
        var result= await new ApiRequestBuilder<bool>("https://localhost:44301/Values/AuthEndPoint")
            .GetAsync();

        return result;
    }
}

如您所见,我已经注释了路线"https://www.example.com"。当我将网址设置为非localhost时,无论我想要多少垃圾邮件都会对该Web请求发出垃圾邮件(通过将x设置为大数)。

ApiRequestBuilder是通过System.Net.Http.HttpClient(版本4.0.0.0 、. NETframework v4.5.1)发出Web请求的助手,该请求将响应序列化为您提供的通用类型参数它。

public class ApiRequestBuilder<T> 
{
    protected string Url => Uri?.AbsoluteUri;
    protected Uri Uri;
    protected readonly HttpClient Client;

    public ApiRequestBuilder(string url) 
    {
        Uri = new Uri(url);
        var client = new HttpClient()
        {
            BaseAddress = this.Uri,
            //Set timeout so replicating this issue itn't a pain
            Timeout = TimeSpan.FromSeconds(5)
        };
        this.Client = client;       
    }

    public async Task<T> GetAsync()
    {
        var response = await this.Client.GetAsync(this.Url);
        var responseContent = await response.Content.ReadAsStringAsync();
        return JsonConvert.DeserializeObject<T>(responseContent);
    }
}

我尝试过的事情:

  • using中每次使用HttpClient都有一个ApiRequestBuilder语句-仍然挂起。

  • 等待Task的for循环中的每个ValuesController.Test-可行,但不能解决我的问题,因为在实践中,我正在尝试使用自定义auth属性模拟并发

可能有用:

  • 我在端口44301上运行本地IIS,最大并发连接数量巨大-4294967295。

  • 就像我说的那样,在授权时将网址从localhost更改为example.com之类的问题即可解决,将问题隔离到本地设置有问题

有人知道为什么在授权属性中使用Web请求似乎与localhost并发而不是在广阔的世界中使用URL并发吗?

更新

在挂起时运行netstat -n(我删除了超时,因此可以查看TCP连接的工作)。对于似乎被阻止的每个正在进行的Web请求,我都有以下内容:

Proto  Local Address          Foreign Address        State
TCP    [::1]:44301            [::1]:44728            CLOSE_WAIT
TCP    [::1]:44728            [::1]:44301            FIN_WAIT_2

this的帮助下,服务器似乎要求客户退出,客户承认了这一点,但是它们都挂了(客户端没有关闭吗?)。

2 个答案:

答案 0 :(得分:0)

可能是因为创建了太多客户端,并导致套接字耗尽的已知问题。

引用YOU'RE USING HTTPCLIENT WRONG AND IT IS DESTABILIZING YOUR SOFTWARE

使用单个静态客户端可能会解决此问题

class A
  @@values = {}
  def initialize
    @@values.each{ |key, val|
      puts("Key: #{key}, Val: #{val}")
    }
  end
end

class B < A
  @@values = { "Test1" => "1", "Test2" => "2"}
  def initialize
    super
  end
end

B.new
#⇒ Key: Test1, Val: 1
#  Key: Test2, Val: 2

答案 1 :(得分:0)