Я работал над приложением WPF, которое использует WCF для доступа к логике и базе данных на стороне сервера.
Я начал с одного прокси-объекта клиента WCF, который неоднократно использовал для вызова методов на сервере. После некоторого использования прокси-сервера сервер в конечном итоге выдает исключение:
System.ServiceModel.EndpointNotFoundException: There was no endpoint listening at http://.../Service/BillingService.svc that could accept the message. This is often caused by an incorrect address or SOAP action. See InnerException, if present, for more details. ---> System.Net.WebException: Unable to connect to the remote server ---> System.Net.Sockets.SocketException: An operation on a socket could not be performed because the system lacked sufficient buffer space or because a queue was full
.
Я думаю, это потому, что каждый сервисный вызов открывал новый сокет от прокси-сервера к серверу и никогда не закрывал их. В конце концов сервер был залит и начал отклонять запросы.
После недолгого поиска я решил, что мне нужно периодически закрывать () прокси. Образцы, которые я обнаружил, очень малы. Этот дал несколько полезных советов, но на самом деле не отвечает на вопрос. Я также видел рекомендации , чтобы избежать использования шаблона () (и вместо этого применить try / catch / finally), потому что метод Dispose прокси может вызвать исключение (yuck).
Похоже, рекомендуемый шаблон выглядит следующим образом:
[TestClass]
public class WCFClientUnitTest
{
BillingServiceClient _service;
[TestMethod]
public void TestGetAddressModel()
{
List<CustomerModel> customers = null;
try
{
_service = new BillingServiceClient();
customers = _service.GetCustomers().ToList();
}
catch
{
_service.Abort();
_service = null;
throw;
}
finally
{
if ((_service != null) &&
(_service.State == System.ServiceModel.CommunicationState.Opened))
_service.Close();
_service = null;
}
if (customers != null)
foreach (CustomerModel customer in customers)
{
try
{
_service = new BillingServiceClient();
AddressModel address = (AddressModel)_service.GetAddressModel(customer.CustomerID);
Assert.IsNotNull(address, "GetAddressModel returned null");
}
catch
{
_service.Abort();
_service = null;
throw;
}
finally
{
if ((_service != null) &&
(_service.State == System.ServiceModel.CommunicationState.Opened))
_service.Close();
_service = null;
}
}
}
Таким образом, мой вопрос все еще вращается вокруг того, как долго я должен поддерживать прокси клиента? Должен ли я открывать / закрывать его для каждого запроса на обслуживание? Это кажется чрезмерным для меня. Не получу ли я значительный удар по производительности?
Что я действительно хочу сделать, так это создать и открыть канал и сделать краткий пакет повторных, коротких, последовательных обращений в службу по всему каналу. Затем аккуратно закройте канал.
В качестве примечания, хотя я еще не реализовал его, я скоро добавлю модель безопасности в службу (как SSL, так и ACL), чтобы ограничить тех, кто может вызывать методы службы. В одном из ответов на в этом посте упоминается, что пересмотр контекста аутентификации и безопасности делает повторное открытие канала для каждого вызова службы бесполезным, но просто рекомендует избегать создания контекста безопасности.
РЕДАКТИРОВАТЬ 03.11.2010: Это кажется важным, поэтому я добавляю его к вопросу ...
В ответ на комментарий / предложение *1029* Эндрю Шепарда я повторно запустил мой модульный тест с отключением TrendMicro AntiVirus, отслеживая вывод команды netstat -b. Netstat смог зафиксировать значительный рост открытых портов, которые принадлежали WebDev.WebServer40.exe. Подавляющее большинство портов были в состоянии TIME_WAIT. Microsoft говорит , что порты могут задерживаться в NET_WAIT после того, как клиент закроет соединение ...
ПРИМЕЧАНИЕ. Нормально иметь гнездо в
состояние TIME_WAIT на длительный период
времени. Время указано в
RFC793 как двойной максимальный сегмент
Пожизненная (MSL). MSL указано быть
2 минуты. Таким образом, сокет может быть в
TIME_WAIT состояние до 4
минут. Некоторые системы реализуют
разные значения (менее 2 минут)
для MSL.
Это наводит меня на мысль, что если каждый вызов службы открывает новый сокет на сервере, и поскольку я вызываю службу в узком цикле, я могу легко переполнить сервер, из-за чего у него заканчиваются доступные сокеты и включите генерацию исключения, которое я отметил выше.
Поэтому мне нужно выбрать один из двух путей:
1) попытка пакетных вызовов службы, чтобы они повторно использовали сокет на стороне сервера
2) измените мой контракт на обслуживание, чтобы я мог возвращать большие порции данных с меньшим количеством вызовов.
Первый выбор кажется мне лучше, и я собираюсь продолжать его преследовать. Я опубликую то, что узнаю, и буду рад дальнейшим комментариям, вопросам и ответам.