SocketException при вызове внешнего API - PullRequest
0 голосов
/ 07 мая 2018

Я вызываю мой API из функции Azure ServiceBusTrigger, используя пользовательский класс "ApiProvider"

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 без лишней информации! Но согласно Заявлению 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: 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();
    }

1 Ответ

0 голосов
/ 08 мая 2018

Вы должны создать 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;
    }
}

UPDATE:

Основываясь на ваших изменениях, я бы предложил:

  1. Удалите _log использование из CreateHttpClient и просто сгенерируйте исключение, если возникнет проблема с загрузкой сертификата. Это должно позволить сохранить этот метод статичным, а также быстро и наглядно завершать работу при возникновении проблемы с настройкой.

  2. Если вам действительно нужен регистратор в CreateHttpClient, сделайте его нестатичным и используйте свой оригинальный код, но вызывайте CreateHttpClient только один раз:

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

    Возможно некоторое состояние гонки, но я не вижу в этом большой проблемы. Добавьте блокировку, если хотите.

...