Dectetando a desconexão

by Israel Aece 23. November 2009 07:22

Um cenário muito comum em serviços, é a capacidade que os mesmos tem de conseguir invocar algum método do lado do cliente, em resposta ou notificação à algum acontecimento que ocorreu. Isso é conhecido no WCF como comunicação duplex, onde você cria um contrato e que o cliente será obrigado a implementá-lo para que, eventualmente, seja notificado de que algo aconteceu. Para maiores detalhes em como implementar isso, consulte esse artigo ou este vídeo.

Para extrair o canal de comunicação entre o serviço e o cliente, utilizamos o método genérico GetCallbackChannel<T> da classe OperationContext. Esse método retorna uma espécie de proxy, que também implementa a interface de callback, especificada através do parâmetro genérico T. Como a ideia é armazená-lo para mais tarde invocar, pode acontecer deste cliente já não estar mais online, e o canal de comunicação já encontra-se em um estado "inválido".

Se você não se atentar à esse detalhe e, incondicionalmente, invocar o callback, você receberá uma exceção do tipo CommunicationObjectAbortedException, informando que o canal de comunicação com o respectivo cliente foi abortado. Antes de mostrar como dectectar a desconexão, precisamos conhecer um novo tipo fornecido pelo próprio WCF, e que tem um papel extremamente importante dentro deste framework: a interface ICommunicationObject.

Essa interface define um contrato para todos os objetos de comunicação dentro do WCF, gerenciando as conexões que são relizadas através dos métodos Open, Close e Abort (e suas versões assíncronas). Essa interface ainda fornece uma propriedade chamada State, que retorna uma das opções definidas no enumerador CommunicationState. Além disso, ela ainda fornece eventos que são disparados tão logo quando o canal entrar em um estado diferente. Entre os eventos, temos: OpenedClosed e Faulted.

Apesar do método GetCallbackChannel<T> retornar a instância tipo genérico especificado em T, ele pode ser convertido em ICommunicationObject, onde você terá acesso à todos os membros que vimos acima, correspondentes ao cliente que você deseja se comunicar. Tendo acesso a está interface, você tem duas opções para identificar a desconexão. A primeira delas é antes de invocar o callback, avaliar a propriedade State, e se ela ainda estiver aberta, você pode invocar sem maiores problemas. O exemplo abaixo ilustra isso:

ICallback callback = OperationContext.Current.GetCallbackChannel<ICallback>();

if (((ICommunicationObject)callback).State == CommunicationState.Opened)
    callback.OnCallback("Algum Parametro");

A segunda opção consiste em assinar o evento Closed, ou dependendo da situação, o Faulted, que serão disparados quando o canal de comunicação for fechado ou quando ele entrar em um estado falho, respectivamente. Dessa forma você pode ser notificado quando isso acontecer, e tormar alguma decisão em cima disso. O código abaixo ilustra como proceder para assinar o evento Closed:

((ICommunicationObject)callback).Closed += (sender, args) => Console.WriteLine("Fechou!");

Esse tipo de técnica é comumente utilizada quando armazenamos do lado do serviço, a referência para um ou vários clientes, como é o caso de aplicações de chat ou algum modelo de publish-subscribe.

Tags:

WCF

Serviços TCP no Silverlight

by Israel Aece 20. November 2009 21:07

A cada dia que o passa, o Silverlight tem ganhado cada vez mais espaço nos sites que são construídos ao redor do mundo. Motivo para a Microsoft evoluir essa tecnologia rapidamente, adicionando cada vez mais funcionalidades dentro dele.

Os adeptos ao Silverlight não se limitam à aqueles construtores de sites institucionais, que na maioria das vezes, são criados para campanhas publicitárias e com poucas funcionalidades dinâmicas. Ele também está atingindo as empresas desenvolvem os softwares para uso interno, justamente porque a Microsoft incorpora nele, funcionalidades que permitem o uso em aplicações do tipo LOB (Line of Business).

Uma das novidades mais recentes, que afeta principalmente o uso de aplicações Silverlight em intranets, é que a partir da versão 4.0, teremos a possibilidade de referenciar e consumir serviços construídos em WCF, através do protocolo TCP (net.tcp).

Como disse no parágrafo acima, esse tipo de protocolo é útil em intranets, já que não haverá restrições de eventuais firewalls, e mesmo que tenha, tudo é negociável. É importante dizer, que as aplicações Silverlight estão condicionadas à utilizarem o seguinte intervalo de portas: 4502 à 4534, ou seja, somente conseguirá acessar um serviço WCF exposto através de uma delas.

Uma das principais características do protocolo TCP, é que possibilita a comunicação duplex, ou seja, permite que o serviço se comunique com o cliente, através de callbacks. Isso já era possível no Silverlight, através do Polling Duplex, que nada mais é do que uma especialização do protocolo BasicHttpBinding, mas que simula callbacks em cima do protocolo HTTP.

Outra grande vantagem do protocolo TCP quando comparado com o HTTP, é a performance, e mesmo em aplicações Silverlight, essa vantagem é bastante significativa, e um dos grande responsáveis por isso, é a codificação binária. Neste momento, para usar este protocolo, você deve abrir mão da segurança em nível de transporte, que não é suportado.

