Controlando a exposição no WSDL

Podem haver algumas situações onde você precisa tratar individualmente cada endpoint de seu serviço WCF. Como sabemos, cada endpoint tem três características básicas, conhecidas como Address, Binding e Contract. E pela estrutura do WCF, cada endpoint define toda a infraestrutura de acesso (binding) à um determinado serviço, expondo um conjunto de operações que podem ser utilizadas (contrato).

Muitas vezes temos contratos que são construídos para atender um “público” específico. Os contratos são desenhados afim de expor uma determinada funcionalidade, e um endpoint torna isso público para aqueles que estejam interessados em consumí-lo. Por exemplo, você possui um serviço expondo um contrato, mas agora você cria um novo contrato para ficar mais coerente com a necessidade atual, mas você não pode simplesmente remover o contrato (endpoint) antigo, pois há clientes consumindo o serviço através dele, mas o que você precisa fazer, é previnir que o endpoint que expõe o contrato antigo não esteja mais visível para os novos consumidores, pois estes já devem partir para o novo contrato.

Desde a primeira versão do WCF, há um behavior em nível de contrato chamado ServiceMetadataContractBehavior, que expõe uma propriedade boleana chamada MetadataGenerationDisabled, que quando definida como True e aplicado à um contrato específico, faz com que ele não seja colocado no documento WSDL, ocultando o ponto de acesso ao serviço qual aquele contrato está sendo exposto.

Mas infelizmente essa classe era internal, ou seja, não podíamos utilizá-la na configuração dos nossos serviços, o que obrigava a fazer um certo malabarismo caso tivéssemos que ocultar o endpoint que estava expondo aquele contrato. Com o intuito de facilitar isso, a Microsoft tornou essa classe pública na versão 4.0 do WCF.

Como ela é um behavior de contrato, temos que adicioná-la no local correto, e para isso recorremos à propriedade Contract, exposta através da classe ServiceEndpoint, que nos é retornada quando chamamos o método AddServiceEndpoint, já com todas as configurações informadas nos parâmetros do mesmo. A propriedade Contract é do tipo ContractDescription, que possui uma coleção chamada Behaviors, que aceita classes que implementam a interface IContractBehavior (para informações de estensibilidade, consulte este artigo). Abaixo estamos aplicando a instância da classe ServiceMetadataContractBehavior ao endpoint que expõe a funcionalidade em seu formato antigo, e logo em seguida, adicionamos um novo endpoint que estará visível aos novos clientes.

//Endpoint Antigo
host.AddServiceEndpoint
    (
        typeof(IContrato1), 
        new BasicHttpBinding(),
        “srv”
    ).Contract.Behaviors.Add(new ServiceMetadataContractBehavior() { MetadataGenerationDisabled = true });

//Endpoint Novo
host.AddServiceEndpoint(typeof(IContrato2), new BasicHttpBinding(), “srv1”);

É importante salientar que a aplicação deste behavior não faz com que requisições que chegam para aquele endpoint sejam rejeitadas. A ideia aqui é apenas evitar que ele seja exposto no documento WSDL, e faça com que os clientes antigos ainda continuem utilizando ele como forma de acessar o serviço.

Diagnósticos em Serviços WCF

O WCF fornece várias opções para monitoramento e diagnósticos de serviços. Todas estas opções não são exclusividades do mesmo, e podemos recorrer à outras ferramentas, como o próprio sistema operacional, para efetuar auxiliar na visualização e/ou entendimento de um eventual problema que possa estar ocorrendo.

Compilei essas opções em um artigo para a revista .Net Magazine, da DevMedia, afim de tentar centralizar tais opções e como fazer uso delas. Para finalizar, gostaria de agradecer ao Leandro Daniel pela oportunidade concedida.

Teste, Carga e Monitoramento – Parte 3

Uma vez que o serviço esteja em funcionamento em algum lugar, podemos nos deparar com problemas de performance, escalabilidade, memória, etc., que podem aparecer por algum descuido, como por exemplo, questões de infraestrutura, tais como a política de gerenciamento de instâncias e concorrência, throttling, funcionalidades expostas pelos bindings, e ainda problemas relacionados ao código que foi escrito dentro da operação, como por exemplo, o uso desordenado de recursos custosos, o não encerramento correto de cada um deles, dados que poderiam ser compartilhados entre várias requisições, etc.

Todos esses problemas são, na maioria das vezes, “invisíveis” enquanto estamos desenvolvendo e testando, onde não há uma carga efetiva de requisições sendo disparadas contra ele, ou seja, por pior que esteja a configuração de infraestrutura ou o código, como sempre há um único usuário/sistema acessando o serviço, dificilmente irá esgotar os recursos do servidor (máquina) que hospeda e gerencia a execução do mesmo.

