Udostępnij za pośrednictwem


Instrukcje: używanie filtrów

W tym temacie opisano podstawowe kroki wymagane do utworzenia konfiguracji routingu, która używa wielu filtrów. W tym przykładzie komunikaty są kierowane do dwóch implementacji usługi kalkulatora, regularnych obliczeń i zaokrąglania. Obie implementacje obsługują te same operacje; jednak jedna usługa zaokrągla wszystkie obliczenia do najbliższej wartości całkowitej przed zwróceniem. Aplikacja kliencka musi mieć możliwość wskazania, czy używać wersji zaokrąglania usługi; jeśli żadna preferencja usługi nie zostanie wyrażona, komunikat jest zrównoważony między dwiema usługami. Operacje uwidocznione przez obie usługi to:

  • Dodaj

  • Subtract

  • Razy

  • Podziel

Ponieważ obie usługi implementują te same operacje, nie można użyć filtru Akcja, ponieważ akcja określona w komunikacie nie będzie unikatowa. Zamiast tego należy wykonać dodatkową pracę, aby upewnić się, że komunikaty są kierowane do odpowiednich punktów końcowych.

Określanie unikatowych danych

  1. Ponieważ obie implementacje usług obsługują te same operacje i są zasadniczo identyczne niż zwracane dane, podstawowe dane zawarte w komunikatach wysyłanych z aplikacji klienckich nie są wystarczająco unikatowe, aby umożliwić określenie sposobu kierowania żądania. Jeśli jednak aplikacja kliencka dodaje unikatową wartość nagłówka do komunikatu, możesz użyć tej wartości, aby określić sposób kierowania komunikatu.

    W tym przykładzie, jeśli aplikacja kliencka wymaga przetworzenia komunikatu przez kalkulator zaokrąglania, dodaje nagłówek niestandardowy przy użyciu następującego kodu:

    messageHeadersElement.Add(MessageHeader.CreateHeader("RoundingCalculator",
                                   "http://my.custom.namespace/", "rounding"));  
    

    Teraz możesz użyć filtru XPath, aby sprawdzić komunikaty dla tego nagłówka i kierować komunikaty zawierające nagłówek do usługi roundCalc.

  2. Ponadto usługa routingu uwidacznia dwa punkty końcowe usługi wirtualnej, których można używać z parametrami EndpointName, EndpointAddress lub PrefixEndpointAddress w celu unikatowego kierowania komunikatów przychodzących do określonej implementacji kalkulatora na podstawie punktu końcowego, do którego aplikacja kliencka przesyła żądanie.

Definiowanie punktów końcowych

  1. Podczas definiowania punktów końcowych używanych przez usługę routingu należy najpierw określić kształt kanału używanego przez klientów i usługi. W tym scenariuszu usługi docelowe używają wzorca żądania-odpowiedzi, więc IRequestReplyRouter jest używany. W poniższym przykładzie zdefiniowano punkty końcowe usługi uwidocznione przez usługę routingu.

    <services>  
          <service behaviorConfiguration="routingConfiguration"  
                   name="System.ServiceModel.Routing.RoutingService">  
            <host>  
              <baseAddresses>  
                <add baseAddress="http://localhost/routingservice/router" />  
              </baseAddresses>  
            </host>  
            <!--Set up the inbound endpoints for the Routing Service-->  
            <!--first create the general router endpoint-->  
            <endpoint address="general"  
                      binding="wsHttpBinding"  
                      name="routerEndpoint"  
                      contract="System.ServiceModel.Routing.IRequestReplyRouter" />  
            <!--create a virtual endpoint for the regular calculator service-->  
            <endpoint address="regular/calculator"  
                      binding="wsHttpBinding"  
                      name="calculatorEndpoint"  
                      contract="System.ServiceModel.Routing.IRequestReplyRouter" />  
            <!--now create a virtual endpoint for the rounding calculator-->  
            <endpoint address="rounding/calculator"  
                      binding="wsHttpBinding"  
                      name="roundingEndpoint"  
                      contract="System.ServiceModel.Routing.IRequestReplyRouter" />  
    
          </service>  
    </services>  
    

    Dzięki tej konfiguracji usługa routingu uwidacznia trzy oddzielne punkty końcowe. W zależności od opcji czasu wykonywania aplikacja kliencka wysyła komunikaty do jednego z tych adresów. Komunikaty przychodzące do jednego z "wirtualnych" punktów końcowych usługi ("zaokrąglanie/kalkulator" lub "zwykłe/kalkulator") są przekazywane do odpowiedniej implementacji kalkulatora. Jeśli aplikacja kliencka nie wyśle żądania do określonego punktu końcowego, komunikat zostanie skierowany do ogólnego punktu końcowego. Niezależnie od wybranego punktu końcowego aplikacja kliencka może również dołączyć nagłówek niestandardowy, aby wskazać, że komunikat powinien zostać przekazany do implementacji kalkulatora zaokrąglania.

  2. W poniższym przykładzie zdefiniowano punkty końcowe klienta (miejsca docelowego), do których usługa routingu kieruje komunikaty.

    <client>  
          <endpoint name="regularCalcEndpoint"  
                    address="net.tcp://localhost:9090/servicemodelsamples/service/"  
                    binding="netTcpBinding"  
                    contract="*" />  
    
          <endpoint name="roundingCalcEndpoint"  
                    address="net.tcp://localhost:8080/servicemodelsamples/service/"  
                    binding="netTcpBinding"  
                    contract="*" />  
    </client>  
    

    Te punkty końcowe są używane w tabeli filtrów, aby wskazać docelowy punkt końcowy, do którego jest wysyłany komunikat, gdy pasuje do określonego filtru.