Os tipos necessários para acessar um serviço WCF através de TCP, estão contidos no assembly System.ServiceModel.NetTcp.dll, localizado em %Program Files%\Microsoft SDKs\Silverlight\v4.0\Libraries\Client. Mas isso não quer dizer que você terá uma classe chamada NetTcpBinding, assim como há no .NET Full. O Silverlight utiliza um CustomBinding, composto com os seguintes bindings elements: BinaryMessageEncodingBindingElement e TcpTransportBindingElement. Ao referenciar o serviço TCP em uma aplicação Silverlight 4.0, a IDE do Visual Studio 2010 já faz as seguintes entradas no respectivo arquivo de configuração:

<configuration>
    <system.serviceModel>
        <bindings>
            <customBinding>
                <binding name="NetTcpBinding_IContrato">
                    <binaryMessageEncoding />
                    <tcpTransport
                        maxReceivedMessageSize="2147483647"
                        maxBufferSize="2147483647" />
                </binding>
            </customBinding>
        </bindings>
        <client>
            <endpoint
                address="net.tcp://localhost:4502/srv"
                binding="customBinding"
                bindingConfiguration="NetTcpBinding_IContrato"
                contract="Servico.IContrato"
                name="NetTcpBinding_IContrato" />
        </client>
    </system.serviceModel>
</configuration>

Ao contrário de outros tipos de comunicação e de aplicações, não basta simplesmente termos o cliente e o serviço funcionando para que eles possam se comunicar. Mesmo através de TCP, este protocolo também está condicionado às políticas de cross-domain.

Para comunicação HTTP, tudo o que precisamos é definir um arquivo XML na aplicação, que permite o acesso. Para suportar a comunicação através de TCP, o Silverlight resolve a questão da restrição de cross-domain de uma forma mais rebuscada. A Microsoft criou uma template de projeto chamada Silverlight TCP Socket Policy, que pode ser acessada a partir das templates Online do Visual Studio 2010 ou através do Visual Studio Gallery, neste endereço.

Essa template dá origem à um projeto do tipo Console, que expõe o arquivo de cross-domain (definido no arquivo SocketPolicy.cs), definindo o intervalo de portas que é permitido que aplicações Silverlight utilizem. Ao rodar as aplicações, certifique-se também de que este projeto esteja rodando, caso contrário, você receberá uma exceção do tipo CommunicationException, informando que você não tem permissões para efetuar o acesso ao serviço, sugerindo que talvez esteja faltando o arquivo de cross-domain.

Conclusão: Apesar de ainda estar em sua versão Beta, o Silverlight 4.0 traz uma série de melhorias e novas funcionalidades, que facilitará cada vez mais a criação de aplicações LOB, e com certeza, uma das principais limitações até então, era o consumo de serviços WCF através do protocolo TCP, que é fator determinante em aplicações deste tipo.

Tags: , , ,

WCF

Mensagens Assíncronas com ChannelFactory

by Israel Aece 18. November 2009 10:41

Uma das formas que temos para consumir serviços WCF em um cliente qualquer, é utilizando a classe ChannelFactory<TChannel>, assim como eu já mostrei um exemplo neste post. Geralmente utilizamos esta técnica quando optamos por compartilhar os tipos (incluindo a interface que representa o contrato) entre o serviço e o cliente, evitando a publicação do documento WSDL, a reconstrução destes tipos do lado do cliente e, principalmente, a necessidade de efetuar a referência do serviço através da IDE do Visual Studio .NET.

Atenção: Antes de prosseguir a leitura deste post, aconselho que leia os seguintes artigos:

Quando fazemos a referência ao serviço através do Visual Studio, há uma opção chamada "Generate asynchronous operations", que para cada operação encontrada no serviço, um par de métodos BeginNomeDaOperacao e EndNomeDaOperacao serão criados, e que irão trabalhar em conjunto, para que assim, o cliente consiga invocar a respectiva operação de forma assíncrona. O problema disso é que muitas vezes o contrato não oferece suporte à chamadas assíncronas, o que nos obrigará a criar toda a infraestrutura do lado do cliente para suportar isso. Imagine que temos o seguinte contrato:

[ServiceContract]
public interface IContrato
{
    [OperationContract]
    string FazAlgo(string value);
}

Como não temos a versão assíncrona do método FazAlgo, temos que recorrer à delegates para conseguir efetuar a chamada. Sendo assim, o código do lado do cliente, poderia ficar da seguinte forma:

private ChannelFactory<IContrato> _factory;
private IContrato _proxy;

public Form1()
{
    this._factory =
        new ChannelFactory<IContrato>(
            new NetTcpBinding(),
            "net.tcp://localhost:8722/srv");

    this._proxy = this._factory.CreateChannel();
}

private void button1_Click(object sender, EventArgs e)
{
    string parametro = "algum valor";

    Func<string, string> executor = p => this._proxy.FazAlgo(p);

    executor.BeginInvoke(
        parametro,
        result =>
        {
            this.textBox1.Invoke(
                new Action<string>(valor => this.textBox1.Text = valor),
                ((Func<string, string>)result.AsyncState).EndInvoke(result));

        },
        executor);
}

