Web Api 中的 HttpClient 非常慢
HttpClient on WebApi is extremely slow
我正在使用 ASP.NET WebApi (ApiController) 为我的应用程序实现一个代理,并使用 HttpClient 在我的授权下发出请求 header。它工作正常,但速度非常慢。下面是主要代码,然后是全局初始化(使用 DefaultConnectionLimit)和 web.config 相关部分。
如您所见,我已经在使用没有代理的 static/shared HttpClient object 和 HttpCompletionOption.ResponseHeadersRead 实际请求.这个 WebApi 端点被并行调用,工作正常。
整个代码运行速度足够快,但是当我使用 ResponseHeadersRead async 时,HttpRequestMessage 被返回并且 body 的其余部分被下载并直接流式传输到 client/caller。
这里 a video 显示了问题。
public class ProxyController : ApiController
{
private const string BASE_URL = "https://developer.api.autodesk.com";
private const string PROXY_ROUTE = "api/viewerproxy/";
// HttpClient has been designed to be re-used for multiple calls. Even across multiple threads.
//
private static HttpClient _httpClient;
[HttpGet]
[Route(PROXY_ROUTE + "{*.}")]
public async Task<HttpResponseMessage> Get()
{
if (_httpClient == null)
{
_httpClient = new HttpClient(new HttpClientHandler()
{
UseProxy = false,
Proxy = null
});
_httpClient.BaseAddress = new Uri(BASE_URL);
}
string url = Request.RequestUri.AbsolutePath.Replace(PROXY_ROUTE, string.Empty);
string absoluteUrl = url + Request.RequestUri.Query;
try
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, absoluteUrl);
request.Headers.Add("Authorization", "Bearer " + AccessToken);
HttpResponseMessage response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
return response;
}
catch (Exception e)
{
return new HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError);
}
}
}
Global.asax,虽然我不认为是连接限制的问题,因为所有请求都已处理,但太慢了...
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
GlobalConfiguration.Configure(Config.WebApiConfig.Register);
ServicePointManager.UseNagleAlgorithm = true;
ServicePointManager.Expect100Continue = false;
ServicePointManager.CheckCertificateRevocationList = true;
ServicePointManager.DefaultConnectionLimit = int.MaxValue;
}
}
和部分Web.Config
<system.web>
<compilation debug="true" targetFramework="4.6" />
<httpRuntime targetFramework="4.6" maxRequestLength="2097151" requestLengthDiskThreshold="16384" requestPathInvalidCharacters="<,>,*,%,&,\,?" />
</system.web>
通过删除 web.config 的 <system.diagnostics>
部分解决。它似乎导致了过多的输出并减慢了所有 HttpClient
请求。
郑重声明,这是我使用并导致所有 HttpClient.SendAsync
调用缓慢的代码。但这对于跟踪连接问题很有用:-)
<system.diagnostics>
<sources>
<source name="System.Net" tracemode="protocolonly" maxdatasize="1024">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
<source name="System.Net.Cache">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
<source name="System.Net.Http">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
</sources>
<switches>
<add name="System.Net" value="Verbose"/>
<add name="System.Net.Cache" value="Verbose"/>
<add name="System.Net.Http" value="Verbose"/>
<add name="System.Net.Sockets" value="Verbose"/>
<add name="System.Net.WebSockets" value="Verbose"/>
</switches>
<sharedListeners>
<add name="System.Net"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="network.log"
/>
</sharedListeners>
<trace autoflush="true"/>
</system.diagnostics>
我正在使用 ASP.NET WebApi (ApiController) 为我的应用程序实现一个代理,并使用 HttpClient 在我的授权下发出请求 header。它工作正常,但速度非常慢。下面是主要代码,然后是全局初始化(使用 DefaultConnectionLimit)和 web.config 相关部分。
如您所见,我已经在使用没有代理的 static/shared HttpClient object 和 HttpCompletionOption.ResponseHeadersRead 实际请求.这个 WebApi 端点被并行调用,工作正常。
整个代码运行速度足够快,但是当我使用 ResponseHeadersRead async 时,HttpRequestMessage 被返回并且 body 的其余部分被下载并直接流式传输到 client/caller。
这里 a video 显示了问题。
public class ProxyController : ApiController
{
private const string BASE_URL = "https://developer.api.autodesk.com";
private const string PROXY_ROUTE = "api/viewerproxy/";
// HttpClient has been designed to be re-used for multiple calls. Even across multiple threads.
//
private static HttpClient _httpClient;
[HttpGet]
[Route(PROXY_ROUTE + "{*.}")]
public async Task<HttpResponseMessage> Get()
{
if (_httpClient == null)
{
_httpClient = new HttpClient(new HttpClientHandler()
{
UseProxy = false,
Proxy = null
});
_httpClient.BaseAddress = new Uri(BASE_URL);
}
string url = Request.RequestUri.AbsolutePath.Replace(PROXY_ROUTE, string.Empty);
string absoluteUrl = url + Request.RequestUri.Query;
try
{
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, absoluteUrl);
request.Headers.Add("Authorization", "Bearer " + AccessToken);
HttpResponseMessage response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead);
return response;
}
catch (Exception e)
{
return new HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError);
}
}
}
Global.asax,虽然我不认为是连接限制的问题,因为所有请求都已处理,但太慢了...
public class Global : System.Web.HttpApplication
{
protected void Application_Start(object sender, EventArgs e)
{
GlobalConfiguration.Configure(Config.WebApiConfig.Register);
ServicePointManager.UseNagleAlgorithm = true;
ServicePointManager.Expect100Continue = false;
ServicePointManager.CheckCertificateRevocationList = true;
ServicePointManager.DefaultConnectionLimit = int.MaxValue;
}
}
和部分Web.Config
<system.web>
<compilation debug="true" targetFramework="4.6" />
<httpRuntime targetFramework="4.6" maxRequestLength="2097151" requestLengthDiskThreshold="16384" requestPathInvalidCharacters="<,>,*,%,&,\,?" />
</system.web>
通过删除 web.config 的 <system.diagnostics>
部分解决。它似乎导致了过多的输出并减慢了所有 HttpClient
请求。
郑重声明,这是我使用并导致所有 HttpClient.SendAsync
调用缓慢的代码。但这对于跟踪连接问题很有用:-)
<system.diagnostics>
<sources>
<source name="System.Net" tracemode="protocolonly" maxdatasize="1024">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
<source name="System.Net.Cache">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
<source name="System.Net.Http">
<listeners>
<add name="System.Net"/>
</listeners>
</source>
</sources>
<switches>
<add name="System.Net" value="Verbose"/>
<add name="System.Net.Cache" value="Verbose"/>
<add name="System.Net.Http" value="Verbose"/>
<add name="System.Net.Sockets" value="Verbose"/>
<add name="System.Net.WebSockets" value="Verbose"/>
</switches>
<sharedListeners>
<add name="System.Net"
type="System.Diagnostics.TextWriterTraceListener"
initializeData="network.log"
/>
</sharedListeners>
<trace autoflush="true"/>
</system.diagnostics>