Definiowanie filtrów

  1. Aby kierować komunikaty na podstawie niestandardowego nagłówka "RoundingCalculator", który aplikacja kliencka dodaje do komunikatu, zdefiniuj filtr używający zapytania XPath w celu sprawdzenia obecności tego nagłówka. Ponieważ ten nagłówek jest definiowany przy użyciu niestandardowej przestrzeni nazw, dodaj również wpis przestrzeni nazw, który definiuje niestandardowy prefiks przestrzeni nazw "niestandardowy", który jest używany w zapytaniu XPath. W poniższym przykładzie zdefiniowano niezbędną sekcję routingu, tabelę przestrzeni nazw i filtr XPath.

    <routing>  
          <!-- use the namespace table element to define a prefix for our custom namespace-->  
          <namespaceTable>  
            <add prefix="custom" namespace="http://my.custom.namespace/"/>  
          </namespaceTable>  
          <filters>  
            <!--define the different message filters-->  
            <!--define an xpath message filter to look for the custom header coming from the client-->  
            <filter name="XPathFilter" filterType="XPath"
                    filterData="/s12:Envelope/s12:Header/custom:RoundingCalculator = 'rounding'"/>  
          </filters>  
    </routing>  
    

    Ten element MessageFilter wyszukuje nagłówek RoundingCalculator w komunikacie zawierającym wartość "zaokrąglania". Ten nagłówek jest ustawiany przez klienta, aby wskazać, że komunikat powinien być kierowany do usługi roundingCalc.

    Uwaga

    Prefiks przestrzeni nazw s12 jest definiowany domyślnie w tabeli przestrzeni nazw i reprezentuje przestrzeń http://www.w3.org/2003/05/soap-envelopenazw .

  2. Należy również zdefiniować filtry, które wyszukują komunikaty odebrane w dwóch wirtualnych punktach końcowych. Pierwszy wirtualny punkt końcowy to punkt końcowy "zwykły/kalkulator". Klient może wysyłać żądania do tego punktu końcowego, aby wskazać, że komunikat powinien być kierowany do usługi regularCalc. Poniższa konfiguracja definiuje filtr, który używa EndpointNameMessageFilter elementu , aby określić, czy komunikat dotarł przez punkt końcowy o nazwie określonej w filterData.

    <!--define an endpoint name filter looking for messages that show up on the virtual regular calculator endpoint-->  
    <filter name="EndpointNameFilter" filterType="EndpointName" filterData="calculatorEndpoint"/>  
    

    Jeśli komunikat zostanie odebrany przez punkt końcowy usługi o nazwie "calculatorEndpoint", ten filtr zwróci wartość true.

  3. Następnie zdefiniuj filtr, który wyszukuje komunikaty wysyłane pod adres zaokrągleniaEndpoint. Klient może wysyłać żądania do tego punktu końcowego, aby wskazać, że komunikat powinien być kierowany do usługi roundingCalc. Poniższa konfiguracja definiuje filtr, który używa PrefixEndpointAddressMessageFilter elementu w celu określenia, czy komunikat dotarł do punktu końcowego "zaokrąglania/kalkulatora".

    <!--define a filter looking for messages that show up with the address prefix.  The corresponds to the rounding calc virtual endpoint-->  
    <filter name="PrefixAddressFilter" filterType="PrefixEndpointAddress"  
            filterData="http://localhost/routingservice/router/rounding/"/>  
    

    Jeśli komunikat zostanie odebrany pod adresem rozpoczynającym się od http://localhost/routingservice/router/rounding/ , ten filtr zwróci wartość true. Ponieważ adres podstawowy używany przez tę konfigurację to http://localhost/routingservice/router i adres określony dla roundingEndpoint to "zaokrąglanie/kalkulator", pełny adres używany do komunikowania się z tym punktem końcowym to http://localhost/routingservice/router/rounding/calculator, który pasuje do tego filtru.

    Uwaga

    Filtr PrefixEndpointAddress nie ocenia nazwy hosta podczas wykonywania dopasowania, ponieważ można odwoływać się do jednego hosta przy użyciu różnych nazw hostów, które mogą być prawidłowymi sposobami odwoływania się do hosta z aplikacji klienckiej. Na przykład wszystkie następujące elementy mogą odwoływać się do tego samego hosta:

    • localhost
    • 127.0.0.1
    • www.contoso.com
    • ContosoWeb01
  4. Końcowy filtr musi obsługiwać routing komunikatów, które docierają do ogólnego punktu końcowego bez nagłówka niestandardowego. W tym scenariuszu komunikaty powinny być alternatywne między zwykłymi usługamiCalc i RoundingCalc. Aby obsługiwać routing "działania okrężnego" tych komunikatów, użyj filtru niestandardowego, który umożliwia dopasowanie jednego wystąpienia filtru dla każdego przetworzonego komunikatu. Poniżej zdefiniowano dwa wystąpienia klasy RoundRobinMessageFilter, które są pogrupowane razem, aby wskazać, że powinny się one naprzemiennie między sobą.

    <!-- Set up the custom message filters.  In this example,   
         we'll use the example round robin message filter,   
         which alternates between the references-->  
    <filter name="RoundRobinFilter1" filterType="Custom"  
                    customType="CustomFilterAssembly.RoundRobinMessageFilter, CustomFilterAssembly"  
                    filterData="group1"/>  
    <filter name="RoundRobinFilter2" filterType="Custom"  
                    customType="CustomFilterAssembly.RoundRobinMessageFilter, CustomFilterAssembly"  
                    filterData="group1"/>  
    

    W czasie wykonywania ten typ filtru jest alternatywny między wszystkimi zdefiniowanymi wystąpieniami filtru tego typu skonfigurowanymi jako ta sama grupa w jednej kolekcji. Powoduje to przetworzenie komunikatów przez ten filtr niestandardowy na alternatywę między zwracaniem true elementu i RoundRobinFilter1RoundRobinFilter2.