Quem pode ajudar a detectar esses possíveis problemas são os testes de carga, onde determinamos um limite de acesso concorrente e verificamos se ele se comporta bem. A partir do momento em que você não consegue atender um número mínimo de requisições em intervalo de tempo que você julga necessário, é um sinal de há algum gargalo, por algum dos indícios que comentei acima.

Há no Visual Studio um conjunto de ferramentas que nos possibilita monitorar e diagnosticar a saúde de uma aplicação, analisando o consumo de processamento, memória e contenção. As versões Ultimate e Premium do Visual Studio 2010 já vem munidas destas ferramentas, que nos permite monitorar vários tipos de aplicações.

Esses monitores estão disponíveis a partir do menu Analyse. Em seguida podemos clicar em Launch Performance Wizard, que lançará um wizard para nos auxiliar na escolha do tipo de monitoramento que desejamos realizar. São apresentados quatro opções, descritas a seguir:

  • CPU Sampling: A cada intervalo de tempo (configurável), extrai da aplicação que está sendo monitorada, informações que nos dirá o quanto a mesma impacta a performance da CPU, exibindo o quanto cada método consome.
  • Instrumentation: Com esta opção, o profiler irá gravar o tempo que cada função consome, desde a sua chamada até a sua saída, contemplando ou não chamadas para outras funções que eventualmente são realizadas.
  • .NET Memory Allocation (Sampling): Coleta informações dos objetos que estão sendo criados em tempo de execução, tais como: número e instâncias, quantidade de memória alocada, etc.
  • Concurrency: Permite o monitoramento de contenção de recursos, como a espera para um objeto que está bloqueado por outra requisição. Além disso, também é possível capturar informações sobre a execução de uma determinada thread, tais como contenção de recursos, utilização de CPU, etc.

É importante dizer que essas ferramentas são bastante intrusivas, então é necessário que se tome cuidado com o que, onde e quando você precisa monitorar, para evitar overheads desnecessários durante a execução.

Além disso, o método Buscar efetua a leitura do – mesmo – arquivo a cada requisição que chega para o mesmo, ou seja, recupera o conteúdo do arquivo e armazena em uma variável (array) local, e logo em seguida, efetua a busca em cima da mesma. Para tentar diagnosticar eventuais pontos de alocação de memória, podemos recorrer ao monitor do tipo .NET Memory Allocation, que tem justamente essa finalidade. Se rodarmos o mesmo em cima do serviço que criamos na primeira parte desta série, o resultado será mais ou menos o que é demonstrado abaixo:

Analisando o resultado do profiler, podemos então voltar ao código e tentar melhorar alguns pontos para diminuir o consumo de memória. A primeira mudança a ser feito é ao invés de ler o arquivo a todo momento, podemos criar uma espécie de caching e armazenar o resultado em um membro privado da classe que representa o serviço. Dentro do construtor inicializamos a coleção, dimensionando a mesma para um número que consiga acomodar a quantidade total de itens, para evitar o redimensionamento do array interno a cada item adicionado. Em seguida, extraimos o conteúdo do arquivo e armazenamos neste membro interno, para evitar que esta tarefa custosa seja realizada a todo momento. Abaixo temos o serviço, já refletindo estas alterações:

[ServiceBehavior(
    InstanceContextMode = InstanceContextMode.Single,
    ConcurrencyMode = ConcurrencyMode.Multiple)]
public class ServicoDeBusca : IBuscador
{
    private readonly string[] linhas;

    public ServicoDeBusca()
    {
        linhas = File.ReadAllLines(@”C:TempTecnologias.txt”);
    }

    public string[] Buscar(string parametro)
    {
        Thread.Sleep(200); //Simula um processamento

        return
            (
                from s in linhas
                where s.IndexOf(parametro, StringComparison.CurrentCultureIgnoreCase) > -1
                select s
            ).ToArray();
    }
}

Ao executar novamente o teste de carga, e fazendo o monitoramento utilizando o mesmo tipo de profiler, podemos perceber que a quantidade de memória alocada caiu drasticamente, assim como podemos reparar na imagem abaixo. É importante dizer que colocar o arquivo sendo lido a todo momento na primeira versão do serviço foi proposital. A ideia foi criar um código que prejudique a execução do serviço para podemos visualizar as ferramentas que temos à nossa disposição para diagnosticar esses problemas e, posteriormente, resolvê-los.

Quando você analisa este tipo de relatório é comum você se deparar com o termo exclusive e inclusive. Exclusive refere-se ao tempo/memória gasto com o método em si, com exceção do que é gasto pelas funções que ele chama internamente; já o inclusive refere-se ao tempo/memória gasto no total geral, incluindo o que se gasta dentro das funções que ele chama internamente.

