Сеансы и очереди

В примере сеанса показано, как отправлять и получать набор связанных сообщений в очереди связи по транспорту очереди сообщений (MSMQ). В этом примере используется привязка netMsmqBinding. Служба представляет собой резидентное консольное приложение, позволяющее наблюдать за получением службой сообщений из очереди.

Примечание.

Процедура настройки и инструкции по построению для данного образца приведены в конце этого раздела.

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

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

В этом образце клиент отправляет ряд сообщений в службу как часть сеанса в рамках одной транзакции.

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

[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();
}

Служба определяет операции службы таким образом, что первая операция зачисляется в транзакцию, но не завершает транзакцию автоматически. Последующие операции также зачисляются в эту же транзакцию, но не производят ее автоматического завершения. Последняя операция в сеансе автоматически завершает транзакцию. Таким образом, одна и та же транзакция используется для вызова нескольких операций в контракте службы. Если любая из операций вызывает исключение, производится откат транзакции и сеанс возвращается в очередь. После успешного завершения последней операции производится фиксация транзакции. В службе значение PerSession параметра InstanceContextMode используется для того, чтобы все сообщения в сеансе получались одним экземпляром службы.

[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());
    }
}

Служба является резидентной. При работе с транспортом MSMQ используемую очередь следует создавать заранее. Это можно сделать вручную или с помощью кода. В данном образце служба содержит код System.Messaging для проверки наличия очереди и ее создания, если требуется. Имя очереди считывается из файла конфигурации с помощью класса AppSettings.

// 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();
    }
}

Имя очереди MSMQ задается в разделе appSettings файла конфигурации. Конечная точка для службы задается в разделе system.serviceModel файла конфигурации и определяет привязку netMsmqBinding.

<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>

Клиент создает область транзакции. Все сообщения в сеансе отправляются в очередь в области транзакции, поэтому она обрабатывается как единый модуль, в котором все сообщения завершаются успехом или сбоем. Транзакция фиксируется путем вызова метода 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();
}

Примечание.

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

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

На клиенте.

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

Press <ENTER> to terminate client.

В службе.

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

Настройка, сборка и запуск примера

  1. Убедитесь, что вы выполнили процедуру однократной установки для примеров Windows Communication Foundation.

  2. Чтобы создать выпуск решения C#, C++или Visual Basic .NET, выполните инструкции по созданию примеров Windows Communication Foundation.

  3. Чтобы запустить пример в конфигурации с одним или несколькими компьютерами, следуйте инструкциям в разделе "Примеры Windows Communication Foundation".

По умолчанию с привязкой NetMsmqBinding безопасность транспорта включена. Существует два важных свойства для безопасности транспорта MSMQ, MsmqAuthenticationMode а MsmqProtectionLevel. по умолчанию — режим Windows проверки подлинности и уровень защиты задан.Sign Чтобы служба MSMQ обеспечивала возможности проверки подлинности и подписывания, она должна входить в домен, а также должна быть установлена возможность интеграции MSMQ со службой каталогов Active Directory. Если запустить этот образец на компьютере, который не удовлетворяет этому условию, возникнет ошибка.

Запуск примера на компьютере, присоединенном к рабочей группе

  1. Если компьютер не входит в домен или не установлена интеграция с Active Directory, отключите безопасность транспорта, задав для режима проверки подлинности и уровня защиты значение None, как показано в следующем образце конфигурации.

    <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. Перед выполнением примера убедитесь, что изменена конфигурация как сервера, так и клиента.

    Примечание.

    Установка для режима безопасности значения None равносильна установке для MsmqAuthenticationMode, MsmqProtectionLevel и безопасности Message значения None.