A diferença que vemos no código acima, é que invocamos o método FazAlgo através de um delegate. Ao invés de criarmos delegates a todo momento para uma necessidade específica, podemos recorrer aos delegates expostos pelo .NET Framework Action<> e Func<>. No caso acima, estamos fazendo uso do Func<string, string>, que coincide com a assinatura do método FazAlgo, ou seja, recebe e devolve uma string. No exemplo acima, estamos fazendo o uso de lambda ao invés de explicitamente criar a instância do delegate.

Todos os delegates dão suporte à chamada assíncrono para método que ele mantém a referência, e sendo assim, via BeginInvoke disparamos a execução da operação do serviço, utilizando uma segunda thread, e mantendo a aplicação disponível para outros trabalhos. Neste caso, o método BeginInvoke recebe três parâmetros: o primeiro é o parâmetro de entrada que o serviço recebe; o segundo é um delegate de callback, que será invocado quando o resultado voltar; e finalmente, o terceiro parâmetro é um System.Object que será passado para o callback, e que estará acessível através da propriedade AsyncState, e que no caso, passamos a instância do delegate criado para invocar o método.

Para recuperar o resultado, dentro do callback invocamos o método EndInvoke, que retornará o resultado do serviço, e como na maioria dos casos, precisamos exibir isso na tela. Como o callback sempre é disparado na thread que está executando o método assíncrono, você não pode tocar nos controles, já que eles tem afinidade com a thread de criação deles. Aqui entra em cena o método Invoke, exposto pelo do controle que deseja atualizar, e através dele, determinamos um método para ser executado na mesma thread que o criou, e aqui também utilizando lambda.

Se você tiver acesso ao contrato e poder alterá-lo, então você pode dar suporte ao processamento assíncrono ao mesmo, e grande parte do trabalho que vimos acima, do lado do cliente, será descartado. Para suportar o processamento assíncrono no contrato, temos que criar as versões assíncronas do método, e com isso, o nosso contrato de exemplo ficará da seguinte forma:

[ServiceContract]
public interface IContrato
{
    [OperationContract]
    string FazAlgo(string value);

    [OperationContract(AsyncPattern = true)]
    IAsyncResult BeginFazAlgo(string value, AsyncCallback callback, object state);

    string EndFazAlgo(IAsyncResult result);
}

Ao contrário do que vimos anteriormente, ao utilizar essa técnica, você terá o processamento assíncrono tanto do lado do cliente, quanto do lado do serviço. Eu já discuti bastante sobre isso nestes outros artigos. Sendo assim, toda a complexidade da chamada assíncrona será movida para o serviço, que deveremos criar a versão assíncrona do método FazAlgo. O código do lado do cliente ficará relativamente mais simples:

private void button2_Click(object sender, EventArgs e)
{
    string parametro = "algum valor";

    this._proxy.BeginFazAlgo(
        parametro,
        result =>
        {
            this.textBox1.Invoke(
                new Action<string>(valor => this.textBox1.Text = valor),
                this._proxy.EndFazAlgo(result));
        }, null);
}

Para ajudar no entendimento, você pode baixar o código de exemplo clicando aqui.

Tags: , , ,

Async | WCF

WCF Vídeo - Tipos de Mensagens

by Israel Aece 22. August 2009 11:30
Tipos de Mensagens Tipos de Mensagens

Há diferentes formatos de mensagens no WCF, onde cada uma delas possui uma funcionalidade distinta, e este vídeo irá focar nos três tipos de mensagens suportados pelo WCF. Para maiores detalhes, consulte este artigo.

Formato: WMV - Duração: 00:40:49 - Tamanho: 40MB

Tags: , ,

WCF

Polling Duplex

by Israel Aece 16. June 2009 18:09

Como já falei há algum tempo, um dos tipos de mensagens suportados pelo WCF é o padrão Duplex. A finalidade desta técnica é permitir uma comunicação bidirecional, ou seja, o cliente invocar método de um serviço, bem como um serviço invocar um método do cliente. Esse tipo de comunicação permite, na maioria das vezes, um determinado serviço notificar o cliente de que algum evento ocorreu, dando a ele, uma chance de conseguir interagir com um cliente específico ou até mesmo vários clientes, através de um sistema publicador-assinante.

Aplicações Silverlight também podem precisar deste tipo de recurso, onde um serviço irá se comunicar com o cliente (que está rodando no navegador) para notificar sobre algum resultado, ou até mesmo para reportar a execução de alguma tarefa mais complexa. Felizmente, a partir da versão 2.0 do Silverlight, ele prove esta funcionalidade, e a partir a versão mais recente, Silverlight 3.0 (ainda em Beta), tornou esse trabalho mais simples. Para quem já está habituado a trabalhar com WCF, utilizar a API do mesmo em uma aplicação Silverlight (independentemente do tipo de comunicação) traz algumas limitações, quais falaremos neste artigo, principalmente dando mais foco no formato Duplex.

Ao utilizar um binding que suporte o tipo de comunicação Duplex, ele criará internamente um endpoint para que o serviço consiga invocar o callback do lado do cliente. Como a aplicação cliente está rodando dentro do navegador, ela não poderá criar um "listener" para receber os eventuais callbacks que o serviço possa disparar, impedindo a utilização dos bindings tradicionais para este cenário. Para resolver essa deficiência, o Silverlight utiliza o recurso conhecido como "poll". Com esta técnica, o cliente ficará interrogando o serviço para determinar se há alguma mensagem que deve ser recebida por ele.

