Follow Me Widget

вторник, 6 декабря 2011 г.

App Fabric Service Bus. Ретрансляция сообщений на примере Echo-сервиса.

Всем привет ! Мы продолжаем знакомиться с технологией от Microsoft под названием App Fabric Service Bus. Данная публикация является уже второй по счету, посвященной этому механизму. В первой я напомню мы познакомились с определением и основными сценариями применения технологии, а также узнали как создать свой первый неймспейс. Посмотреть ее можно вот по этой ссылке: Введение в AppFabric Service Bus. Сценарии применения.Сегодня пост будет более техническим – попытаемся создать и использовать WCF-сервис посредством Service Bus.
Итак, задача стоит следующая: построить WCF-сервис, содержащий один единственный метод. Метод будет принимать строковый параметр и сразу же возвращать его клиенту. Захостить этот сервис постараемся на сервисной шине. То есть по факту Service Bus будет выступать как ретранслятор сообщений между клиентом и WCF-сервисом.Для решения поставленной задачи был создан новый солюшен со следующими проектами: EchoService.Client, EchoService.Server, EchoService.Source. В студии это выглядит следующим образом:
Echo service solution architecture
EchoService.Client представляет собой консольное приложение, через которое пользователь может отправлять сообщения на сервер. EchoService.Server – это наша серверная часть (также консольное приложение), на базе которой будет открываться WCF-хост, запускающий необходимый нам WCF-сервис. Так как и клиенту и серверу необходим контракт WCF-сервиса – мы поместили его в совместно используемую сборку под названием EchoService.Source. В данной сборке находится один единственный интерфейс, код которого представлен ниже.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;

namespace EchoService.Source
{
    [ServiceContract(Name = "EchoContract", Namespace = "http://samples.microsoft.com/ServiceModel/Relay/")]
    public interface IEchoContract
    {
        [OperationContract] 
        string Echo(string message);
    }
}

Ничего необычного – стандартный интерфейс с одним единственным методом Echo, принимающий и возвращающий строковый параметр. В сборке EchoService.Server находится реализация данного интерфейса – это класс под названием EchoServer – его код также представлен чуть ниже.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using EchoService.Source;
using System.ServiceModel;

namespace EchoService.Server
{
    [ServiceBehavior(Name = "EchoService", Namespace = "http://samples.microsoft.com/ServiceModel/Relay/")]
    public class EchoServer : IEchoContract
    {
        public string Echo(string message)
        {
            Console.WriteLine("Received: {0}", message);
            return message;
        }
    }
}

Здесь также все стандартно – класс реализует интерфейс IEchoService и помечается атрибутом ServiceBehavior (тем самым класс помечается как реализация WCF-сервиса). Метод Echo помимо того, что возвращает пришедшую в параметре строку обратно клиенту, также выводит ее в консоль сервера. Основная же функциональность сервера заключена в файле Program.cs. Давайте посмотрим на его содержимое:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.ServiceBus;
using System.ServiceModel;
using System.ServiceModel.Description;

namespace EchoService.Server
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.Title = "Сервер";
            Console.Write("Введите Service Bus неймспейс: ");
            string serviceNamespaceDomain = Console.ReadLine();
            Console.Write("Issuer Name: ");
            string issuerName = Console.ReadLine();
            Console.Write("Issuer Secret: ");
            string issuerSecret = Console.ReadLine();

            Uri address = ServiceBusEnvironment.CreateServiceUri("sb", serviceNamespaceDomain, "EchoService");

            TransportClientEndpointBehavior sharedSecretServiceBusCredential = new TransportClientEndpointBehavior();
            sharedSecretServiceBusCredential.CredentialType = TransportClientCredentialType.SharedSecret;
            sharedSecretServiceBusCredential.Credentials.SharedSecret.IssuerName = issuerName;
            sharedSecretServiceBusCredential.Credentials.SharedSecret.IssuerSecret = issuerSecret;

            ServiceHost host = new ServiceHost(typeof(EchoServer), address);
            IEndpointBehavior serviceRegistrySettings = new ServiceRegistrySettings(DiscoveryType.Public);
            foreach (ServiceEndpoint endpoint in host.Description.Endpoints)
            {
                endpoint.Behaviors.Add(sharedSecretServiceBusCredential);
            }
            host.Open();
            Console.WriteLine("Адрес сервиса: " + address);
            Console.WriteLine("Нажмите [Enter] для выхода");
            Console.ReadLine();
            host.Close();
        }
    }
}

