每个套接字地址 (protocol/network address/port) 通常只允许使用一次
Only one usage of each socket address (protocol/network address/port) is normally permitted
过去几周我们在使用 Azure 搜索 SDK (1.1.1 - 1.1.2) 和执行搜索时遇到此错误消息。
我们使用内部 APIs(部署为 Azure Web 应用程序)的搜索 SDK,它根据流量扩展 up-down(因此可能有超过 1 个 API 正在搜索)。
我们的 API 查询 5 个不同的索引并维护一个 in-memory 对应于每个索引的 SearchIndexClient object 副本,一个非常简单的实现如下:
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);
}
}
由于需要接收自定义 HTTP headers 而不是 SearchAsync
方法,我们正在使用 SearchWithHttpMessagesAsync
。
这样我们就可以避免 opening/closing 客户端处于流量突发状态。在使用此内存缓存(并将每个客户端包装在 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)
正如您问题中的代码所实现的,缓存不会防止端口耗尽。这是因为您将其实例化为 ApiController
的字段,每个请求创建一次。如果你想避免端口耗尽,缓存必须在所有请求之间共享。为了使其并发安全,您应该使用 ConcurrentDictionary
而不是 Dictionary
.
"connection attempt failed" 错误可能无关。
过去几周我们在使用 Azure 搜索 SDK (1.1.1 - 1.1.2) 和执行搜索时遇到此错误消息。
我们使用内部 APIs(部署为 Azure Web 应用程序)的搜索 SDK,它根据流量扩展 up-down(因此可能有超过 1 个 API 正在搜索)。
我们的 API 查询 5 个不同的索引并维护一个 in-memory 对应于每个索引的 SearchIndexClient object 副本,一个非常简单的实现如下:
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);
}
}
由于需要接收自定义 HTTP headers 而不是 SearchAsync
方法,我们正在使用 SearchWithHttpMessagesAsync
。
这样我们就可以避免 opening/closing 客户端处于流量突发状态。在使用此内存缓存(并将每个客户端包装在 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)
正如您问题中的代码所实现的,缓存不会防止端口耗尽。这是因为您将其实例化为 ApiController
的字段,每个请求创建一次。如果你想避免端口耗尽,缓存必须在所有请求之间共享。为了使其并发安全,您应该使用 ConcurrentDictionary
而不是 Dictionary
.
"connection attempt failed" 错误可能无关。