Обычно разрешено только одно использование каждого адреса сокета (протокол / сетевой адрес / порт)

Последние несколько недель мы сталкивались с этим сообщением об ошибке при использовании Azure Search SDK (1.1.1 - 1.1.2) и выполнении поиска.

Мы используем Search SDK из внутренних API-интерфейсов (развернутых как веб-приложения Azure), которые масштабируются в зависимости от трафика (таким образом, может быть более 1 экземпляра API, выполняющих поиск).

Наш API запрашивает 5 различных индексов и поддерживает в памяти копию объекта SearchIndexClient, соответствующую каждому индексу, очень простая реализация будет выглядеть так:

public class AzureSearchService
{
    private readonly SearchServiceClient _serviceClient;

    private Dictionary<string, SearchIndexClient> _clientDictionary;

    public AzureSearchService()
    {
        _serviceClient = new SearchServiceClient("myservicename", new SearchCredentials("myservicekey"));
        _clientDictionary = new Dictionary<string, SearchIndexClient>();
    }

    public SearchIndexClient GetClient(string indexName)
    {
        try
        {
            if (!_clientDictionary.ContainsKey(indexName))
            {
                _clientDictionary.Add(indexName, _serviceClient.Indexes.GetClient(indexName));
            }
            return _clientDictionary[indexName];
        }
        catch
        {
            return null;
        }
    }

    public async Task<SearchResults> SearchIndex(SearchIndexClient client, string text)
    {
        var parameters = new SearchParameters();
        parameters.Top = 10;
        parameters.IncludeTotalResultCount = true;
        var response = await client.Documents.SearchWithHttpMessagesAsync(text, parameters, null, null);
        return response.Body;
    }
}

И API будет вызывать службу:

public class SearchController : ApiController
{
        private readonly AzureSearchService service;

        public SearchController()
        {
            service = new AzureSearchService();
        }


        public async Task<HttpResponseMessage> Post(string indexName, [FromBody] string text)
        {
            var indexClient = service.GetClient(indexName);
            var results = await service.SearchIndex(indexClient, text);
            return Request.CreateResponse(HttpStatusCode.OK, results, Configuration.Formatters.JsonFormatter);              
        }

}

Мы используем SearchWithHttpMessagesAsync из-за необходимости получения пользовательских заголовков HTTP вместо SearchAsync метод.

Таким образом, мы избегаем открывать / закрывать клиента под пробками трафика. Прежде чем использовать этот кэш памяти (и обернуть каждый клиент в using пункт) мы будем получать оповещения об исчерпании порта в службах приложений Azure.

Это хороший шаблон? Можем ли мы получить эту ошибку из-за того, что несколько экземпляров работают параллельно?

В случае необходимости трассировка стека показывает:

System.Net.Http.HttpRequestException: Only one usage of each socket address (protocol/network address/port) is normally permitted service.ip.address.hidden:443


[SocketException:Only one usage of each socket address (protocol/network address/port)is normally permitted service.ip.address.hidden:443]

at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult)

at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure,Socket s4,Socket s6,Socket& socket,IPAddress& address,ConnectSocketState state,IAsyncResult asyncResult,Exception& exception)



[WebException:Unable to connect to the remote server]

at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult,TransportContext& context)

at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)

РЕДАКТИРОВАТЬ: Мы также получаем эту ошибку A connection attempt failed because the connected party did not properly respond after a period of time:

System.Net.Http.HttpRequestException: 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 service.ip.address.hidden:443


[SocketException: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 service.ip.address.hidden:443]

at System.Net.Sockets.Socket.EndConnect(IAsyncResult asyncResult)

at System.Net.ServicePoint.ConnectSocketInternal(Boolean connectFailure,Socket s4,Socket s6,Socket& socket,IPAddress& address,ConnectSocketState state,IAsyncResult asyncResult,Exception& exception)



[WebException:Unable to connect to the remote server]

at System.Net.HttpWebRequest.EndGetRequestStream(IAsyncResult asyncResult,TransportContext& context)

at System.Net.Http.HttpClientHandler.GetRequestStreamCallback(IAsyncResult ar)

1 ответ

Решение

Как реализовано в коде вашего вопроса, кеш не предотвратит исчерпание порта. Это потому, что вы создаете его как поле ApiController, который создается один раз за запрос. Если вы хотите избежать исчерпания порта, кеш должен быть общим для всех запросов. Чтобы обеспечить безопасность параллелизма, вы должны использовать что-то вроде ConcurrentDictionary вместо Dictionary,

Ошибка "попытка подключения не удалась", вероятно, не связана.

Другие вопросы по тегам