Я работал над 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
.
Я думаю, это связано с тем, что каждый служебный вызов открывает новый сокет от прокси-сервера до сервера и никогда не закрывает его. В конце концов сервер был затоплен и начал отказывать в запросах.
После краткого поиска, я решил, что мне нужно периодически закрывать() прокси. Образцы, которые я нашел, вырождены. Этот дал некоторые полезные подсказки, но на самом деле не отвечает на вопрос. Я также видел рекомендации, чтобы избежать шаблона use() (и вместо этого применить try/catch/finally), потому что метод dispose 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), чтобы ограничить, кто может вызвать методы службы. В одном из ответов на этот пост упоминается, что пересмотр контекста аутентификации и безопасности делает повторный запуск канала для каждого вызова службы расточительным, но просто рекомендует избегать построения контекста безопасности.
EDIT 11/3/2010: Это кажется важным, поэтому я добавляю его к вопросу...
В ответ на комментарий/предложение Andrew Shepherd я перезапустил мой unit test с отключением TrendMicro AntiVirus, контролируя вывод netstat -b. Netstat смог зафиксировать значительный рост открытых портов, принадлежащих WebDev.WebServer40.exe. Подавляющее большинство портов находилось в состоянии TIME_WAIT. Microsoft говорит, что порты могут задерживаться в NET_WAIT после того, как клиент закроет соединение...
ПРИМЕЧАНИЕ. Нормально иметь гнездо в состояние TIME_WAIT в течение длительного периода времени времени. Время указано в RFC793 в два раза больше максимального сегмента Время жизни (MSL). MSL определяется как 2 минуты. Таким образом, сокет может находиться в TIME_WAIT до тех пор, пока 4 минут. Некоторые системы реализуют разные значения (менее 2 минут) для MSL.
Это заставляет меня думать, что если каждый вызов службы открывает новый сокет на сервере, и потому что я вызываю службу в узком цикле, я мог бы легко затопить сервер, в результате чего у него закончились доступные сокеты и в turn генерирует исключение, указанное выше.
Поэтому мне нужно продолжить один из двух путей: 1) попытка пакетных вызовов службы, чтобы они повторно использовали сокет на стороне сервера 2) измените мой контракт на обслуживание, чтобы я мог вернуть более крупные куски данных с меньшим количеством вызовов.
Первый выбор кажется мне лучше, и я продолжу его преследовать. Я отправлю назад то, что обнаруживаю, и приветствую дальнейшие комментарии, вопросы и ответы.