Conclusão: Vimos no decorrer deste artigo e nos anteriores, a grande gama de ferramentas que é fornecida pelo Visual Studio para nos auxiliar, fornecendo recursos extremamente úteis que nos auxiliam a detectar problemas que possam ocorrer durante a execução de um serviço, quando o mesmo está sendo disponibilizado para um grande volume de requisições.

Teste, Carga e Monitoramento – Parte 2

Na primeira parte desta série eu comentei sobre como utilizar a ferramenta WCF Load Test para gerar testes unitários para serviços WCF. Como eu havia comentado lá, o grande intuito da geração daqueles testes é justamente a integração que podemos ter com outros tipos de testes fornecidos pelo próprio Visual Studio.

O principal tipo de teste a ser utilizado por serviços WCF é o teste de carga (Load Test). Testes de carga são usados para verificar se sua aplicação/serviço irá se comportar bem enquanto sofre uma grande quantidade de acessos simultâneos. A ideia aqui será combinar com os testes unitários que fizemos no artigo anterior, e com isso reproduzir a(s) chamada(s) para o(s) serviço(s) através de N usuários concorrentes dentro de um período de tempo, e que serão configurados através deste tipo de teste.

A ideia do teste de carga é especificar a quantidade, distribuição, periodicidade dos acessos, tentando estressar ao máximo o serviço/aplicação, afim de detectar possíveis cenários, onde depois de um certo número, a aplicação/serviço possa começar a não dar mais conta de atender todas as requisições que chegam para ela.

Para criar um teste de carga para serviços WCF, podemos utilizar o mesmo projeto criado anteriormente, e incluir nele um teste chamado Load Test, assim como é mostrado na imagem abaixo:

Ao incluir este teste, um wizard será iniciado para nos conduzir nas configurações do teste de carga. O primeiro que é exigido é a escolha do think time, que determina o período de tempo que existe entre as requisições (delay), que podemos geralmente utilizar a opção padrão. Já no passo seguinte temos o modelo do teste de carga, que controla a frequência em que os testes envolvidos serão executados. Podemos escolher uma entre quatro opções disponíveis:

  • Based on the total number of tests
  • Based on the number of virtual users
  • Based on user pace
  • Based on sequential test order

Além disso, uma das principais configurações do teste de carga é justamente a quantidade de usuários que acessarão o serviço. Há duas opções disponíveis: Constant Load e Step Load. A primeira exige um número inteiro que corresponde ao número de usuários, e que serão disparadas o mais rápido possível ao aplicação/serviço. A segunda opção, Step Load, permite aumentar gradativamente o número de usuários que acessam a aplicação/serviço, até atingir um número máximo de usuários, que pode ser mais próximo ao mundo real. A imagem abaixo ilustra essas configurações:

Dentro de um teste de carga, ainda podemos criar vários cenários, onde cada um deles poderá conter testes do tipo Web Performance Test (usado por aplicações Web) ou Unit Test, e cada teste sendo executado para um cenário específico, como por exemplo, N usuários, a distribuição das requisições durante o tempo estipulado, qual navegador está sendo utilizado para acesso (útil para aplicações Web), através de qual meio de conexão (LAN ou DialUp), etc.

Ao clicar no método Add para adicionar, serão listados os Unit Tests, inclusive aquele que criamos anteriormente, para o consumo do serviço WCF. A imagem abaixo ilustra esta tela e o teste já selecionado:

Na última tela temos as configurações que determinam por quanto tempo o teste irá rodar. Através da opção Load Test Duration, podemos especificar um tempo para warm-up, que determina a partir de quanto tempo os dados começarão a serem coletados. Isso é interessante porque permite ignorarmos inicializações que o serviço eventualmente faça, para que isso não seja contemplado nos resultados; além disso, temos a opção Run Duration, que determina a quantidade de tempo que em que o teste rodará. E para finalizar, esta tela conta também com o campo chamado Sampling Rate, que determina o tempo em que as informações serão coletadas e exibidas.

Com essas configurações feitas, o teste de carga é criado no Visual Studio, e a qualquer momento permitirá a alteração de alguma configuração previamente definida. Você pode também estar interessado em monitorar contadores de performance específicos, como é o caso dos contadores do ASP.NET e do WCF, que ajudarão nos diagnósticos de eventuais gargalos. A imagem abaixo resume a configuração realizada:

Tudo o que precisamos fazer agora é rodar o teste. Neste momento, o Visual Studio irá começar a executar o teste que criamos, respeitando todas as configurações lá colocadas e, principalmente, coletando as informações que você está interessado, e que será exibida graficamente através da leitura os contadores de performance que você escolheu. A imagem abaixo exibe o resultado do processamento do teste, onde podemos visualizar a quantidade de requisições, duração de cada uma delas, instâncias criadas, etc.