Antes de falarmos sobre as classes que dão suporte a esta técnica, vamos analisar os assemblies necessários. O Silverlight 2.0 e 3.0 traz dois assemblies com o nome de System.ServiceModel.PollingDuplex.dll, que estão em diretórios diferentes. Um deles contido no diretório %ProgramFiles%\Microsoft SDKs\Silverlight\VERSAO\Libraries\Server e outro no diretório %ProgramFiles%\Microsoft SDKs\Silverlight\VERSAO\Libraries\Client. Como podemos perceber, um dos assemblies será utilizado pelo lado do serviço, onde trará os tipos necessários para a criação de serviços Duplex, enquanto o segundo deverá ser utilizado por aplicações cliente, para o consumo de serviços Duplex.

O binding responsável por fornecer toda a infraestrutura necessária para a comunicação Duplex no Silverlight é chamado de PollingDuplexHttpBinding. Este binding herda diretamente da classe BasicHttpBinding, e customiza o mesmo para suportar o "poll" através do protocolo HTTP. Este binding utiliza os protocolos abertos Net Duplex e WS-Make Connection (WS-MC). O protocolo Net Duplex é utilizado para estabelecer uma sessão entre o cliente e o serviço, para que seja possível correlacionar as mensagens que serão trocadas. Já o protocolo WS-MC, faz o trabalho necessário para a criação de um canal dentro da sessão previamente criada, e que será utilizado pelo serviço para criar a mensagem que deve ser entregue ao cliente ou para o cliente interrogar o serviço.

Em termos de implementação do serviço nada muda, ou seja, a criação de uma Interface que representará o callback continuará sendo necessária. Além disso, a forma de invocarmos o callback continua sendo através do método GetCallbackChannel<T> da classe OperationContext. Para entender mais detalhes de como implementar um serviço que suporte callbacks, consulte este artigo. Se você já sabe construir serviços que utilizam callbacks, você notará que não há nenhuma diferença na criação destes mesmos serviços para Silverlight.

Depois de criado o contrato do serviço, de callback e da classe que representará o serviço, precisamos configurar o host para expor o serviço através do novo binding que mencionamos acima. Infelizmente, até a versão atual (Silverlight 3.0 Beta), ainda não existe suporte para a configuração deste tipo de serviços através de arquivos de configuração (como o Web.config). Na maioria das vezes o serviço estará hospedado no IIS (mas poderá utilizar outros hosts) e a deficiência na configuração declarativa, nos obrigará a customizar o ServiceHost e o ServiceHostFactoryBase (responsável por criar as instâncias da classe ServiceHost). Aqui não há diferenças em relação a forma que já conhecemos para a configuração de um endpoint, ou seja, continuamos necessitando o endereço, binding e contrato. A única atenção que temos que ter é especificar um binding que suporte este tipo de comunicação.

Utilizaremos o IIS como hosting (via arquivo *.svc), e como mencionado acima, será necessário criarmos uma especialização da classe ServiceHost e também da ServiceHostFactoryBase. Isso é mostrado através do código abaixo, e note que dentro do método InitializeRuntime adicionamos os endpoints necessários (através do método AddServiceEndpoint), incluindo um com um binding customizado, e entre os elementos que irão compor ele, temos a instância da classe PollingDuplexBindingElement, que habilita a comunicação Duplex entre o serviço e o cliente.

public class PollingDuplexServiceHostFactory : ServiceHostFactoryBase
{
    public override ServiceHostBase CreateServiceHost(
        string constructorString, Uri[] baseAddresses)
    {
        return new PollingDuplexServiceHost(baseAddresses);
    }

    private class PollingDuplexServiceHost : ServiceHost
    {
        public PollingDuplexServiceHost(params Uri[] addresses)
            : base(typeof(Servico), addresses)
        {
            this.Description.Behaviors.Add(
                new ServiceMetadataBehavior() { HttpGetEnabled = true });
        }

        protected override void InitializeRuntime()
        {
            this.AddServiceEndpoint(
                typeof(IContrato),
                new CustomBinding(
                    new PollingDuplexBindingElement(),
                    new TextMessageEncodingBindingElement(),
                    new HttpTransportBindingElement()),
                "");

            this.AddServiceEndpoint(
                typeof(IMetadataExchange),
                MetadataExchangeBindings.CreateMexHttpBinding(),
                "mex");

            base.InitializeRuntime();
        }
    }
}

É importante dizer ao runtime do WCF que criamos uma factory própria, e para vinculá-la ao serviço, utilizamos a diretiva @ServiceHost no arquivo *.svc. Essa diretiva possui um atributo chamado Factory, onde podemos especificar o tipo de uma classe que implemente a classe base ServiceHostFactoryBase.

<%@ ServiceHost
    Language="C#"
    Debug="true"
    Factory="Web.UI.Host.Network.PollingDuplexServiceHostFactory" %>