Definiowanie tabel filtru

  1. Aby skojarzyć filtry z określonymi punktami końcowymi klienta, należy umieścić je w tabeli filtrów. W tym przykładowym scenariuszu są również używane ustawienia priorytetu filtru, które jest opcjonalnym ustawieniem, które umożliwia wskazanie kolejności przetwarzania filtrów. Jeśli nie określono priorytetu filtru, wszystkie filtry są oceniane jednocześnie.

    Uwaga

    Podczas określania priorytetu filtru można kontrolować kolejność przetwarzania filtrów, może ona niekorzystnie wpłynąć na wydajność usługi routingu. Jeśli to możliwe, należy utworzyć logikę filtru, aby użycie priorytetów filtru nie było wymagane.

    Poniżej zdefiniowano tabelę filtrów i dodano definicję "XPathFilter" zdefiniowaną wcześniej do tabeli z priorytetem 2. Ten wpis określa również, że jeśli XPathFilter komunikat pasuje do komunikatu, komunikat zostanie przekierowany do roundingCalcEndpoint.

    <routing>  
    ...      <filters>  
    ...      </filters>  
          <filterTables>  
            <table name="filterTable1">  
              <entries>  
                <!--add the filters to the message filter table-->  
                <!--first look for the custom header, and if we find it,  
                    send the message to the rounding calc endpoint-->  
                <add filterName="XPathFilter" endpointName="roundingCalcEndpoint" priority="2"/>  
              </entries>  
            </table>  
          </filterTables>  
    </routing>  
    

    Podczas określania priorytetu filtru najpierw oceniane są filtry o najwyższym priorytcie. Jeśli co najmniej jeden filtr na określonym poziomie priorytetu jest zgodny, nie zostaną ocenione żadne filtry na niższych poziomach priorytetu. W tym scenariuszu 2 jest najwyższym priorytetem określonym i jest to jedyny wpis filtru na tym poziomie.

  2. Zdefiniowano wpisy filtru, aby sprawdzić, czy komunikat został odebrany w określonym punkcie końcowym, sprawdzając nazwę punktu końcowego lub prefiks adresu. Poniższe wpisy dodają oba te wpisy filtru do tabeli filtrów i skojarzą je z docelowymi punktami końcowymi, do których będzie kierowany komunikat. Te filtry są ustawione na priorytet 1, aby wskazać, że powinny być uruchamiane tylko wtedy, gdy poprzedni filtr XPath nie był zgodny z komunikatem.

    <!--if the header wasn't there, send the message based on which virtual endpoint it arrived at-->  
    <!--we determine this through the endpoint name, or through the address prefix-->  
    <add filterName="EndpointNameFilter" endpointName="regularCalcEndpoint" priority="1"/>  
    <add filterName="PrefixAddressFilter" endpointName="roundingCalcEndpoint" priority="1"/>  
    

    Ponieważ te filtry mają priorytet filtru 1, będą oceniane tylko wtedy, gdy filtr na poziomie priorytetu 2 nie jest zgodny z komunikatem. Ponadto, ponieważ oba filtry mają ten sam poziom priorytetu, zostaną ocenione jednocześnie. Ponieważ oba filtry wykluczają się wzajemnie, możliwe jest, aby tylko jeden lub drugi był zgodny z komunikatem.

  3. Jeśli komunikat nie jest zgodny z żadnym z poprzednich filtrów, komunikat został odebrany za pośrednictwem ogólnego punktu końcowego usługi i nie zawierał żadnych informacji nagłówka wskazujących, gdzie go kierować. Te komunikaty mają być obsługiwane przez filtr niestandardowy, który równoważy ich obciążenie między dwiema usługami kalkulatora. W poniższym przykładzie pokazano, jak dodać wpisy filtru do tabeli filtrów; każdy filtr jest skojarzony z jednym z dwóch docelowych punktów końcowych.

    <!--if none of the other filters have matched,   
        this message showed up on the default router endpoint,   
        with no custom header-->  
    <!--round robin these requests between the two services-->  
    <add filterName="RoundRobinFilter1" endpointName="regularCalcEndpoint" priority="0"/>  
    <add filterName="RoundRobinFilter2" endpointName="roundingCalcEndpoint" priority="0"/>  
    

    Ponieważ te wpisy określają priorytet 0, będą oceniane tylko wtedy, gdy żaden filtr wyższego priorytetu nie pasuje do komunikatu. Ponadto, ponieważ oba te elementy mają ten sam priorytet, są oceniane jednocześnie.

    Jak wspomniano wcześniej, niestandardowy filtr używany przez te definicje filtrów oblicza tylko jeden lub drugi, jak true dla każdego odebranego komunikatu. Ponieważ tylko dwa filtry są definiowane przy użyciu tego filtru, z tym samym określonym ustawieniem grupy, skutek polega na tym, że usługa routingu zmienia się między wysyłaniem do zwykłego punktu obliczeniowego i zaokrąglaniacalcEndpoint.

  4. Aby ocenić komunikaty względem filtrów, tabela filtrów musi najpierw być skojarzona z punktami końcowymi usługi, które będą używane do odbierania komunikatów. W poniższym przykładzie pokazano, jak skojarzyć tabelę routingu z punktami końcowymi usługi przy użyciu zachowania routingu:

    <behaviors>  
      <!--default routing service behavior definition-->  
      <serviceBehaviors>  
        <behavior name="routingConfiguration">  
          <routing filterTableName="filterTable1" />  
        </behavior>  
      </serviceBehaviors>  
    </behaviors>  
    

