Hospedando um serviço WCF em uma Worker Role do Windows Azure
Olá pessoal, hoje vou voltar a falar sobre WCF com vocês, mas com a novidade que eu quero mostrar é a possibilidade de hospedar os serviços no Windows Azure, que é a plataforma de Cloud Computing da Microsoft.
Podemos contar no Windows Azure com os serviços de computação, com possibilidade de utilizarmos estes serviços para web (Web Role) e processamento background (Worker Role). Em uma web role, o WCF pode ser hospedado de maneira similar ao IIS, utilizando arquivos .svc. Já em uma worker role, que pode ser entendida para o equivalente à um Windows Service, precisamos gerenciar o ciclo de vida do WCF nela. Este post fala exatamente sobre WCF com Worker Roles.
Vou iniciar a solução com dois projetos existentes: uma ClassLibrary com a definição do contrato para um serviço de “Echo” (projeto WcfDefinition) e um cliente para o serviço (WcfClientApplication). O contrato do serviço está abaixo:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
namespace WcfDefinition
{
[ServiceContract]
public interface IEchoService
{
[OperationContract]
string Echo(string text);
}
}
A aplicação cliente é uma aplicação WPF que possui uma caixa de texto, um botão e uma lista para mostrar o retorno de cada chamada ao serviço, um print está abaixo:
O seu arquivo de configuração:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding>
<security mode="None"/>
</binding>
</netTcpBinding>
</bindings>
<client>
<endpoint address="net.tcp://localhost:8080/EchoService"
binding="netTcpBinding"
contract="WcfDefinition.IEchoService"
name="WcfDefinition.IEchoService" />
</client>
</system.serviceModel>
</configuration>
E o código fonte:
public partial class MainWindow : Window
{
private ObservableCollection<string> _results = new ObservableCollection<string>();
public MainWindow()
{
InitializeComponent();
}
private void SendButton_Click(object sender, RoutedEventArgs e)
{
ChannelFactory<WcfDefinition.IEchoService> channel =
new ChannelFactory<WcfDefinition.IEchoService>("WcfDefinition.IEchoService");
WcfDefinition.IEchoService proxy = channel.CreateChannel();
_results.Add(proxy.Echo(ContentTextBox.Text));
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
ResultListBox.ItemsSource = _results;
}
}
Até aí tudo normal, não apresentei nenhuma novidade para o WCF. Agora vou incluir o Worker Role para pode hospedar o nosso serviço, é só adicionar um projeto do tipo Windows Azure Cloud Service:
E depois adicionar uma Worker Role:
Na Worker Role preciso implementar o serviço de Echo:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using System.ServiceModel;
using WcfDefinition;
namespace WcfServiceWorkerRole
{
[ServiceBehavior(AddressFilterMode = AddressFilterMode.Any)]
class EchoService : IEchoService
{
public string Echo(string text)
{
Trace.WriteLine(string.Format("[EchoService.Echo]: {0}", text), "Information");
return text;
}
}
}
Notem o uso do AddressFilterMode, isso é feito porque o serviço na verdade não é acessado diretamente pelos clientes. As chamadas dos clientes para o serviço passam por um roteamente interno no Windows Azure, se nada fosse utilizado o serviço iria simplesmente recusar a mensagem e retornar um erro.
O arquivo de configuração, exibido parcialmente, da Worker Role está abaixo, notem que ele não traz o endereço do serviço.
<system.serviceModel>
<bindings>
<netTcpBinding>
<binding>
<security mode="None"/>
</binding>
</netTcpBinding>
</bindings>
<services>
<service name="WcfServiceWorkerRole.EchoService">
<endpoint address="EchoService"
binding="netTcpBinding"
contract="WcfDefinition.IEchoService" />
</service>
</services>
</system.serviceModel>
Para que o serviço possa ser exposto em algum endereço, primeiro precisamos avisar o Windows Azure que isso irá acontecer e isso é feito através do arquivo ServiceDefinition.csdef, em formato xml, onde configuro um endereço de endpoint:
<?xml version="1.0" encoding="utf-8"?>
<ServiceDefinition name="WcfWorkerRole" xmlns="https://schemas.microsoft.com/ServiceHosting/2008/10/ServiceDefinition">
<WorkerRole name="WcfServiceWorkerRole">
<ConfigurationSettings>
<Setting name="DiagnosticsConnectionString" />
</ConfigurationSettings>
<Endpoints>
<InputEndpoint name="EchoService" protocol="tcp" port="8080" />
</Endpoints>
</WorkerRole>
</ServiceDefinition>
Isso também pode ser feito pela tela de configuração da Worker Role:
Depois no método OnStart da Worker Role, configuro o ServiceHost:
public override bool OnStart()
{
// Set the maximum number of concurrent connections
ServicePointManager.DefaultConnectionLimit = 12;
DiagnosticMonitor.Start("DiagnosticsConnectionString");
// For information on handling configuration changes
// see the MSDN topic at https://go.microsoft.com/fwlink/?LinkId=166357.
RoleEnvironment.Changing += RoleEnvironmentChanging;
CreateServiceHost();
return base.OnStart();
}
private void CreateServiceHost()
{
RoleInstanceEndpoint externalEndpoint =
RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["EchoService"];
string baseAddress = string.Format("net.tcp://{0}/",
externalEndpoint.IPEndpoint);
Trace.WriteLine(string.Format("[WorkerRole.CreateServiceHost]: Base Address {0}", baseAddress), "Information");
ServiceHost = new ServiceHost(typeof(EchoService), new Uri(baseAddress));
}
Primeiro é necessário buscar a configuração do endereço para o Windows Azure (RoleInstanceEndpoint externalEndpoint = RoleEnvironment.CurrentRoleInstance.InstanceEndpoints["EchoService"];), em seguida é necessário configurar o serviço para utilizá-lo, o que é feito na linha seguinte (string baseAddress = string.Format("net.tcp://{0}/", externalEndpoint.IPEndpoint);).
O último passo é colocar o serviço no ar, através de um ServiceHost.Open() , feito no método Run da Worker Role:
public override void Run()
{
// This is a sample worker implementation. Replace with your logic.
Trace.WriteLine("WcfServiceWorkerRole entry point called", "Information");
ServiceHost.Open();
while (true)
{
Thread.Sleep(10000);
Trace.WriteLine("Working", "Information");
}
}
O código fonte pode ser encontrado aqui.
Comments
Anonymous
February 14, 2011
Apesar de ser possível fazer isso, entendo que não deveria ser feito. Deveria ser utilizado a WebRole que já permite a hospedagem de WCF e todo o gerenciamento com o AppFabric.Anonymous
February 15, 2011
The comment has been removed