Com isso finalizamos tudo o que é necessário por parte do serviço. Com o endereço do mesmo em mãos, devemos referenciá-lo no cliente através da IDE do Visual Studio .NET ou através do utilitário de linha de comando chamado slsvcutil.exe. Ao fazer isso, uma classe que corresponderá ao proxy será criada mas o arquivo de configuração não terá nenhuma informação. Isso se deve ao fato de que a versão atual (Silverlight 3.0 Beta) ainda não traz suporte a este tipo de funcionalidade. Um outro detalhe importante que você notará ao explorar a classe que representa o proxy, é que não haverá a versão síncrona do método, pois o Silverlight não possui isso nativamente. Assim, todos os métodos expostos pelo contrato, o cliente somente terá a versão assíncrona dos mesmos, que é composta por um par de métodos, BeginOperacao e EndOperacao. Apesar destes dois métodos existirem, eles são privados e não estão acessíveis além do proxy, que disponibiliza o modelo baseado em eventos para o consumo. Para maiores detalhes de como o processo assíncrono funciona, consulte este artigo.

Da mesma forma que utilizamos um binding customizado do lado do serviço, temos que fazer o mesmo do lado do cliente. Temos apenas que tomar os devidos cuidados para utilizar o assembly System.ServiceModel.PollingDuplex.dll que está no diretório %ProgramFiles%\Microsoft SDKs\Silverlight\VERSAO\Libraries\Client. Veja que estamos montando o binding customizado com os mesmos elementos (e na mesma ordem) que utilizamos na configuração do serviço, apenas nos atentando que a classe PollingDuplexBindingElement está no assembly que faz parte do cliente. O código abaixo mostra como devemos proceder para utilizar o proxy que foi gerado:

private Servico.ContratoClient _proxy;

public MainPage()
{
    InitializeComponent();

    EndpointAddress ea = new EndpointAddress("http://127.0.0.1.:51116/Servico.svc");
    CustomBinding cb =
        new CustomBinding(
            new PollingDuplexBindingElement(),
            new TextMessageEncodingBindingElement(),
            new HttpTransportBindingElement());

    _proxy = new ContratoClient(cb, ea);
    _proxy.OnCallbackReceived += (o, e) => MessageBox.Show(e.msg);
}

private void Button_Click(object sender, RoutedEventArgs e)
{
    _proxy.PingAsync("Teste");
}

Antes de invocar o método Ping, é necessário nos vincularmos ao evento OnCallbackReceived, que será disparado quando a operação finalizar. Note que as operações são sufixadas com a palavra "Async", e haverá sempre um evento que corresponde ao término da execução da respectiva operação.

Limites e Timeouts

As classes que vimos acima possuem algumas propriedades que podemos utilizar para ter um maior controle em alguns timeouts, e também na quantidade máxima de sessões e de mensagens que podem ser criadas. Para iniciar vamos falar sobre as propriedades fornecidas pela classe PollingDuplexBindingElement do serviço. As propriedades que gerenciam as quantidades máximas são: MaxPendingSessions e MaxPendingMessagesPerSession. A primeira determina a quantidade máxima de sessões suportadas, enquanto a segunda determina a quantidade máxima de mensagens para cada sessão criada. Com relação aos timeouts, temos as propriedades InactivityTimeout e ServerPollTimeout. A primeira propriedade determina um intervalo de tempo que o serviço pode passar sem nenhuma atividade antes do canal se tornar inválido. Já a segunda propriedade, especifica o tempo em que o serviço irá monitorar o cliente anter de retornar.

Do lado do cliente apenas temos duas propriedades, também fornecidas pela classe PollingDuplexBindingElement: InactivityTimeout e ClientPollTimeout. A primeira propriedade tem a mesma finalidade da propriedade InactivityTimeout do lado do serviço, enquanto a propriedade ClientPollTimeout determina o intervalo de tempo que o cliente ficará interrogando o serviço, para verificar a existência de novas mensagens. Todas essas propriedades devem ser configuradas antes da conexão ser estabelecida entre o cliente e o serviço.

Conclusão: Felizmente o Silverlight já traz suporte à comunicação Duplex. Como vimos no decorrer deste artigo, o único binding que possibilita isso em aplicações Silverlight é o PollingDuplexHttpBinding. Como estamos atualmente na versão Beta, ainda há algumas deficiências, como é o caso da ausência do suporte a configuração declarativa deste binding (via arquivo Web.config), que torna o processo de desenvolvimento um pouco mais trabalhoso e propício a erros. De qualquer forma, conseguimos atingir o nosso objetivo, que é proporcionar uma boa interatividade, já que podemos inicar uma tarefa e o serviço nos notificar quando a mesma finalizar.

SLComWCF.zip (92.46 kb)

Tags: , ,

WCF

WCF - Tipos de Mensagens

by Israel Aece 29. September 2008 16:20

Tradicionalmente, em qualquer tipo de aplicação, podemos criar um método que faz alguma tarefa. Ao criá-lo, podemos consumí-lo na mesma aplicação ao até mesmo referenciar a classe em que ele está contido e também consumí-los nos mais variados projetos. Ao realizar a chamada para este método, devemos esperar a sua execução e, quando finalizada, damos continuidade na execução do programa.

Ao criar uma operação em um serviço WCF, ela também se comportará da mesma forma. Mas essa não é a única alternativa fornecida pelo WCF. Desconsiderando a possibilidade de chamadas assíncronas, temos três alternativas que podemos utilizar ao construir uma operação (método) de um serviço: request-reply, one-way e duplex (callbacks). O foco deste artigo é explorar tais alternativas e como elas influenciam na configuração e implementação e execução do serviço.

Request-Reply