Przykład

Poniżej znajduje się pełna lista pliku konfiguracji.

<?xml version="1.0" encoding="utf-8" ?>  
<!-- Copyright (c) Microsoft Corporation. All rights reserved -->  
<configuration>  
  <system.serviceModel>  
    <services>  
      <service behaviorConfiguration="routingConfiguration"  
               name="System.ServiceModel.Routing.RoutingService">  
        <host>  
          <baseAddresses>  
            <add baseAddress="http://localhost/routingservice/router" />  
          </baseAddresses>  
        </host>  
        <!--Set up the inbound endpoints for the Routing Service-->  
        <!--first create the general router endpoint-->  
        <endpoint address="general"  
                  binding="wsHttpBinding"  
                  name="routerEndpoint"  
                  contract="System.ServiceModel.Routing.IRequestReplyRouter" />  
        <!--create a virtual endpoint for the regular calculator service-->  
        <endpoint address="regular/calculator"  
                  binding="wsHttpBinding"  
                  name="calculatorEndpoint"  
                  contract="System.ServiceModel.Routing.IRequestReplyRouter" />  
        <!--now create a virtual endpoint for the rounding calculator-->  
        <endpoint address="rounding/calculator"  
                  binding="wsHttpBinding"  
                  name="roundingEndpoint"  
                  contract="System.ServiceModel.Routing.IRequestReplyRouter" />  
  
      </service>  
    </services>  
    <behaviors>  
      <!--default routing service behavior definition-->  
      <serviceBehaviors>  
        <behavior name="routingConfiguration">  
          <routing filterTableName="filterTable1" />  
        </behavior>  
      </serviceBehaviors>  
    </behaviors>  
  
    <client>  
