Обычно разрешено только одно использование каждого адреса сокета (протокол / сетевой адрес / порт)
Последние несколько недель мы сталкивались с этим сообщением об ошибке при использовании 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
,
Ошибка "попытка подключения не удалась", вероятно, не связана.