C# Asp net core 2.2 http客户端系统.net.Sockets.SocketException

C# Asp net core 2.2 http客户端系统.net.Sockets.SocketException,c#,asp.net,asp.net-core,C#,Asp.net,Asp.net Core,TL;博士: 要发送请求,我们以前使用的是错误的方法: using (var client = new HttpClient()) 最近我们迁移到推荐的HttpClientFactory。自迁移以来,在服务之间发送请求时,我们得到System.Net.Sockets.SocketException 更详细的信息: 如前所述,我们正在为每个使用using块发送的请求执行新的HttpClient()。 根据一些博客文章,这是一件坏事,我们经历了它的不良副作用,并采用推荐的方法使用IHttpCli

TL;博士

要发送请求,我们以前使用的是错误的方法:

using (var client = new HttpClient()) 
最近我们迁移到推荐的HttpClientFactory。自迁移以来,在服务之间发送请求时,我们得到System.Net.Sockets.SocketException

更详细的信息

如前所述,我们正在为每个使用using块发送的请求执行新的HttpClient()。 根据一些博客文章,这是一件坏事,我们经历了它的不良副作用,并采用推荐的方法使用IHttpClientFactory,让dotnet做它的事:

因此,我们用推荐的发送请求方式替换了丑陋的使用块:

public class RestClient
{
    private IHttpClientFactory clientFactory_;
    public RestClient(IHttpClientFactory clientFactory)
    {
        clientFactory_ = clientFactory;
    }

    public HttpResponseMessage SendRequest(HttpMethod httpMethod, string requestUri, List<KeyValuePair<string, string>> headerContent, HttpContent content)
    {
        using (var requestMessage = new HttpRequestMessage(httpMethod, requestUri))
        {
            // business logic
            var response = clientFactory_.CreateClient("businessRestClient").SendAsync(requestMessage).Result;
            // process and return
        }
    }
}
公共类RestClient
{
私人IHttpClientFactory客户端工厂;
公共RestClient(IHttpClientFactory客户端工厂)
{
clientFactory=客户工厂;
}
公共HttpResponseMessageSendRequest(HttpMethod HttpMethod,字符串请求URI,列表标题内容,HttpContent内容)
{
使用(var requestMessage=newhttprequestmessage(httpMethod,requestUri))
{
//业务逻辑
var response=clientFactory.CreateClient(“businessRestClient”).SendAsync(requestMessage).Result;
//过程和返回
}
}
}
我们使用以下扩展方法创建并添加命名的HttpClient及其处理程序:

    public static IServiceCollection AddRestClient(this IServiceCollection collectionBuilder)
    {
        collectionBuilder.AddHttpMessageHandlers();
        collectionBuilder
            .AddHttpClient("businessRestClient")
            .AddHttpMessageHandler<BusinessRequestHandler>()
            .AddHttpMessageHandler<HttpLoggingHandler>();

        collectionBuilder.AddScoped<RestClient>();
        return collectionBuilder;
    }

    private static IServiceCollection AddHttpMessageHandlers(this IServiceCollection collectionBuilder)
    {
        collectionBuilder.TryAddTransient<HttpLoggingHandler>();
        collectionBuilder.TryAddTransient<BusinessRequestHandler>();
        return collectionBuilder;
    }
公共静态IServiceCollection AddRestClient(此IServiceCollection collectionBuilder)
{
collectionBuilder.AddHttpMessageHandlers();
收藏家
.AddHttpClient(“businessRestClient”)
.AddHttpMessageHandler()
.AddHttpMessageHandler();
collectionBuilder.AddScoped();
返回集合生成器;
}
专用静态IServiceCollection AddHttpMessageHandler(此IServiceCollection collectionBuilder)
{
collectionBuilder.TryAddTransient();
collectionBuilder.TryAddTransient();
返回集合生成器;
}
在我们的服务中,RestClient由DI传递给构造函数,并执行调用它的请求。(我们知道将.Result用于异步方法不是一个好主意)