<!--set up the destination endpoints-->  
      <endpoint name="regularCalcEndpoint"  
                address="net.tcp://localhost:9090/servicemodelsamples/service/"  
                binding="netTcpBinding"  
                contract="*" />  
  
      <endpoint name="roundingCalcEndpoint"  
                address="net.tcp://localhost:8080/servicemodelsamples/service/"  
                binding="netTcpBinding"  
                contract="*" />  
    </client>  
    <routing>  
      <!-- use the namespace table element to define a prefix for our custom namespace-->  
      <namespaceTable>  
        <add prefix="custom" namespace="http://my.custom.namespace/"/>  
      </namespaceTable>  
      <filters>  
        <!--define the different message filters-->  
        <!--define an xpath message filter to look for the custom header coming from the client-->  
        <filter name="XPathFilter" filterType="XPath" filterData="/s12:Envelope/s12:Header/custom:RoundingCalculator = 'rounding'"/>  
  
        <!--define an endpoint name filter looking for messages that show up on the virtual regular calculator endpoint-->  
        <filter name="EndpointNameFilter" filterType="EndpointName" filterData="calculatorEndpoint"/>  
  
        <!--define a filter looking for messages that show up with the address prefix.  The corresponds to the rounding calc virtual endpoint-->  
        <filter name="PrefixAddressFilter" filterType="PrefixEndpointAddress" filterData="http://localhost/routingservice/router/rounding/"/>  
  
        <!--Set up the custom message filters.  In this example, we'll use the example round robin message filter, which alternates between the references-->  
        <filter name="RoundRobinFilter1" filterType="Custom" customType="CustomFilterAssembly.RoundRobinMessageFilter, CustomFilterAssembly" filterData="group1"/>  
        <filter name="RoundRobinFilter2" filterType="Custom" customType="CustomFilterAssembly.RoundRobinMessageFilter, CustomFilterAssembly" filterData="group1"/>  
      </filters>  
      <filterTables>  
        <table name="filterTable1">  
          <entries>  
            <!--add the filters to the message filter table-->  
            <!--first look for the custom header, and if we find it, send the message to the rounding calc endpoint-->  
            <add filterName="XPathFilter" endpointName="roundingCalcEndpoint" priority="2"/>  
  
            <!--if the header wasn't there, send the message based on which virtual endpoint it arrived at-->  
            <!--we determine this through the endpoint name, or through the address prefix-->  
            <add filterName="EndpointNameFilter" endpointName="regularCalcEndpoint" priority="1"/>  
            <add filterName="PrefixAddressFilter" endpointName="roundingCalcEndpoint" priority="1"/>  
  
            <!--if none of the other filters have matched, this message showed up on the default router endpoint, with no custom header-->  
            <!--round robin these requests between the two services-->  
            <add filterName="RoundRobinFilter1" endpointName="regularCalcEndpoint" priority="0"/>  
            <add filterName="RoundRobinFilter2" endpointName="roundingCalcEndpoint" priority="0"/>  
          </entries>  
        </table>  
      </filterTables>  
    </routing>  
  </system.serviceModel>  
</configuration>  

Zobacz też