Создание маршрутизатора WCF - Дуплексные маршрутизаторы

ОГЛАВЛЕНИЕ

Дуплексные маршрутизаторы

В коде на рис. 6 показан пример контракта дуплексного маршрутизатора для поддержки ситуации, когда сообщения между клиентом, маршрутизатором и службами приложений отправляются по протоколу TCP. У этого контракта следующие отличия от традиционного контракта маршрутизатора типа запрос-ответ.

  • ProcessMessage теперь является односторонней операцией.
  • Контракту службы требуются сеансы, и у него имеется соответствующий контракт обратного вызова. Важно обратить внимание на то, что при этом не требуется клиент для реализации обратного вызова; это внутренняя задача маршрутизатора.
  • У контракта обратного вызова имеется единственный метод для получения ответов от обращений маршрутизатора к службам приложений. Обратите внимание, что службы не знают о том, что их ответы отправляются в канал обратного вызова; они могут быть сообщениями запрос-ответ.

Рис. 6 Контракт дуплексного маршрутизатора

[ServiceContract(Namespace = 
  "http://www.thatindigogirl.com/samples/2008/01",
  SessionMode = SessionMode.Required,
  CallbackContract = typeof(IDuplexRouterCallback))]

public interface IDuplexRouterService {
  [OperationContract(IsOneWay=true, Action = "*")]
  void ProcessMessage(Message requestMessage);
}

[ServiceContract(Namespace =
  "http://www.thatindigogirl.com/samples/2008/01",
  SessionMode = SessionMode.Allowed)]
public interface IDuplexRouterCallback {
  [OperationContract(IsOneWay=true, Action = "*")]
  void ProcessMessage(Message requestMessage);
}

Архитектура дуплексного маршрутизатора показана на рис. 7. Что касается клиента, отправляются запросы и ожидается синхронный ответ. Маршрутизатор получает запросы на одностороннюю операцию и сохраняет клиентский канал обратного вызова, чтобы отправить ответ. Тем временем маршрутизатор перенаправляет сообщения, используя дуплексный канал, и обеспечивает канал обратной связи для получения ответа от службы.

 

Рис. 7 Архитектура дуплексного маршрутизатора

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

Реализация маршрутизатора для этого случая показана на рис. 8. Существует несколько соответствующих изменений в реализациях маршрутизатора типа запрос-ответ. Во-первых, маршрутизатор поддерживает сеансы и реализует дуплексный контракт. Когда маршрутизатор перенаправляет сообщения службам, дуплексный канал создается с помощью DuplexChannelFactory<T>, что означает предоставление объекта обратного вызова, который будет получать ответы от службы.

Рис. 8 Реализация дуплексного маршрутизатора

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, 
  ConcurrencyMode = ConcurrencyMode.Multiple,
  AddressFilterMode=AddressFilterMode.Any,
  ValidateMustUnderstand=false)]

public class DuplexRouterService : IDuplexRouterService, IDisposable {
  object m_duplexSessionLock = new object();
  IDuplexRouterService m_duplexSession;

  public void ProcessMessage(Message requestMessage) {
   lock (this.m_duplexSessionLock) {
    if (this.m_duplexSession == null) {
     IDuplexRouterCallback callback =
      OperationContext.Current.GetCallbackChannel
      <IDuplexRouterCallback>();

     DuplexChannelFactory<IDuplexRouterService> factory =
      new DuplexChannelFactory<IDuplexRouterService>
      (new InstanceContext(null,
      new DuplexRouterCallback(callback)), "serviceEndpoint");
     factory.Endpoint.Behaviors.Add(new MustUnderstandBehavior(false));
     this.m_duplexSession = factory.CreateChannel();
    }
   }

   this.m_duplexSession.ProcessMessage(requestMessage);
  }
  public void Dispose() {
   if (this.m_duplexSession != null) {
    try {
     ICommunicationObject obj = this.m_duplexSession as
      ICommunicationObject;
     if (obj.State == CommunicationState.Faulted)
      obj.Abort();
     else
      obj.Close();
    }
    catch {}
   }
  }
}

public class DuplexRouterCallback : IDuplexRouterCallback {

  private IDuplexRouterCallback m_clientCallback;

  public DuplexRouterCallback(IDuplexRouterCallback clientCallback) {
   m_clientCallback = clientCallback;
  }

  public void ProcessMessage(Message requestMessage) {
   this.m_clientCallback.ProcessMessage(requestMessage);
  }
}

Объект обратного вызова реализует контракт обратного вызова, и он будет получать ответы от служб. Этот объект обратного вызова должен использовать клиентский канал обратного вызова для отправки ответа клиенту.

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