Udostępnij za pośrednictwem


Sesje i kolejki

Przykład sesji pokazuje, jak wysyłać i odbierać zestaw powiązanych komunikatów w komunikacji w kolejce za pośrednictwem transportu kolejkowania komunikatów (MSMQ). W tym przykładzie użyto netMsmqBinding powiązania. Usługa jest aplikacją konsolową self-hosted, aby umożliwić obserwowanie usługi odbierających komunikaty w kolejce.

Uwaga

Procedura instalacji i instrukcje kompilacji dla tego przykładu znajdują się na końcu tego tematu.

W komunikacji w kolejce klient komunikuje się z usługą przy użyciu kolejki. Mówiąc dokładniej, klient wysyła komunikaty do kolejki. Usługa odbiera komunikaty z kolejki. W związku z tym usługa i klient nie muszą być uruchomione w tym samym czasie, aby komunikować się przy użyciu kolejki.

Czasami klient wysyła zestaw komunikatów, które są ze sobą powiązane w grupie. Gdy komunikaty muszą być przetwarzane razem lub w określonej kolejności, kolejka może służyć do grupowania ich w celu przetworzenia przez jedną aplikację odbieraną. Jest to szczególnie ważne, gdy istnieje kilka aplikacji odbierających w grupie serwerów i należy upewnić się, że grupa komunikatów jest przetwarzana przez tę samą aplikację odbierającą. Sesje w kolejce to mechanizm służący do wysyłania i odbierania powiązanego zestawu komunikatów, które muszą zostać przetworzone jednocześnie. Sesje w kolejce wymagają transakcji, aby pokazać ten wzorzec.

W przykładzie klient wysyła do usługi kilka komunikatów w ramach sesji w zakresie pojedynczej transakcji.

Kontrakt usługi to IOrderTaker, który definiuje jednokierunkową usługę, która jest odpowiednia do użycia z kolejkami. Użyty SessionMode w kontrakcie pokazanym w poniższym przykładowym kodzie wskazuje, że komunikaty są częścią sesji.

[ServiceContract(Namespace = "http://Microsoft.ServiceModel.Samples", SessionMode=SessionMode.Required)]
public interface IOrderTaker
{
    [OperationContract(IsOneWay = true)]
    void OpenPurchaseOrder(string customerId);

    [OperationContract(IsOneWay = true)]
    void AddProductLineItem(string productId, int quantity);

    [OperationContract(IsOneWay = true)]
    void EndPurchaseOrder();
}

Usługa definiuje operacje usługi w taki sposób, aby pierwsza operacja zarejestrowała się w transakcji, ale nie kończy transakcji automatycznie. Kolejne operacje są również wykonywane w tej samej transakcji, ale nie są automatycznie wykonywane. Ostatnia operacja w sesji automatycznie kończy transakcję. W związku z tym ta sama transakcja jest używana w przypadku kilku wywołań operacji w kontrakcie usługi. Jeśli którykolwiek z operacji zgłasza wyjątek, transakcja zostanie wycofana, a sesja zostanie ponownie umieszczona w kolejce. Po pomyślnym zakończeniu ostatniej operacji transakcja zostanie zatwierdzona. Usługa używa PerSession jako elementu InstanceContextMode , aby odbierać wszystkie komunikaty w sesji w tym samym wystąpieniu usługi.

