调用外部时出现 SocketException api

SocketException when calling external api

我正在使用自定义 class "ApiProvider"

从 Azure Function ServiceBusTrigger 调用我的 API
public class ApiProvider
{
    private readonly string _backendUrl;
    private static HttpClient _client;
    private readonly TraceWriter _log;

    public ApiProvider(TraceWriter log)
    {
        _pamUrl = Environment.GetEnvironmentVariable("ApiUrl");
        _log = log;
        _client = CreateHttpClient();
    }
}

我为 ServiceBus 队列中发送的每条消息调用它,假设有 1500 条消息,它将调用我支持的 1500 次。

有些调用是成功的,但有时我在 Azure Functions 日志中出错,没有太多信息!但根据 Application Insights:

System.Net.Sockets.SocketException
Exception while executing function: QueueDirectory An error occurred while sending the request. Unable to connect to the remote server Only one usage of each socket address (protocol/network address/port) is normally permitted

我认为将 HttpClient 设为静态就足够了,但它似乎并没有完全解决我猜想的问题,还是我遗漏了什么?

环境: Azure Functions 运行时:1.0.11702.0

编辑:我的方法 CreateHttpClient():

    private static HttpClient CreateHttpClient()
    {
        string thumbprint = Environment.GetEnvironmentVariable("WEBSITE_LOAD_CERTIFICATES");
        if (thumbprint != null)
        {
            _log.LogInformation("Get the certificate of thumbprint : " + thumbprint);

            using (X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser))
            {
                certStore.Open(OpenFlags.ReadOnly);
                X509Certificate2Collection certCollection =
                    certStore.Certificates.Find(X509FindType.FindByThumbprint, thumbprint, false);

                // Get the first cert with the thumbprint
                var certificate = certCollection.OfType<X509Certificate2>().FirstOrDefault();
                if (certificate != null)
                {
                    _log.LogInformation("Certificate has been found.");
                    var handler = new WebRequestHandler();
                    handler.ClientCertificates.Add(certCollection[0]);
                    return new HttpClient(handler);
                }
                _log.LogInformation("Certificate has not been found.");
            }
        }
        return new HttpClient();
    }

您应该只创建一次 HttpClient,而不是为每个请求创建一次:

private static HttpClient _client = new HttpClient();

或者如果您需要保留初始化:

public class ApiProvider
{
    private readonly string _backendUrl;
    private static HttpClient _client;
    private readonly TraceWriter _log;

    static ApiProvider()
    {
        _client = CreateHttpClient();
    }

    public ApiProvider(TraceWriter log)
    {
        _pamUrl = Environment.GetEnvironmentVariable("ApiUrl");
        _log = log;
    }
}

更新:

根据您的修改,我建议:

  1. CreateHttpClient 中删除 _log 用法,如果您遇到证书加载问题,则抛出异常。这应该允许保持此方法静态,并且如果设置出现问题,也会快速且明显地失败。

  2. 如果您确实需要 CreateHttpClient 中的记录器,请将其设为非静态并使用您的原始代码,但只调用 CreateHttpClient 一次:

    public ApiProvider(TraceWriter log)
    {
        _pamUrl = Environment.GetEnvironmentVariable("ApiUrl");
        _log = log;
        if (_client == null) _client = CreateHttpClient();
    }
    

    可能存在一些竞争条件,但我认为这不是什么大问题。如果需要,请加锁。