O serviço que criamos no artigo anterior, está sem qualquer configuração extra no WCF, ou seja, o gerenciamento de instâncias padrão é definido como PerSession e a sincronização como sendo Single. Outra configuração que pode afetar aqui é o throttle, apesar de que na versão 4.0 do WCF, seus limites foram incrementados. Apenas para constar, abaixo temos os limites de throttle em ambas as versões:

  • WCF 3.0
    •  MaxConcurrentSessions: 10
    •  MaxConcurrentCalls: 16
    •  MaxConcurrentInstances: 26 (MaxConcurrentSessions + MaxConcurrentInstances)
  • WCF 4.0
    • MaxConcurrentSessions: 100 * Número de Processador
    • MaxConcurrentCalls: 16 * Número de Processador
    • MaxConcurrentInstances: MaxConcurrentSessions + MaxConcurrentInstances

Para o nosso caso, podemos alterar o serviço para haver uma única instância para todas as requisições, e garantir o acesso concorrente ao método Buscar, e para fazer essa alteração, devemos incluir o atributo ServiceBehaviorAttribute para customizar isso:

[ServiceBehavior(
    InstanceContextMode = InstanceContextMode.Single,
    ConcurrencyMode = ConcurrencyMode.Multiple)]
public class ServicoDeBusca : IBuscador { }

Quando temos a configuração acima, o único parâmetro que nos interessa no throttle é a quantidade máxima de chamadas concorrentes (MaxConcurrentCalls), que está limitado em 16 * número de processadores. Vamos incrementar isso para 150, incluindo um behavior de serviço, conforme é mostrado no código abaixo. Para mais detalhes sobre throttle, consulte este artigo.

<serviceThrottling maxConcurrentCalls=”150″ />

Após essa alteração aplicada ao serviço, se rodarmos o mesmo teste, veremos que a quantidade de requisições é elevada, conseguindo atender mais requisições simultâneamente.

Conclusão: Com as informações geradas por este teste, é possível diagnosticar e realizar alguns ajustes finos no WCF (throttling, por exemplo), no IIS e no pipeline de execução do ASP.NET, para conseguirmos dar mais vazão ao processamento das requisições. De qualquer forma, existem outras ferramentas que nos permite extrair informações de custo de processamento, memória e threading e que serão abordados em um futuro artigo.

Teste, Carga e Monitoramento – Parte 1

Um grupo dentro da Microsoft tem trabalhado na construção de um “novo” tipo de teste para o Visual Studio, que tem a finalidade de criar testes unitários para serviços WCF baseando-se nas requisições que chegam para ele ou através de uma análise de um arquivo de tracing, que também é gerado pelo próprio WCF.

Esses testes unitários servirão apenas como uma forma de automatizar/criar as chamadas que serão realizadas para as operações expostas pelo serviço, mas a grande utilidade será a possibilidade de criar um teste de carga para serviços WCF, incluindo esses testes unitários como uma forma de customizar e automatizar como e quantas requisições serão disparadas contra o mesmo e, consequentemente, conseguirmos mensurar o consumo de alguns recursos como memória e processamento que estarão sendo utilizados pelo mesmo. Note que o nome do projeto é WCF Load Test, que está em sua versão 3.0.

Como exemplo, vamos criar um contrato que recebe uma string como parâmetro e retorna um array, também do tipo string, representando os itens que foram encontrados baseando-se naquele parâmetro. Abaixo temos o contrato para ilustrar:

[ServiceContract]
public interface IBuscador
{
    [OperationContract]
    string[] Buscar(string parametro);
}

A implementação para este contrato é extremamente simples, onde tudo o que temos dentro do serviço é a leitura de todo o conteúdo de um arquivo texto que contém um conjunto de tecnologias Microsoft, qual faremos a busca para ver se encontramos o item dentro deste arquivo. A classe que representa o serviço está abaixo:

public class ServicoDeBusca : IBuscador
{
    public string[] Buscar(string parametro)
    {
        string[] linhas = File.ReadAllLines(@”C:TempTecnologias.txt”);
        Thread.Sleep(200); //Simula um processamento

        return
            (
                fromin linhas
                where s.IndexOf(parametro, StringComparison.CurrentCultureIgnoreCase) > -1
                select s
            ).ToArray();
    }
}

O serviço está sendo exposto através do binding BasicHttpBinding, mas a configuração do mesmo será omitida aqui, assim como a configuração do tracing de mensagens, que precisa estar habilitado para submetermos para a criação do teste. Se você quiser saber mais sobre o tracing do WCF, você pode consultar este artigo ou este vídeo.

Depois do projeto WCF Load Test instalado, você já será capaz de adicionar um teste do tipo WCF Test em seu projeto de testes. A imagem abaixo ilustra esse novo item que é adicionado após a instalação do arquivo MSI que você precisa baixar.