Давайте более подробно разберемся что же происходит. Итак, во-первых для запуска сервиса необходимы входные данные, а именно криденшалы для доступа к Service Bus и название неймспейса. Они то как раз и собираются в первых строчках программы. После этого создается уникальный всеобще доступный адрес сервиса (на выходе мы получим вот такого формата адрес: sb://<YOUR-NAMESPACE>.servicebus.windows.net/EchoService). Для доступа к Service Bus все клиенты должны быть аутентифицированными. В нашем случае используется так называемая аутентификация  SharedSecret и происходит это следующем сегменте кода:
           
            TransportClientEndpointBehavior sharedSecretServiceBusCredential = new TransportClientEndpointBehavior();
            sharedSecretServiceBusCredential.CredentialType = TransportClientCredentialType.SharedSecret;
            sharedSecretServiceBusCredential.Credentials.SharedSecret.IssuerName = issuerName;
            sharedSecretServiceBusCredential.Credentials.SharedSecret.IssuerSecret = issuerSecret;

Данный способ аутентификации похож на стандартные схемы, при которых передается пара логин/пароль (кроме того поддерживаются следующие схемы аутентификации: Saml, SimpleWebToken и Unauthenticated). Ну и в конце концов при помощи стандартного инструментария из сборки System.ServiceModel.dll создается WCF-хост и запускается наш Echo-сервис (конечно же при этом для каждой точки доступа устанавливается созданное ранее поведение, гарантирующее возможность подключения лишь аутентифицированных пользователей).

Давайте теперь взглянем на исходный код клиента.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.ServiceBus;
using System.ServiceModel;
using EchoService.Source;

namespace EchoService.Client
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.Title = "Клиент";
            Console.Write("Введите Service Bus неймспейс: ");
            string serviceNamespaceDomain = Console.ReadLine();
            Console.Write("Issuer Name: ");
            string issuerName = Console.ReadLine();
            Console.Write("Issuer Secret: ");
            string issuerSecret = Console.ReadLine();

            Uri serviceUri = ServiceBusEnvironment.CreateServiceUri("sb", serviceNamespaceDomain, "EchoService");

            TransportClientEndpointBehavior sharedSecretServiceBusCredential = new TransportClientEndpointBehavior();
            sharedSecretServiceBusCredential.CredentialType = TransportClientCredentialType.SharedSecret;
            sharedSecretServiceBusCredential.Credentials.SharedSecret.IssuerName = issuerName;
            sharedSecretServiceBusCredential.Credentials.SharedSecret.IssuerSecret = issuerSecret;

            ChannelFactory<IEchoContract> channelFactory = new ChannelFactory<IEchoContract>("RelayEndpoint", new EndpointAddress(serviceUri));

            channelFactory.Endpoint.Behaviors.Add(sharedSecretServiceBusCredential);
            IEchoContract channel = channelFactory.CreateChannel();
            ((ICommunicationObject)channel).Open();

            Console.WriteLine("Введите текст для отправки на сервер:");
            string input = Console.ReadLine();
            while (!String.IsNullOrEmpty(input))
            {
                try
                {
                    Console.WriteLine("Сервер ответил: {0}", channel.Echo(input));
                }
                catch (Exception e)
                {
                    Console.WriteLine("Ошибка: " + e.Message);
                }
                input = Console.ReadLine();
            }

            ((ICommunicationObject)channel).Close();
            channelFactory.Close();
        }
    }
}

Алгоритм подключения к шине совершенно идентичный оному в серверной части. Различия заключаются лишь в том, что мы не открываем новый хост, а открываем канал при помощи вот этого кода:
            ChannelFactory<IEchoContract> channelFactory = new ChannelFactory<IEchoContract>("RelayEndpoint", new EndpointAddress(serviceUri));
            channelFactory.Endpoint.Behaviors.Add(sharedSecretServiceBusCredential);
            IEchoContract channel = channelFactory.CreateChannel();
            ((ICommunicationObject)channel).Open();

Все, что нам осталось сделать это добавить пару строчек кода в конфигурационные файлы обоих проектов. В серверную часть добавим следующую конфигурацию:
<?xml version="1.0"?>
<configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup>
  <system.serviceModel>
    <services>
      <service name="EchoService.Server.EchoServer">
        <endpoint contract="EchoService.Source.IEchoContract" binding="netTcpRelayBinding"></endpoint>
      </service>
    </services>
  </system.serviceModel>
</configuration>

В клиентскую же отправляется следующий фрагмент:
<?xml version="1.0"?>
<configuration>
<startup><supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/></startup>
  <system.serviceModel>
    <client>
      <endpoint name="RelayEndpoint" contract="EchoService.Source.IEchoContract" binding="netTcpRelayBinding"></endpoint>
    </client>
  </system.serviceModel>
</configuration>

В обоих случаях указывается основная точка доступа к сервису и новый биндинг под названием netTcpRelayBinding (этот вид биндинга находится в сборке Microsoft.ServiceBus.dll и обеспечивает безопасную и надежную коммуникацию между компьютерами посредством Service Bus)

Все готово, осталось сделать лишь один маленький шаг перед запуском системы. А именно узнать на портале криденшалы для доступа (это наши Issuer Name и Issuer Secret, требуемые как клиентом так и сервером). Найти их можно в разделе AppFabric –> Service Bus в свойстве неймспейса под названием Default Key

Default Keys For AppFabric Service Bus

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

Service Bus Keys

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

Echo Service via Service Bus

Как видим система работает прекрасно, а для создания сервиса нам не пришлось прикладывать значительных усилий, большинство рутинной работы за нас выполняет новый WCF-биндинг, находящийся в пространстве имен Microsoft.ServiceBus.

На этом пост можно завершать. Исходные файлы проекта можно взять по следующей ссылке: AppFabric Service Bus Echo Service. Очень скоро мы познакомимся с более прогрессивными и изощренными вариантами применения Service Bus, а пока что спасибо за внимание, надеюсь публикация была полезна и интересна :)

Комментариев нет: