间歇性套接字异常调用 API / 来自 ASP.NET Azure 中托管的 Core 5.0 MVC 站点的服务

Intermittent Socket Exceptions calling API / Services from ASP.NET Core 5.0 MVC site hosted in Azure

Application Insights 中的错误消息:

A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond. (OurApiUrlAddress:443) A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond.

它总是 21 秒 TCP 超时,这是我知道的一个非常普遍的错误,但这个错误的原因并不总是相同的,我一直在阅读所有关于这个的线程。我们几个月来一直在调查这个问题,但没有成功,我们也在与 Azure 团队联系。

重要:用 RUBY 编写的同一站点在过去使用相同的 API 没有任何问题,API 是响应迅速,从其他站点调用它没有任何问题,但是这个特定站点已从 RUBY 迁移到 .NET,同时该站点托管在 AZURE 中,这是 2 大变化。这只会在网站(记住它托管在 Azure 中)调用我们公司托管的 API / 服务时发生,当网站调用其他地方托管的服务时不会发生这种情况,这让我们认为问题可能与此有关到公司基础设施,但它不能单独存在,这必须以某种方式与 .NET 和 AZURE 相关,因为这些 APIs 和服务可以完美地响应来自我们网络中托管的其他站点的调用,并且它们工作正常使用本网站的 ruby 版本。从公司网络外部在浏览器中调用时,这些 API 和服务不会抛出此错误。

services/apis 位于防火墙后面,但端口配置完美(没有任何其他流量应用程序或设备在运行)。

这个错误似乎与端口耗尽或 SNAT 有关,因为有时只有 1 个开发人员在 DEV 环境中工作,他会得到这个套接字异常错误。

仅供参考,我们在生产环境中每天会收到大约 250 个套接字异常,这只是所有调用的一小部分,所以有时会发生这种情况。

我们知道创建多个实例时众所周知的 HttpClient 问题,因此我们决定使用 Singleton 方法确保每个 API/Service 仅 1 个实例,正如我将在此处展示的那样,这是调用提供更多套接字异常:

StartUp class/file:

services.AddSingleton<IUploadApi>(new UploadApi(new HttpClient() { BaseAddress = new Uri(appSettings.Endpoints.UploadServicesUrl) }));

appsettings.json的一部分:

"Endpoints": {
    "UploadServicesUrl": "https://ourApiUrlAddress"
},

UploadApi.cs

public interface IUploadApi
{
    Task<UploadArtworkViewModel.UploadConfigurationData> GetUploadConfiguration();
}

public class UploadApi : IUploadApi
{
    private readonly HttpClient httpClient;

    public UploadApi(HttpClient client)
    {
        httpClient = client;
    }

    public async Task<UploadArtworkViewModel.UploadConfigurationData> GetUploadConfiguration()
    {
        var response = await httpClient.GetAsync("api/GetUploadConfiguration").ConfigureAwait(false);
        var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

        return JsonConvert.DeserializeObject<UploadArtworkViewModel.UploadConfigurationData>(json);
    }
}

来自控制器的调用:

model.UploadConfiguration = await UploadApi.GetUploadConfiguration().ConfigureAwait(false);

欢迎任何关于要测试的东西或要看的地方的想法,显然我无法重现这一点。我们知道总是有 21 秒的超时,这是 TCP 超时,但这并没有多大帮助。可能由于某种原因连接断开或 Azure 在访问公司网络时出现问题(有时)。如果需要,我可以 post 来自 application insights 的更多信息,但我没有看到关于该错误的任何特殊信息。

编辑 - 更多信息:当从这个 MVC 站点控制器调用任何 API 或服务时会发生这种情况,因此问题偶尔会出现(仍然像每个 300 次天)当站点服务器尝试访问 API 或服务时,这让我相信它与公司基础设施有关,但仍然不知道它可能是什么。

来自asp.net个怪物:

"the application has exited and yet there are still a bunch of these connections open"

"They are in the TIME_WAIT state which means that the connection has been closed on one side (ours) but we’re still waiting to see if any additional packets come in on it because they might have been delayed on the network somewhere."

即使您使用的是单例 HttpClient,似乎某些连接正在等待其他包,这会导致套接字耗尽。

解决方案是更改代码并使用 HttpClientFactory 或 HttpClientFacotoryLite。使用 HttpClientFactory 的原因是生成 HttpClient 实例,这些实例重新使用套接字处理程序池中的套接字处理程序。处理程序会定期回收以处理 DNS 更改。总之,在使用 HttpClientFactory 时,HttpClient 将工作委托给 SocketClientHandler。

在与 Azure 团队合作一段时间后,我们终于解决了这个问题,这是一个网关问题,解决方案是应用 NAT/Vnet 集成。这是我们修复它的方法: https://docs.microsoft.com/en-us/azure/app-service/networking/nat-gateway-integration