Quando clicar no botão OK, um wizard será inicializado, onde você poderá customizar o teste do serviço. O primeiro passo determina a fonte de onde quer extrair as informações para gerar os testes, onde a primeira opção é selecionar a aplicação cliente que consome o serviço, ou utilizar um arquivo de log previamente criado. Além disso, é necessário selecionar uma das três opções que estão logo abaixo: Client-side, Server-side e Fiddler text. As duas primeiras se referem aos arquivos de tracing gerados pelo WCF, e como estamos gerando o tracing através do serviço, temos que selecionar a opção Server-side. Além disso, ainda há a possibilidade de utilizar um arquivo de log do Fiddler, caso o serviço esteja sendo exposto através do protocolo HTTP. A imagem abaixo ilustra este passo:

Observação: Para poder gerar uma requisição para o serviço, podemos utilizar o utilitário WCF Test Client, que nos permite consumir um serviço WCF sem a necessidade de ter que criar uma aplicação para efetuar a requisição.

O próximo passo consiste em selecionar quais operações (SOAP Actions) serão envolvidas no teste. Lembrando que o serviço pode possuir várias delas, e você tem a possibilidade de envolver somente aquelas que julgar necessário para compor os testes. Podemos visualizar este passo através da imagem abaixo:

Para finalizar, precisamos informar no último passo do wizard onde estão os contratos (de serviço, de dados ou faults) que serão utilizados pelo cliente. A ideia é selecionar o assembly onde estão este tipos. E se dentro deste assembly você tiver somente os contratos ou tiver o proxy efetivamente criado, a geração do código de testes é ligeiramente diferente.

Quando o wizard é finalizado, uma classe é gerada encapsulando toda a comunicação com esse serviço, respeitando os métodos selecionados. Para cada operação do serviço, um método de teste é criado e decorado com o atributo TestMethodAttribute. Além disso, um método de inicialização (TestInitializeAttribute) é criado em uma classe parcial, que é utilizado para construir a instância o proxy. Esse método exige uma customização, que é o binding a ser utilizado e o endereço até o serviço.

Abaixo temos a classe gerada para efetuar os testes, qual o Visual Studio (MSTest) utiliza para rodar como sendo um teste unitário. Note que como temos o método Buscar, ele é colocado dentro desta classe, abastecendo o parâmetro com o valor que também foi extraído do arquivo de tracing. Depois disso, envolvemos a chamada para a operação no timer fornecido pela classe TestContext, que será utilizado mais tarde por um teste de carga.

[TestClass]
public partial class ServicoDeBuscaTests
{
    private Servicos.IBuscador buscadorClient;

    public TestContext TestContext { get; set; }
        
    [TestMethod]
    public void ServicoDeBusca()
    {
        this.Buscar();
    }
        
    private string[] Buscar()
    {
        string parametro = “Windows”;
        this.CustomiseBuscar(ref parametro);
        this.TestContext.BeginTimer(“ServicoDeBusca_Buscar”);
        try
        {
            return buscadorClient.Buscar(parametro);
        }
        finally
        {
            this.TestContext.EndTimer(“ServicoDeBusca_Buscar”);
        }
    }
}

Como já mencionei acima, uma configuração necessária para o teste funcionar corretamente, é a necessidade de definir o binding que será utilizado e o endereço até o serviço. Para isso, você pode utilizar o construtor da classe ChannelFactory<TChannel>, que está localizada dentro da classe parcial que foi criada automaticamente através dos passos acima. Você poderá analisar essa configuração no projeto que está anexado no final deste artigo.

Finalmente você pode rodar o projeto de teste através do Visual Studio para se certificar de que a chamada seja realizada, e com isso visualizar o andamento através da janela que sumariza os testes, indicando o status da execução e se sucedeu ou não:

Conclusão: Neste artigo exploramos uma ferramenta que podemos integrá-la no Visual Studio para podermos gerar testes unitários se baseando em serviços WCF. O grande intuito dele é ser utilizado dentro de um outro teste, como é o caso de um teste de carga, que nos permite adicionar um teste previamente criado para compor a carga que será disparada contra o serviço, assunto qual será abordado em um futuro artigo.

WCFLoadTesting.zip (24.22 kb)

Controlando o Processamento de Mensagens do MSMQ

Quando fazemos uso do Message Queue em serviços WCF, a ideia é que as mensagens sejam enfileiradas tanto no cliente quanto no serviço, respeitando as tentativas de entrega e de (re)processamento de cada uma delas. Como uma das principais características é permitir o trabalho desconectado e rodando em horários diferentes, podendo haver um acúmulo de mensagens, o que fará com que o serviço seja estressado quando ele precisar processar cada uma delas, já que ele criará o número de instâncias e permitirá o acesso concorrente de acordo com as configurações do serviço.

Dependendo do que o serviço faz, há a necessidade de envolvê-lo em uma transação para proteger o processamento da mensagem e garantir que se algum problema acontecer, a mensagem seja devolvida para a fila e ser reprocessada mais tarde, mas isso somente acontecerá se as configurações de tentativas estejam devidamente configuradas.

As transações levam o nosso serviço para um outro nível, fazendo com que problemas intermitentes “não afetem” o processamento da mensagem, ou seja, se o banco de dados estiver indisponível por algum motivo, talvez pouco tempo depois já será possível acessá-lo sem problemas. Utilizar as tentativas de reprocessamento aqui, irá auxiliar a garantir esses ciclos, devolvendo a mensagem para a fila e contabilizando o número de tentativas.

Apesar da transação garantir essa confiabilidade, ela tem um efeito colateral que afeta a performance do processamento, pois há um overhead associado ao uso dela. Uma otimização que o WCF nos permite é agrupar o processamento de várias mensagens em um único batch, ou seja, uma transação é criada protegendo o processamento de várias mensagens que rodarão dentro dela, efetivando (commit) todas elas em uma única unidade, ou seja, se uma das mensagens falhar ao ser processada, todas as outras serão desfeitas, garantindo a atomicidade de todas as mensagens envolvidas na transação.

Para controlar isso, que por padrão está desabilitado, temos um behavior em nível de endpoint chamado de TransactedBatchingBehavior, que possui uma única propriedade chamada de MaxBatchSize, que recebe um inteiro que corresponde a quantidade de mensagens que serão agrupadas dentro de uma mesma transação. O exemplo abaixo ilustra a configuração deste behavior, configurando a quantidade do batch como 30.

<system.serviceModel>
  <services>
    <service name=”ServicoDeGestaoDePedidos”
             behaviorConfiguration=”serviceConfig”>
      <endpoint address=”net.msmq://localhost/private/App/ServicoDeGestaoDePedidos.svc”
                behaviorConfiguration=”endpointBehavior”
                binding=”netMsmqBinding”
                bindingConfiguration=”bindingConfig”
                contract=”IGestorDePedidos”/>
    </service>
  </services>
  <behaviors>
    <serviceBehaviors>
      <behavior name=”serviceConfig”>
        <serviceMetadata httpGetEnabled=”true”/>
        <serviceDebug includeExceptionDetailInFaults=”true”/>
      </behavior>
    </serviceBehaviors>
    <endpointBehaviors>
      <behavior name=”endpointBehavior”>
        <transactedBatching maxBatchSize=”30″ />
      </behavior>
    </endpointBehaviors>
  </behaviors>
</system.serviceModel>

Obviamente que nem todas as chamadas serão colocadas dentro de um mesmo batch, ou seja, somente aquelas que possuem a propriedade TransactionScopeRequired e TransactionAutoComplete do atributo OperationBehaviorAttribute definidas como True. Durante o processamento, o WCF irá extrair as mensagens do Message Queue e colocará dentro de uma mesma transação até que o valor definido no propriedade MaxBatchSize seja atingido. O commit será executado desde que nenhuma falha aconteça e:

  • A quantidade for atingida.
  • Quando encontrar uma mensagem com o atributo TransactionScopeRequired definido como False. O próximo batch somente será criado quando encontrar outra mensagem com TransactionScopeRequired definido como True.
  • Se não existir mais mensagens na fila e a quantidade ainda não foi atingida.
  • Depois de 80% do tempo de timeout da transação.

Conclusão: Utilizar essa configuração garante uma melhor performance, evitando o overhead da criação de uma transação para proteger cada mensagem, mas é importante dizer que se uma mensagem estiver danificada, pode evitar o processamento das outras que estejam íntegras, independentemente se elas estejam ou não relacionadas.

Recursos da Palestra sobre WCF e MessageQueue

Conforme mencionei neste post, no último sábado eu efetuei uma palestra no DevBrasil Summit. Gostaria de agradecer publicamente à todos os presentes e ao Ramon Durães pela oportunidade.

Para aqueles que estão interessados no projeto que utilizei para demonstração e na apresentação (PPT), podem baixá-los clicando aqui. Qualquer dúvidas, críticas e sugestões são sempre bem-vindas.

WCF – Injeção de Dependências

Serviços WCF expõem operações, que dentro delas, executam algumas tarefas e retornam o resultado para o cliente que invocou a operação. Como sabemos, o serviço em si nada mais é que uma classe, que por sua vez, pode necessitar de recursos externos (outras classes), para desempenhar as suas respectivas tarefas.

Esses recursos externos são chamados de dependências da classe, que de alguma forma, precisam ser criados para que o serviço execute da forma correta. Só que depender diretamente de uma implementação é sempre complicado, por dificultar os testes, a reusabilidade e estensibilidade. O ideal é sempre depender de uma definição, que muitas vezes é representada por uma interface, pois isso nos permite acomodar uma implementação válida, ou até mesmo algo fake, justamente para simular alguma situação, sem precisar ter todo o “overhead” de uma implementação concreta, que em alguns cenários, não se faz necessário, como é o caso dos testes.

O ideia deste artigo é apresentar os pontos de estensibilidade do WCF, e que podemos utilizá-los para interceptar e injetar as dependências, suprindo todas as suas necessidades, antes de deixar a classe que representa o serviço pronta para ser utilizada. Para isso, vamos utilizar um container de injeção de dependência criado pela Microsoft, chamado Unity, que pode ser baixado isoladamente ou através do Enterprise Library.

Para exemplificar, considere o serviço WCF abaixo. Ele precisa de um logger e um repositório, que abstrai a persistência de usuários de um banco de dados qualquer. Essas duas dependências precisam ser informadas para o correto funcionamento das operações do serviço. Note que a ideia é abastecer essas dependências através do construtor do serviço.

public class ServicoDeUsuarios : IContrato
{
    private readonly IRepositorioDeUsuarios repositorio;
    private readonly ILogger logger;

    public ServicoDeUsuarios(IRepositorioDeUsuarios repositorio, ILogger logger)
    {
        this.repositorio = repositorio;
        this.logger = logger;
    }

    public void Cadastrar(Usuario usuario)
    {
        logger.Write(string.Format(“Cadastrando o usuário {0}”, usuario.Nome));
        this.repositorio.Adicionar(usuario);
    }

    public Usuario Buscar(string nome)
    {
        logger.Write(string.Format(“Buscando o usuário {0}”, nome));
        return this.repositorio.RecuperarTodos()
                   .Where(u => u.Nome == nome)
                   .SingleOrDefault();
    }

    public Usuario[] RecuperarTodos()
    {
        logger.Write(“Recuperando todos os usuários”);
        return this.repositorio.RecuperarTodos();
    }
}

A questão principal aqui é que, em princípio, nós não controlamos como a instância da classe que representa o serviço é construída. Se expormos o serviço acima diretamente, o runtime do WCF irá disparar uma exceção do tipo InvalidOperationException, informando que o tipo do serviço especificado no ServiceHost não possui um construtor sem parâmetros.

É justamente neste ponto que precisamos interceptar a criação desta classe, para abastecer as dependências exigidas pelo serviço. Para fazer essa interceptação, temos que customizar a classe responsável pela criação de instâncias da classe do serviço, e para isso, o WCF fornece uma interface chamada IInstanceProvider, que fornece dois métodos autoexplicativos: GetInstance e ReleaseInstance.

Aqui nós poderíamos criar a instância da classe ServicoDeUsuarios, passando as implementações concretas de cada dependência, mas vamos recorrer à algum container de injeção de dependências, para que ele resolva em runtime cada uma delas. Para executar essa tarefa, o provedor de instâncias do serviço precisará receber também o container pré-configurado com as dependências e um objeto do tipo Type, que representa a classe (serviço) a ser construída.

Agora podemos reparar na implementação do método GetInstance, que recorre ao método Resolve do container do Unity. Este método recebe o tipo a ser construído, que irá verificar todas as dependências do mesmo (incluindo o construtor), e irá procurar as implementações concretas dentro do container, e se localizadas, serão construídas e informadas no construtor da classe (serviço).

public class UnityInstanceProvider : IInstanceProvider
{
    private readonly Type serviceType;
    private readonly IUnityContainer container;

    public UnityInstanceProvider(Type serviceType, IUnityContainer container)
    {
        this.serviceType = serviceType;
        this.container = container;
    }

    public object GetInstance(InstanceContext instanceContext)
    {
        return GetInstance(instanceContext, null);
    }

    public object GetInstance(InstanceContext instanceContext, Message message)
    {
        return this.container.Resolve(this.serviceType);
    }

    public void ReleaseInstance(InstanceContext instanceContext, object instance)
    {
        this.container.Teardown(instance);
    }
}

Só que a criação desta classe não é o suficiente. Precisamos acoplá-la à execução, e para isso, devemos recorrer à um behavior de serviço, que permitirá ter acesso aos dispatchers, onde poderemos informar que o criador da instância da classe que representa o serviço será realizada pelo Unity.

Para colocar isso em prática, vamos recorrer a interface IServiceBehavior, que nos permite acessar o objeto DispatchRuntime, que expõe propriedades para customizar a execução do serviço. Uma dessas propriedades é a InstanceProvider, que recebe a instância de uma classe que implementa a interface IInstanceProvider, e que no nosso cenário, será o Unity o responsável pela criação e resolução de dependências do mesmo.

public class UnityServiceBehavior : IServiceBehavior
{
    private readonly IUnityContainer container;