[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class OrderTakerService : IOrderTaker
{
    PurchaseOrder po;

    [OperationBehavior(TransactionScopeRequired = true,
                                 TransactionAutoComplete = false)]
    public void OpenPurchaseOrder(string customerId)
    {
        Console.WriteLine("Creating purchase order");
        po = new PurchaseOrder(customerId);
    }

    [OperationBehavior(TransactionScopeRequired = true,
                                  TransactionAutoComplete = false)]
    public void AddProductLineItem(string productId, int quantity)
    {
        po.AddProductLineItem(productId, quantity);
        Console.WriteLine("Product " + productId + " quantity " +
                            quantity + " added to purchase order");
    }

    [OperationBehavior(TransactionScopeRequired = true,
                                  TransactionAutoComplete = true)]
    public void EndPurchaseOrder()
    {
       Console.WriteLine("Purchase Order Completed");
       Console.WriteLine();
       Console.WriteLine(po.ToString());
    }
}

Usługa jest hostowana samodzielnie. W przypadku korzystania z transportu MSMQ używana kolejka musi zostać utworzona z wyprzedzeniem. Można to zrobić ręcznie lub za pomocą kodu. W tym przykładzie usługa zawiera System.Messaging kod umożliwiający sprawdzenie istnienia kolejki i utworzenie jej w razie potrzeby. Nazwa kolejki jest odczytywana z pliku konfiguracji przy użyciu AppSettings klasy .

// Host the service within this EXE console application.
public static void Main()
{
    // Get MSMQ queue name from app settings in configuration.
    string queueName = ConfigurationManager.AppSettings["queueName"];

    // Create the transacted MSMQ queue if necessary.
    if (!MessageQueue.Exists(queueName))
        MessageQueue.Create(queueName, true);

    // Create a ServiceHost for the OrderTakerService type.
    using (ServiceHost serviceHost = new ServiceHost(typeof(OrderTakerService)))
    {
        // Open the ServiceHost to create listeners and start listening for messages.
        serviceHost.Open();

        // The service can now be accessed.
        Console.WriteLine("The service is ready.");
        Console.WriteLine("Press <ENTER> to terminate service.");
        Console.WriteLine();
        Console.ReadLine();

        // Close the ServiceHost to shutdown the service.
        serviceHost.Close();
    }
}

Nazwa kolejki MSMQ jest określona w sekcji aplikacji Ustawienia pliku konfiguracji. Punkt końcowy usługi jest zdefiniowany w sekcji system.serviceModel pliku konfiguracji i określa netMsmqBinding powiązanie.

<appSettings>
  <!-- Use appSetting to configure MSMQ queue name. -->
  <add key="queueName" value=".\private$\ServiceModelSamplesSession" />
</appSettings>

<system.serviceModel>
  <services>
    <service name="Microsoft.ServiceModel.Samples.OrderTakerService"
        behaviorConfiguration="CalculatorServiceBehavior">
      ...
      <!-- Define NetMsmqEndpoint -->
      <endpoint address="net.msmq://localhost/private/ServiceModelSamplesSession"
                binding="netMsmqBinding"
                contract="Microsoft.ServiceModel.Samples.IOrderTaker" />
      ...
    </service>
  </services>
  ...
</system.serviceModel>

Klient tworzy zakres transakcji. Wszystkie komunikaty w sesji są wysyłane do kolejki w zakresie transakcji, co powoduje, że jest traktowana jako jednostka niepodzielna, w której wszystkie komunikaty kończą się powodzeniem lub niepowodzeniem. Transakcja jest zatwierdzana przez wywołanie metody Complete.

//Create a transaction scope.
using (TransactionScope scope = new TransactionScope(TransactionScopeOption.Required))
{
    // Create a client with given client endpoint configuration.
    OrderTakerClient client = new OrderTakerClient("OrderTakerEndpoint");
    // Open a purchase order.
    client.OpenPurchaseOrder("somecustomer.com");
    Console.WriteLine("Purchase Order created");

    // Add product line items.
    Console.WriteLine("Adding 10 quantities of blue widget");
    client.AddProductLineItem("Blue Widget", 10);

    Console.WriteLine("Adding 23 quantities of red widget");
    client.AddProductLineItem("Red Widget", 23);

    // Close the purchase order.
    Console.WriteLine("Closing the purchase order");
    client.EndPurchaseOrder();

    //Closing the client gracefully closes the connection and cleans up resources.
    client.Close();

    // Complete the transaction.
    scope.Complete();
}

Uwaga

Można użyć tylko jednej transakcji dla wszystkich komunikatów w sesji, a wszystkie komunikaty w sesji muszą być wysyłane przed zatwierdzeniem transakcji. Zamknięcie klienta zamyka sesję. W związku z tym klient musi zostać zamknięty przed zakończeniem transakcji w celu wysłania wszystkich komunikatów w sesji do kolejki.

Po uruchomieniu przykładu działania klienta i usługi są wyświetlane zarówno w oknach konsoli usługi, jak i klienta. Usługa odbiera komunikaty od klienta. Naciśnij klawisz ENTER w każdym oknie konsoli, aby zamknąć usługę i klienta. Ponieważ kolejkowanie jest używane, klient i usługa nie muszą być uruchomione w tym samym czasie. Możesz uruchomić klienta, zamknąć go, a następnie uruchomić usługę i nadal odbierać komunikaty.

Na kliencie.

Purchase Order created
Adding 10 quantities of blue widget
Adding 23 quantities of red widget
Closing the purchase order

Press <ENTER> to terminate client.

W usłudze.

The service is ready.
Press <ENTER> to terminate service.

Creating purchase order
Product Blue Widget quantity 10 added to purchase order
Product Red Widget quantity 23 added to purchase order
Purchase Order Completed

Purchase Order: 7c86fef0-2306-4c51-80e6-bcabcc1a6e5e
        Customer: somecustomer.com
        OrderDetails
                Order LineItem: 10 of Blue Widget @unit price: $2985
                Order LineItem: 23 of Red Widget @unit price: $156
        Total cost of this order: $33438
        Order status: Pending

Konfigurowanie, kompilowanie i uruchamianie przykładu

  1. Upewnij się, że wykonano procedurę instalacji jednorazowej dla przykładów programu Windows Communication Foundation.

  2. Aby skompilować wersję rozwiązania W#, C++lub Visual Basic .NET, postępuj zgodnie z instrukcjami w temacie Kompilowanie przykładów programu Windows Communication Foundation.

  3. Aby uruchomić przykład w konfiguracji pojedynczej lub między maszynami, postępuj zgodnie z instrukcjami w temacie Uruchamianie przykładów programu Windows Communication Foundation.

Domyślnie w systemie NetMsmqBindingsą włączone zabezpieczenia transportu. Istnieją dwie istotne właściwości zabezpieczeń transportu MSMQ, MsmqAuthenticationMode a.MsmqProtectionLeveldomyślnie tryb uwierzytelniania jest ustawiony na Windows , a poziom ochrony jest ustawiony na Sign. Aby usługa MSMQ zapewniała funkcję uwierzytelniania i podpisywania, musi być częścią domeny, a opcja integracji usługi Active Directory dla usługi MSMQ musi być zainstalowana. Jeśli uruchomisz ten przykład na komputerze, który nie spełnia tych kryteriów, zostanie wyświetlony błąd.

Uruchamianie przykładu na komputerze przyłączonym do grupy roboczej

  1. Jeśli komputer nie jest częścią domeny lub nie ma zainstalowanej integracji z usługą Active Directory, wyłącz zabezpieczenia transportu, ustawiając tryb uwierzytelniania i poziom ochrony, tak None jak pokazano w poniższej przykładowej konfiguracji.

    <system.serviceModel>
      <services>
        <service name="Microsoft.ServiceModel.Samples.OrderTakerService"
                 behaviorConfiguration="OrderTakerServiceBehavior">
          <host>
            <baseAddresses>
              <add baseAddress=
             "http://localhost:8000/ServiceModelSamples/service"/>
            </baseAddresses>
          </host>
          <!-- Define NetMsmqEndpoint -->
          <endpoint
              address=
            "net.msmq://localhost/private/ServiceModelSamplesSession"
              binding="netMsmqBinding"
              bindingConfiguration="Binding1"
           contract="Microsoft.ServiceModel.Samples.IOrderTaker" />
          <!-- The mex endpoint is exposed at-->
          <!--http://localhost:8000/ServiceModelSamples/service/mex. -->
          <endpoint address="mex"
                    binding="mexHttpBinding"
                    contract="IMetadataExchange" />
        </service>
      </services>
    
      <bindings>
        <netMsmqBinding>
          <binding name="Binding1">
            <security mode="None" />
          </binding>
        </netMsmqBinding>
      </bindings>
    
        <behaviors>
          <serviceBehaviors>
            <behavior name="OrderTakerServiceBehavior">
              <serviceMetadata httpGetEnabled="True"/>
            </behavior>
          </serviceBehaviors>
        </behaviors>
    
      </system.serviceModel>
    
  2. Przed uruchomieniem przykładu upewnij się, że zmienisz konfigurację zarówno na serwerze, jak i na kliencie.

    Uwaga

    Ustawienie trybu None zabezpieczeń na wartość jest równoważne ustawieniu MsmqAuthenticationModewartości , MsmqProtectionLeveli Message zabezpieczeń na Nonewartość .