好吧,根据每一篇博文,我们读到的关于这个主题的博文女士建议一切都应该是好的,对吗?因此,我们部署了我们的服务,在mintes内,我们开始得到这个例外: System.Net.Sockets.SocketException:每个套接字地址(协议/网络地址/端口)通常只允许使用一次

因此,我们检查了azure门户和我们的应用程序服务计划的指标,发现在部署时,一堆tcp连接被关闭,在部署之后,它们再次上升,根本没有处于关闭、关闭等待状态。基本上,我们的服务表现与以前一样,只是由于不断出现的异常,它们现在几乎不可用

如果有人有这个问题以及他们是如何解决的,一些建议或信息会很好

谢谢

---编辑---

我们的请求处理程序 requesthandler如下所示:

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // Cookies
        if (!request.Headers.Contains("HEADERNAME"))
        {
            var cookies = new List<System.Net.Http.Headers.CookieHeaderValue>();
            var sessionInfoCookieStore = await idInfoAccessor_.GetSessionInfoCookieStore();
            if (signedCookieOptionsAccessor_.Options != null && sessionInfoCookieStore != null)
            {
                string cookieValue = sessionInfoCookieStore.GetCookieString();
                cookies.Add(new System.Net.Http.Headers.CookieHeaderValue(signedCookieOptionsAccessor_.Options.CookieName, cookieValue));
            }

            // Add headers
            request.Headers.Add("HEADERNAME", HEADERVALUE);
            request.Headers.Add(HeaderNames.Cookie, cookies.Select(cookieHeader => cookieHeader.ToString()));
        }

        // Response
        var response = await base.SendAsync(request, cancellationToken);
        return response;
    }
protectedoverride async Task sendaync(HttpRequestMessage请求,CancellationToken CancellationToken)
{
//饼干
如果(!request.Headers.Contains(“HEADERNAME”))
{
var cookies=新列表();
var sessionInfo CookieStore=等待idInfoAccessor.getSessionInfo CookieStore();
if(signedCookieOptionAccessor_u2;.Options!=null&&sessionInfo CookieStore!=null)
{
字符串cookieValue=SessionInfo CookieStore.GetCookieString();
添加(新的System.Net.Http.Headers.CookieHeaderValue(signedCookieOptionsAccessor_uz.Options.CookieName,cookieValue));
}
//添加标题
request.Headers.Add(“HEADERNAME”,HEADERVALUE);
添加(HeaderNames.Cookie,cookies.Select(cookieHeader=>cookieHeader.ToString());
}
//回应
var response=await base.sendaync(请求、取消令牌);
返回响应;
}

另一个请求处理程序只是在请求前后用ILogger记录了几行代码

在该代码中等待发生死锁。我想说的是,首先需要使用适当的异步代码来修复这个问题。您是否也可以发布处理程序中的代码?可能是他们造成了问题。你能更详细地谈谈你对死锁的看法吗@对于代码本身,它有一些问题,并且没有遵循链接文章中的模式。首先,不要使用
.Result
。HttpClient没有任何阻塞方法是有原因的
SendRequest
应成为异步方法。使用async一直到顶端(操作)允许更大的可伸缩性,这转化为为为每台服务器提供更多的请求,或者更确切地说,需要更小/更少的服务器来服务相同的流量。第二,
RestClient
是一个类型化的客户端,这意味着它应该在构造函数中接受
HttpClient
,不是
IHttpClientFactory
。文章显示,应该使用
AddHttpClient
注册和配置它。依赖于构造函数中的
RestClient
的控制器将通过HttpClientFactory接收一个完全配置的实例。最后,所有文章都解释了代码应该创建并重用HttpClient的一个实例
RestClient
的作用正好相反——它为每个RestClient实例创建一个新实例,而不释放(处置)它。这将很快耗尽套接字连接池。它与使用
var-client=new-HttpClient()没有什么不同
在每个请求中,确切地说是文章所说的不要做的