Quando criamos um método que retorne alguma informação dentro de uma interface que será o contrato do serviço, podemos decorar este método com o atributo OperationContractAttribute, que dirá ao runtime do WCF que ele deverá ser publicado e, conseqüentemente, consumido pelos clientes que referenciarem o serviço. Com essa configuração básica, o método já estará baseado no padrão request-reply.

Como o próprio nome diz, ao invocar o método a partir de um cliente, uma mensagem será enviada ao serviço através do proxy, contendo as informações necessárias para a execução do método. Neste momento a mensagem viaja pela rede até o respectivo serviço; ao chegar do outro lado, o dispatcher abre a mensagem e efetivamente executa o método requirido. Ao finalizar a execução do mesmo, uma nova mensagem é gerada e devolvida como retorno para o mesmo cliente que a requisitou. Ao chegar do outro lado (cliente), o proxy abre a mensagem, captura o resultado (ou o erro) e devolve para a aplicação.

Durante toda a execução do método, a aplicação cliente ficará bloqueada enquanto o resultado não voltar ou até o momento em que o timeout seja atingido. Com exceção dos bindings NetPeerTcpBinding e NetMsmqBinding, todos suportam operações do tipo request-reply. Para um melhor entendimento a imagem abaixo ilustra o processo entre o cliente e o serviço quando um método baseado no padrão request-reply é executado:

Figura 1 - Modelo Request-Reply.

One-Way

Quando você tem operações que não retornam valores e, principalmente, o cliente não está interessado no sucesso ou falha da mesma, podemos recorrer à técnica chamada de one-way. Também conhecida como fire-and-forget, ela permite que o proxy envie a mensagem ao seu destino, sem esperar pela sua execução. Há um bloqueio mínimo do lado do cliente, que é apenas necessário para que o proxy mande a mensagem e o serviço a enfileire. A partir daí, o cliente está livre para dar continuidade na execução do programa.

Como a característica desde tipo de mensagem é não notificar o cliente de sucesso ou falha, qualquer exceção que ocorra durante a execução do método não notificará o cliente (salvo algumas exceções que veremos mais abaixo). Neste caso, podemos utilizar algumas funcionalidades fornecidas pelo binding que está sendo utilizado, como o uso de sessões confiáveis ou até mesmo a possibilidade da criação de um contrato de callback em conjunto com operações duplex que veremos mais tarde, ainda neste artigo. Através da imagem abaixo, podemos visualizar a mensagem viajando entre cliente e servidor, sem haver uma mensagem de resposta correlacionada:

Figura 2 - Modelo One-Way.

A criação do contrato para suportar este tipo de mensagem muda ligeiramente em relação ao formato tradicional e todos os bindings suportam este tipo de mensagem. Já que a idéia das operações do tipo one-way é não ter retorno, os métodos não devem retornar nada, ou seja, devem ser void. Qualquer tipo de dado que você coloque diferente disso em operações marcadas como one-way, uma exceção do tipo XXXXX será lançada. Como o padrão é request-reply, para dizermos ao WCF que a operação trata-se de uma operação one-way, devemos definir a propriedade IsOneWay, fornecida pelo atributo OperationContractAttribute, como True. O trecho de código abaixo ilustra esta configuração:

using System;
using System.ServiceModel;

[ServiceContract]
public interface ICliente
{
    [OperationContract(IsOneWay = true)]
    void Notificar(string email);
}

Se algum erro ocorrer durante a execução do serviço e o mesmo for configurado como PerCall (que não mantém estado/sessão) e, exposto via BasicHttpBinding ou WSHttpBinding (sem as opções de segurança e mensagens confiáveis habilitadas), o proxy do cliente não será afetado e, conseqüentemente, poderá fazer requisições subseqüentes; agora, se estiver sendo exposto através do WSHttpBinding com segurança, NetTcpBinding sem a opção de mensagens confiáveis habilitada ou através do binding NetNamedPipeBinding e uma exceção ocorrer durante o processamento da operação, isso afetará o proxy do lado do cliente, e ele não poderá mais executar operações a partir deste mesmo proxy, e o fechamento do mesmo não ocorrerá de forma segura. Finalmente, se esses últimos bindings estiverem com a opção de mensagens confiáveis habilitada, ele poderá continuar utilizando o mesmo proxy, mesmo que uma exceção ocorra.

Quando o serviço estiver exposto no modelo PerSession ou Single suportando sessão, fica um pouco mais difícil ter um controle sob as exceções que são disparadas. Se o serviço está exposto via NetTcpBinding ou NetNamedPipeBinding e um erro ocorrer, a sessão será encerrada, a instância do serviço será destruída e o proxy do cliente será afetado, não podendo fazer chamadas subseqüentes.

Duplex (Callbacks)

A terceira e última alternativa que temos no WCF é a possibilidade de efetuar callbacks. A finalidade desta técnica é permitir uma comunicação bidirecional, ou seja, o cliente invocar método de um serviço, bem como um serviço invocar um método do cliente. Esse tipo de comunicação permite, na maioria das vezes, um determinado serviço notificar o cliente de que algum evento ocorreu, dando a ele, uma chance de conseguir interagir com um cliente específico ou até mesmo vários clientes, através de um sistema publicador-assinante. A imagem abaixo ilustra o funcionamento deste tipo de mensagem:

Figura 3 - Modelo Duplex (callbacks).

