Что происходит в WCF с методами с IsOneWay=true при завершении приложения
У меня есть клиентское приложение, которое время от времени уведомляет о своем прогрессе службу. Вызов метода для службы помечен IsOneWay = true, потому что уведомление не требует никакого возвращаемого значения, и я не хочу откладывать.
Клиент может уведомить об ошибках сервис, и после этого он прекращает работу.
Вопрос в том, возвращает ли вызов метода oneway код вызывающей стороны после отправки сообщения? или оно ставит сообщение в очередь, а затем отправляется другим потоком?
Два процесса (клиент и служба) находятся на одной машине, и я заметил, что иногда (когда машина перегружена) служба не получает уведомление об ошибке. Я подозреваю, что второй вариант, который я упомянул, случается, но я не уверен.
Если я прав, как я могу убедиться, что уведомление отправлено, и сохранить метод в одном направлении?
6 ответов
Хороший вопрос. Прежде чем клиентское приложение вызывает метод, оно открывает канал. Канал используется для передачи всех данных. Существует два способа отправки: 1) надежный сеанс - когда ваши пакеты надежно удалены, а взломанные пакеты повторно отправлены, 2) упорядочение - когда запросы к услуге вычисляются в порядке их передачи от клиента (а не как они доставляются), Если у вас есть надежный заказанный сеанс, и у хоста службы возникают проблемы с данными после закрытия приложения, хост попытается запросить у клиента данные повторно, а после отсутствия ответа отклонит все ваши запросы. В другой ситуации (ненадежной) после открытия канала вы можете отправлять данные и уничтожать связь, односторонний метод вычислит ваш запрос, если не будет исключения.
Чтобы проверить некоторые возможности, связанные с проблемой обслуживания (однако не совсем то, что проблема с вашим клиентом полезна), я создаю решение:
1) Библиотечный проект "WcfContracts" с одним файлом "IService1.cs":
[ServiceContract]
public interface IService1
{
[OperationContract]
void ThrowException();
[OperationContract(IsOneWay=true)]
void ThrowExceptionUseIsOneWay();
}
2) Консольный проект "WcfConsoleHoster", который имеет ссылку на два "WcfContracts" и состоит из трех файлов:
a) Service1.cs, который является реализацией сервиса
public class Service1 : WcfContracts.IService1
{
public void ThrowException()
{
throw new Exception("Basic exception");
}
public void ThrowExceptionUseIsOneWay()
{
throw new Exception("Basic exception using IsOneWay=true");
}
}
б) Program.cs, который имеет точку входа по умолчанию и просто запускает сервис
static void Main(string[] args)
{
ServiceHost host = new ServiceHost(typeof(Service1));
host.Open();
Console.WriteLine("host 1 opened");
Console.ReadKey();
}
в) Сервис "App.config"
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<services>
<service behaviorConfiguration="behavourHttpGet" name="WcfConsoleHoster.Service1">
<host>
<baseAddresses>
<add baseAddress="http://localhost:8732/Design_Time_Addresses/WcfConsoleHoster/Service1/" />
</baseAddresses>
</host>
<endpoint binding="wsHttpBinding" contract="WcfContracts.IService1" />
<endpoint address ="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="behavourHttpGet">
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
</configuration>
3) Консольный проект "WcfConsoleClient", который просто вызывает сервис
а) в "Program.cs"
Console.WriteLine("Wcf client. Press any key to start");
Console.ReadKey();
ChannelFactory<IService1> factory = new ChannelFactory<IService1>("Service1_Endpoint");
IService1 channel = factory.CreateChannel();
//Call service method
channel.ThrowException();
Console.WriteLine("Operation executed");
Console.ReadKey();
б) Клиент "App.config"
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<client>
<endpoint name="Service1_Endpoint"
address="http://localhost:8732/Design_Time_Addresses/WcfConsoleHoster/Service1/"
binding="wsHttpBinding"
contract="WcfContracts.IService1">
</endpoint>
</client>
</system.serviceModel>
</configuration>
1. Бросив исключение. Сначала мы вызываем двухсторонний метод, который выдает исключение на хосте сервера. Затем это исключение возвращается к клиенту и канал поднимает его на стороне клиента, приложение уничтожается. Конечно, вы можете справиться с этим с помощью блока try()catch{}.
Давайте посмотрим то же самое с односторонним методом, вызвав channel.ThrowExceptionUseIsOneWay();
, Исключение возникает в хосте службы, но на стороне клиента нет исключения, и мы получаем "Операция выполнена". Важно понимать, что канал будет недоступен для следующего использования.
Так IsOneWay=true
работает как положено - отправляет сообщение только одним способом. Вы не можете вернуть какой-либо объект из метода (ожидается void) и не можете использовать FaultContract или получить InvalidOperationException после запуска службы.
2. Thread.Sleep (). Следующий тест на массовой работе с Thread.Sleep()
,IService1
распространяется на
[OperationContract]
int ThreadSleep();
[OperationContract(IsOneWay=true)]
и реализация в Service1.cs ждет 5 секунд
public int ThreadSleep()
{
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
return 1;
}
public void ThreadSleepUseIsOneWay()
{
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
}
Теперь небольшая модификация для клиента для подсчета истекшего времени вызова
System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
stopwatch.Start();
//call methode
channel.ThreadSleep();
stopwatch.Stop();
Console.WriteLine(string.Format("Operation executed in {0} seconds", stopwatch.Elapsed.Seconds));
Console.ReadKey();
Вызов двухстороннего метода ThreadSleep()
имеет результат "Операция выполнена за 7 секунд" (5 секунд для спящего потока + 2 секунды для инициализации канала).
One way method
с вызовом channel.ThreadSleepUseIsOneWay()
имеет результат "0 секунд"! Там нет ожидания ответа службы!
Лучше всего использовать NetNamedPipeBinding, который является надежным и быстрым соединением на одной машине.
Проверьте этот пост для получения дополнительной информации: http://kennyw.com/?p=130
Кроме того, я считаю, что если у вас включен надежный обмен сообщениями, запрос будет проверен как успешно отправленный, но, как указано выше, заметки, служба завершит соединение после этой точки.
У меня была похожая проблема (односторонний вызов ничего не делал или выбрасывал исключение). Я обнаружил, что десерилизатор, отвечающий за преобразование аргумента метода из xml обратно в объекты, создает исключение. Мой метод обслуживания никогда не достигался, и исключение не возвращалось, потому что это был односторонний вызов. Я не обнаружил его, пока временно не отключил "односторонний", поэтому мое исключение было передано обратно клиенту.
Надеюсь, что это помогает кому-то еще в подобной ситуации.
Вы не можете "убедиться, что уведомление отправлено"... и "сохранить метод в одном направлении". Это противоречит тому, что означает "OneWay":)
Если вы хотите убедиться, что сообщение отправлено, это нормально сделать TwoWay. Скорее всего, вы не заметите небольшой скачок производительности. И если сервер и клиент находятся на той же машине, о которой вы упоминали... тогда вы вообще не заметите снижения производительности.
Это может быть хорошим местом для использования netMsmqBinding. Все сообщения MSMQ по своей природе являются односторонними, поскольку очереди по своей сути отключены. Как только клиент ставит сообщение в очередь с помощью клиента netMsmq, он может безопасно завершить работу, а затем сервер может забрать сообщение и обработать его.
Я согласен с Тимоти. Я также хочу добавить, что служба WCF хранит очередь для входящих сообщений. Эта очередь может заполниться, если служба не сможет обрабатывать сообщения так быстро, как они поступают. Когда входящая очередь заполнится, WCF будет отбрасывать новые сообщения.
Я не уверен, что происходит на стороне клиента, если отбрасываются односторонние сообщения. Я предполагаю, что никакое исключение / ошибка не выброшено, но я не знаю это точно.