WCF и подтверждения — нужно ли мне отправлять клиенту «ОК, понял»?

Если у меня есть сотни/тысячи клиентских компьютеров, подключающихся к моему серверу через WCP, должен ли я ответить сообщением типа «200 OK», в котором говорится, что я получил данные и успешно сохранил их в базе данных?

Это уже встроено в WCF?


person Community    schedule 22.10.2008    source источник


Ответы (4)


Здесь упоминаются три шаблона обмена сообщениями:

  1. Синхронный запрос-ответ
  2. Асинхронная отправка (выстрелил и забыл с односторонней службой)
  3. Асинхронный запрос-ответ (дуплексный сервисный вызов с односторонним обслуживанием)

Все три демонстрируют различное поведение при обмене сообщениями, и шаблон, который будет использоваться, следует выбирать в зависимости от ваших потребностей.

Для вашего требования возвращать клиенту сообщение об успешном завершении, когда данные были сохранены в базе данных, подходят варианты 1 или 3.

Опция 1

Это конфигурация по умолчанию.

Это возвращает ответ 200 по тому же http-соединению после того, как служба выполнила все свои задачи. Это означает, что вызовы службы будут блокироваться в ожидании ответа — ваш клиент будет зависать до тех пор, пока служба не выполнит запись в базу данных и не сделает все необходимое.

Вариант 2

Возвращает ответ 202, если транспортный уровень и уровень инфраструктуры обмена сообщениями работают успешно. Возвращенный код 202 означает, что сообщение было принято. Из http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html:

Запрос принят в обработку, но обработка не завершена. (...) Ответ 202 намеренно ни к чему не обязывает.

Это означает, что ваш клиент продолжит выполнение, как только инфраструктура службы успешно запустит службу, и вы не получите никакой информации о том, был ли вызов базы данных успешным или нет.

Вариант 3

Подобно варианту 2, ответ представляет собой http 202, а не 200, но теперь, когда вы вызываете службу от клиента, вы предоставляете объект InstanceContext, который указывает объект для обработки обратного вызова. Клиент восстанавливает управление, и новый поток асинхронно ожидает ответа службы, информирующего вас об успехе или неудаче.


Ниже приведены дополнительные сведения о реализации этих шаблонов в WCF. После написания это довольно долго, но есть пара полезных комментариев, а также код.

Как упомянул Виск, у Ювала Лоуи есть статья, в которой подробно описывается (и не только!) здесь

Опция 1

Это поведение по умолчанию в WCF, поэтому вам не нужно ничего делать — оно работает с множеством различных привязок, включая wsHttpBinding и basicHttpBinding.

Следует отметить, что поведение, описанное с вашим клиентом, зависающим в ожидании ответа 200, происходит, даже если у вас есть пустая операция, например:

[ServiceContract]
interface IMyServiceContract
{
    [OperationContract]       
    void DoSomeThing(InputMessage Message);
}

Вариант 2

Чтобы установить операцию как одностороннюю, вы просто оформляете ее следующим образом:

[ServiceContract]
interface IMyServiceContract
{
    [OperationContract(IsOneWay = true)]       
    void DoSomeThing(InputMessage Message);
}

Методы, оформленные таким образом, должны иметь только возвращаемый тип void. Одна из проблем заключается в том, что служба будет успешно построена, и вы не получите исключение о том, что конфигурация службы недействительна, пока вы не подключитесь к ней.

Вариант 3

Код интерфейса для создания дуплексной службы обратного вызова:

[ServiceContract(CallbackContract = typeof(IMyContractCallback))]
interface IMyContract
{
    [OperationContract(IsOneWay=true)]       
    void DoSomeThing(InputMessage Message);
}

public interface IMyContractCallback
{
    [OperationContract(IsOneWay = true)]
    void ServiceResponse(string result);      
}

А на стороне клиента нужно что-то вроде этого для настройки обратного вызова:

public class CallbackHandler : IMyContractCallback        
{
    #region IEchoContractCallback Members

    public void ServiceResponse(string result)
    {
        //Do something with the response
    }

    #endregion
}

// And in the client when you set up the service call:

InstanceContext instanceContext = new InstanceContext(new CallbackHandler());
MyContractClient client = new MyContractClient(instanceContext);

InputMessage msg = new InputMessage();

client.DoSomething(msg);           

В сервисе у вас, возможно, будет какой-то код, например:

class MyContractImplementation : IMyContract
{
    public void DoSomeThing(MyMessage Message)
    {

        string responseMessage;

        try
        {
           //Write to the database
           responseMessage = "The database call was good!";
        }
        catch (Exception ex)
        {
            responseMessage = ex.Message;
        }

        Callback.ServiceResponse(responseMessage);
    }
}

Следует отметить одну важную вещь, которая поначалу меня зацепила, — быть осторожным с исключениями. Если ты:

  • Используйте исключение, вы не получите предупреждения об ошибке (но произойдет обратный вызов)

  • Вызовите необработанное исключение, служба завершится, и клиент даже не получит обратный вызов.

Это один из больших недостатков этого метода по сравнению с вариантом 1. Вариант 1 возвращает исключение при возникновении необработанного исключения.

person David Hall    schedule 23.10.2008

По умолчанию ваша служба будет возвращать клиенту сообщение «ОК» (даже если ваш метод службы указывает тип возвращаемого значения void), если не будет выбрано исключение. Клиент будет ждать, пока не получит это сообщение, прежде чем продолжить свою жизнь.

Итак, исходя из вашего вопроса, вы получаете поведение, которое хотите по умолчанию.

Если вы этого не хотите, я бы согласился с Whisk и пометил вашу операцию как One Way (настройка в вашем контракте на операцию).

Удачи!

person James Bender    schedule 22.10.2008

Это можно сделать двумя способами: вернуть значение из вашего вызова wcf, указывающее на успех или неудачу, или предположить успех и использовать обратный вызов, чтобы сообщить клиенту, есть ли проблема.

Я бы предпочел использовать односторонний сервисный вызов с дуплексная служба и использовать контракт обратного вызова для службы, чтобы уведомить клиента неудачи. Хорошее описание контрактов обратного вызова можно найти здесь и здесь.

Я думаю, что вы получите более красивую, более асинхронную архитектуру, если пойдете по второму пути, но для простых приложений может быть проще просто возвращать успех или неудачу.

person Whisk    schedule 22.10.2008
comment
в одностороннем порядке, как я могу сгенерировать ошибку? Будет ли это ошибкой, о которой узнают и клиент, и сервер? - person ; 22.10.2008
comment
Извините, моя первая попытка была немного поспешной и довольно неточной (прошло некоторое время с тех пор, как я делал это с WCF), надеюсь, это станет немного яснее. - person Whisk; 23.10.2008

Я думаю, что вопрос мог бы использовать немного больше разработки, чтобы ответить правильно. Первичный, какой тип привязки вы используете в настоящее время?

Если вы используете привязку, которая поддерживает надежность, и вы просто хотите быть уверены со стороны клиента, что этот сервер действительно получил сообщение, тогда просто включите надежность, и все готово. Когда это включено, WCF автоматически заставит каждую сторону разговора сказать: «Вы поняли?» — Я понял. Ты понял? "Я понял."

person pettys    schedule 23.10.2008