Nem todos os bindings possibilitam esse tipo de comunicação. Os bindings NetTcpBinding e NetNamedPipeBinding fazem isso nativamente, devido à natureza dos protocolos utilizados. O protocolo HTTP não traz esse suporte, por se tratar de uma conexão que não mantém a ligação entre cliente e servidor. Para possibilitar que os callbacks funcionem a partir do HTTP, foi criado um binding chamado de WSDualHttpBinding que tem essa finalidade.

Quando fazemos uso do WSDualHttpBinding, o WCF utilizará um canal diferente para invocar o callback. Internamente, o que acaba sendo feito do lado do cliente - por parte do runtime do WCF - é a criação de um "endpoint" (incluindo uma porta disponível) para que o serviço relacionado consiga invocá-lo. Esse endereço temporário criado pelo WCF é registrado sob o Http.sys que é um componente de baixo nível e que faz parte do sistema de comunicação do Windows. Sendo assim, qualquer requisição que chegar para esse "endpoint", o Http.sys a encaminhará para o runtime do WCF que, finalmente, irá disparar a classe que implementa a interface de callback (mais detalhes a seguir) que, por sua vez, foi fornecida pelo contrato do serviço. A imagem abaixo ilustra como é realizada a comunicação entre servidor e cliente a partir do WSDualHttpBinding:

Figura 4 - Funcionamento do WSDualHttpBinding.

Para implementarmos esse tipo de comunicação, além de definirmos a interface que servirá como o contrato do serviço, precisaremos também criar uma segunda interface que especificará o callback (isso também é chamado de contrato de callback). Essa interface irá conter o método que deverá ser implementado pelo cliente e que o WCF irá disparar quando você achar necessário. Ao contrário de uma interface de contrato, a interface de callback apenas decora os métodos com o atributo OperationContractAttribute. O código abaixo ilustra como devemos proceder para criá-la:

using System;
using System.ServiceModel;

public interface ICallback
{
    [OperationContract]
    void NotificacaoRealizada(string msg);
}

Apesar das operações de callback não requererem que sejam one-way, é uma boa prática, já que diminui a possibilidade de deadlocks. Depois que o contrato de callback está definido, precisamos associá-lo ao contrato do serviço. Para isso, há uma propriedade chamada CallbackContract fornecida pelo atributo ServiceContractAttribute, que espera um tipo Type, especificando o contrato de callback. Uma vez associado o callback ao contrato de serviço, o WSDL irá contemplá-lo, permitindo que o cliente possa implementar o callback e informá-lo durante a criação do proxy. O código abaixo ilustra essa associação:

using System;
using System.ServiceModel;

[ServiceContract(CallbackContract = typeof(ICallback))]
public interface ICliente
{
    [OperationContract]
    void Notificar(string email);
}

Será a execução do serviço que determinará quando invocar o callback. Para que isso seja possível, primeiramente precisamos obter o canal de comunicação entre o serviço e o cliente e, para isso, recorrer à classe OperationContext. Ela fornece um método genérico chamado GetCallbackChannel que retornará a instância do canal entre o serviço e o cliente. Caso o retorno não seja nulo, então quer dizer que o cliente implementou (e se interessa) pelo callback. A partir de agora, no momento que achar conveniente, você poderá invocar o método de callback que será disparado no cliente. O exemplo abaixo exibe como invocar o callback:

using System;
using System.ServiceModel;

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall
    , ConcurrencyMode = ConcurrencyMode.Reentrant)]
public class ServicoDeClientes : ICliente
{
    public void Notificar(string email)
    {
        bool houveProblemas = false;

        try
        {
            //faz a notificação
        }
        catch
        {
            houveProblemas = true;
        }
        finally
        {
            ICallback callback = 
                OperationContext.Current.GetCallbackChannel<ICallback>();

            string msg = 
                houveProblemas ? 
                "Houve problemas na notificação." :
                "Notificação realizada com sucesso.";

            if (callback != null)
                callback.NotificacaoRealizada(msg);
        }
    }
}

Como dito anteriormente, o callback será contemplado no WSDL e, como o contrato do serviço, a interface que representa o callback também será disponibilizada para a implementação do lado do cliente. Essa interface deve ser implementada em uma classe, colocando o código desejado dentro do(s) método(s) fornecido(s) pela interface de callback. Com a classe implementada, você deverá informá-la na criação (construtor) do proxy. Essa nova versão do construtor do proxy aceita uma instância da classe InstanceContext. Essa classe envolve e gerencia a instância da classe onde a interface de callback foi implementada. Neste momento, o runtime do WCF ficará monitorando para que, quando o callback for disparado, o proxy seja notificado e, conseqüentemente, invocará o respectivo método do lado do cliente. O código ilustra essa configuração:

using System;
using System.ServiceModel;

using (ClienteClient proxy = 
    new ClienteClient(new InstanceContext(new GerenciadorDeRetorno())))
{
    proxy.Notificar("israel@projetando.net");
    Console.ReadLine();
}

//....

public class GerenciadorDeRetorno : IClienteCallback
{
    public void NotificacaoRealizada(string msg)
    {
        Console.WriteLine(msg);
    }
}