    public UnityServiceBehavior(IUnityContainer container)
    {
        this.container = container;
    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
        foreach (ChannelDispatcher cd in serviceHostBase.ChannelDispatchers)
            foreach (EndpointDispatcher ed in cd.Endpoints)
                ed.DispatchRuntime.InstanceProvider = 
                    new UnityInstanceProvider(serviceDescription.ServiceType, container);
    }

    //Outros do Métodos Ocultados
}

Note que estamos instanciando a classe UnityInstanceProvider que criamos previamente, e no seu construtor informamos o tipo (Type) do serviço a ser construído e o container, que também é passado através do construtor para este behavior e é armazenado internamente no membro chamado container.

Depois do behavior criado, chega o momento de criarmos o host e, principalmente, o container do Unity, registrando os tipos para que ele consiga resolvê-los em runtime. Criamos um método chamado ConfigureContainer, que para o exemplo, registra os tipos envolvidos estaticamente no container utilizando a sua interface fluente, mas em um ambiente real, pode ser mais flexível configurar o container e suas dependências através do arquivo de configuração, já que o Unity dá suporte a isso.

static void Main(string[] args)
{
    using (ServiceHost host = 
        new ServiceHost(typeof(ServicoDeUsuarios), new Uri(“net.tcp://localhost:8282”)))
    {
        host.AddServiceEndpoint(typeof(IContrato), new NetTcpBinding(), “srv”);

        using (IUnityContainer container = ConfigureContainer())
        {
            host.Description.Behaviors.Add(new UnityServiceBehavior(container));
            host.Open();

            Console.WriteLine(“[ Serviço no Ar ]”);
            Console.ReadLine();
        }
    }
}

private static IUnityContainer ConfigureContainer()
{
    return new UnityContainer()
        .RegisterType(typeof(ILogger), typeof(TextLogger))
        .RegisterType(typeof(IRepositorioDeUsuarios), typeof(RepositorioDeUsuarios));
}

Para acoplar a execução, pegamos o container recém criado e devidamente configurado, e o informamos no construtor do behavior UnityServiceBehavior, que dali em diante já configura o Unity para criar e resolver as dependências da classe que representa o serviço. Feito todas essas customizações, o serviço já é capaz de ser executado, utilizando as implementações que foram supridas pelo Unity.

Conclusão: A injeção de dependência não é algo que está disponível somente com WCF. Tão importante quanto o container que você usa e de sua automatização na resolução das dependências, é a forma como você escreve o seu código, visando o baixo acoplamento entre as dependências envolvidas.

WCF – InjecaoDeDependencia.zip (12.91 kb)

DevBrasil Summit 2011 – Criando Serviços de Alta Disponibilidade

Ramon Durães está organizando um evento na cidade de São Paulo sobre tecnologias Microsoft, que acontecerá dia 22 de janeiro de 2011. Entre vários palestrantes, eu tive o prazer de ser convidado para realizar uma palestra sobre serviços WCF, e decidi falar sobre um assunto interessante, e que quando utilizado, pode trazer grandes benefícios aos nossos serviços. Para maiores detalhes do evento, acesse este endereço. Abaixo está os detalhes da minha palestra:

Título: Criando Serviços de Alta Disponibilidade
Palestrante: Israel Aece
Descritivo: A Microsoft criou o WCF com a finalidade de ser o pilar de comunicação dentro da plataforma .NET, onde o desenvolvedor se preocupa em conhecer apenas uma única forma de construir os serviços, ficando independente de como ele será exposto ou acessado. Além dessa centralização, a Microsoft também se preocupou em manter compatibilidade com algumas funcionalidades existentes, como é o caso do Message Queue.

Utilizar o Message Queue nas aplicações, possibilita o trabalho assíncrono e a garantia de entrega mesmo que o serviço esteja offline, dando assim mais vazão aos clientes, que não precisam esperar pelo término da execução, e permitirá ao serviço processar as mensagens no seu tempo. A finalidade desta palestra é demonstrar a integração que existe entre o WCF e o Message Queue, que podem levar a confiabilidade e disponibilidade dos serviços para um outro nível.
Pré-requisitos: Para tirar melhor proveito da palestra, é importante que os expectadores tenham um conhecimento básico sobre a construção e consumo de serviços WCF.

Conteúdo de WCF para estudo

Muitas pessoas me escrevem para indicar algum material sobre WCF para guiar o estudo de alguém que esteja disposto a aprender esta tecnologia. Como eu já tenho bastante conteúdo escrito sobre isso, resolvi criar um post para elencar todos eles, em uma ordem de leitura que você poderia seguir até atingir este objetivo. Só é importante ressaltar que estes artigos foram escritos individualmente, e podem não ser tão intuitivos quanto um livro, mas acredito que ajudará a dar os primeiros passos.

Observação: Nem todos os posts que já escrevi sobre o WCF estão mencionados aqui. Pode haver informações, não menos importantes, que você pode encontrar na seção de busca ou clicando na categoria WCF.