Создание маршрутизатора WCF - Физическая и логическая адресация

ОГЛАВЛЕНИЕ

Физическая и логическая адресация

При введении маршрутизатора в архитектуру приложения всегда лучше дать клиенту возможность отправлять сообщения, используя верный заголовок To для службы, но отправляя сообщение все же маршрутизатору. Один из способов добиться этого – настроить клиент на использование ClientViaBehavior, как в настройке, показанной на рис. 8. Такая настройка указывает прокси клиента создать сообщение с заголовком To в соответствии с логическим адресом конечной точки, но переслать его через физический адрес маршрутизатора. Проблема в том, что это прикрепляет клиента к существованию маршрутизатора.

Рис. 8 Использование ClientViaBehavior

<client>
  <endpoint address="http://localhost:8000/MessageManagerService"
   binding="wsHttpBinding"  
   bindingConfiguration="wsHttpNoSecurity"
   contract="localhost.IMessageManagerService" 
   name="basicHttp" 
   behaviorConfiguration="viaBehavior"/>
</client>
<behaviors>
  <endpointBehaviors>
   <behavior name="viaBehavior">
    <clientVia viaUri="http://localhost:8010/RouterService"/>
   </behavior>
  </endpointBehaviors>
</behaviors>

Другой способ решить эту проблему – указать службе настроить атрибут listenUri для ее конечных точек, чтобы логический адрес службы совпадал с таковым маршрутизатора, тогда как физический адрес оставался бы собственным. Рассмотрите следующую настройку службы:

<endpoint address="http://localhost:8010/RouterService" 
  contract="MessageManager.IMessageManagerService" 
  binding="wsHttpBinding"
  bindingConfiguration="wsHttpNoSecurity" 
  listenUri="http://localhost:8000/MessageManagerService"/> 

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

Альтернатива – использование службой логического адреса, являющегося типом URI-адреса, не привязанного к маршрутизатору или службе, с последующим указанием вручную клиентам физического адреса для отправки сообщений, поскольку он не будет частью метаданных. Вот пример такой настройки конечной точки:

<endpoint address="urn:MessageManagerService" 
  contract="MessageManager.IMessageManagerService" 
  binding="wsHttpBinding"  
  bindingConfiguration="wsHttpNoSecurity" 
  listenUri="http://localhost:8000/MessageManagerService"/> 

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

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

ServiceBehaviorAttribute поддерживает это через свойство AddressFilterMode, которое может быть установлено на одно из перечислений AddressFilterMode: Exact («Точный») (установка по умолчанию), Prefix («Префикс») или Any («Любой»). Поскольку совпадение префикса маршрутизатора со всеми службами, для которых он получает сообщения, нельзя гарантировать, имеет смысл разрешить передачу всех заголовков To, вот так:

[ServiceBehavior(InstanceContextMode = 
  InstanceContextMode.Single, 
  ConcurrencyMode = ConcurrencyMode.Multiple, 
  AddressFilterMode=AddressFilterMode.Any)]
public class RouterService : IRouterService 

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

В приведенном ниже фрагменте кода показан раздел customBinding, устанавливающий эту функцию для транспортного канала HTTP:

<customBinding>
  <binding name="manualAddressing">
   <textMessageEncoding />
   <httpTransport manualAddressing="true"/>
  </binding>
 </customBinding> 

Это помогает транспортному потоку адресации, показанному на рис. 9, где заголовки не изменены.

 

Рис. 9 Адресация семантики через маршрутизатор с ручной адресацией