Quando um callback faz parte do contrato, a instância da classe InstanceContext passa a ser obrigatória em qualquer uma das versões (overloads) do proxy. Um outro ponto importante é com relação ao tempo de vida do proxy pois, quando o callback acontecer, ele ainda deverá estar ativo para conseguir receber a notificação enviada pelo serviço.

Observação Importante: Quando você possibilita que callbacks sejam disparados pelo seu serviço, é importante que você se atente ao modelo de sincronização utilizado pelo mesmo pois, do contrário, você poderá enfrentar problemas de deadlocks. Caso você queira saber mais sobre os modos de gerenciamento de concorrência dentro do WCF e, principalmente, sobre o modo Reentrant, poderá recorrer à este artigo.

Conclusão: O artigo exibiu as três características possíveis que uma operação possa ser desenvolvida em WCF. Cada uma delas tem uma finalidade exclusiva que, se bem adotada, pode trazer grande melhoria em performance e também possibilitar uma maior interatividade entre cliente e serviço. Para finalizar, é importante dizer também que quando mencionado cliente neste artigo, podemos também estar nos referindo à outro serviço, algo que é perfeitamente possível.

TiposDeMensagens.zip (314.44 kb)

Tags: ,

CSD | WCF

Callbacks em Windows Forms

by Israel Aece 24. September 2008 10:53

Como sabemos, ao desenvolver aplicações em Windows Forms, os controles são criados sob a thread que é iniciada junto ao processo. Com isso, os controles tem uma afinidade com esta thread e, sendo assim, toda e qualquer alteração em cima destes controles, será necessário que seja realizada através da própria thread que os criou.

Quando utilizamos o recurso de callbacks do WCF, é muito comum implementarmos a interface de callback no próprio formulário Windows, fazendo a atualização de controles que estão dentro do próprio formulário. Quando o callback for executado pelo runtime do WCF, ele será disparado em uma thread diferente da thread de criação dos controles e, seguramente, teremos uma exceção sendo disparada justamente devido a questão de afinidade que os controles possuem.

Em uma configuração padrão, ao fazer isso a exceção não ocorrerá. Isso se deve ao fato de que, se o método do proxy (isso envolve também o método Open) for invocado a partir de algum evento gerado pela UI (Form.Load, Button.Click, etc.), ele estabelecerá a afinidade com esta thread e, quando o callback acontecer, automaticamente ele será executado nesta mesma thread, ou seja, a mesma que efetuou a chamada e que também, é a mesma que criou os controles.

Isso é garantido porque o WCF faz o uso de uma classe chamada SynchronizationContext. Esta classe permite executarmos uma determinada tarefa em uma outra thread, diferente da qual estamos atualmente, representando uma espécie de canal entre as duas threads envolvidas. Quando o callback ocorrer, o WCF utilizará este mecanismo para executar o mesmo na própria thread de criação dos controles. Mas lembre-se de que isso somente acontece porque ao fazer a chamada, ele conseguiu estabelecer o contexto com a thread atual.

É importante dizer que voce pode controlar o uso do SynchronizationContext. Para isso, recorremos à propriedade booleana chamada UseSynchronizationContext, que é exposta através do atributo CallbackBehaviorAttribute, que pode ser aplicada à classe que implementa a interface de callback que, neste caso, é o próprio formulário Windows.

Tags: ,

Async | CSD | WCF

Duplex Services no ASP.NET

by Israel Aece 27. February 2008 15:20

O WCF fornece uma funcionalidade chamada Duplex Services. Este tipo de serviço permite que o servidor invoque um método do lado do cliente através de callbacks.

Quando estamos trabalhando com Windows Forms, o consumo deste tipo de serviço não tem muito segredo, pois voce pode apontar para um método dentro do teu formulário ou até mesmo em uma classe do teu projeto que, quando chegar o momento, o runtime do WCF o invocará. No ASP.NET as coisas funcionam de forma diferente. Uma vez que voce requisita uma página ASPX, o ASP.NET cria o objeto correspondente a mesma, executa e, por fim, o descarta. Com isso, se apontarmos o callback de um serviço duplex para um método qualquer da página, muito provavelmente, quando chegar o momento do serviço executá-lo, ele já não existirá mais.

Para resolver, ou melhor, aguardar até que o callback seja disparado, voce pode utilizar um sincronizador, como o é o caso do ManualResetEvent que irá aguardar até que o callback seja disparado. O exemplo desta utilização é mostrado através do trecho de código abaixo:

public partial class _Default : Page, IServiceCallback
{
    private ManualResetEvent _mre = new ManualResetEvent(false);

    protected void Page_Load(object sender, EventArgs e)
    {
        using (ServiceClient proxy =
            new ServiceClient(new InstanceContext(this)))
        {
            proxy.Send("Israel");
            _mre.WaitOne();
        }
    }

    public void Notification(string message)
    {
        Response.Write(message);
        _mre.Set();
    }
}

Como podemos notar, o método WaitOne aguardará até que o método Set seja disparado que, por sua vez, está sendo invocado dentro do método de callback.

Tags: ,

ASP.NET | Async | WCF

Powered by BlogEngine.NET 1.5.0.0
Theme by Mads Kristensen

Sobre

Meu nome é Israel Aece e sou especialista em tecnologias de desenvolvimento Microsoft, atuando como desenvolvedor de aplicações para o mercado financeiro utilizando a plataforma .NET. [ Mais ]

Twitter

Host