Caching do .NET Framework

Desde a primeira versão do ASP.NET, a Microsoft incorporou nele recursos de caching (System.Web.Caching), com a finalidade de colocar em memória, informações que são custosas o bastante para carregar e/ou montar a todo momento, ou seja, apenas a primeira requisição pagaria o preço da carga/montagem, e as requisições subsequentes, passam a utilizar a informação que já está em memória, melhorando consideravelmente a performance.

O maior problema daquela API, é a forte afinidade com o ASP.NET, mais especificamente, com o assembly System.Web.dll, o que fica difícil de reutilizar a mesma em outras aplicações que não sejam web. Caching é um recurso útil para qualquer tipo de aplicação ou serviço, ou seja, qualquer um pode fazer uso deste recurso afim de melhorar a performance.

Sabendo desta dificuldade, o time de Patterns & Practices da Microsoft, decidiu criar um framework para atender esta demanda. Este framework foi chamado de Caching Application Block, e que foi incorporado ao Enterprise Library. Como isso é uma necessidade de grande parte das aplicações, e para alinhar com outros produtos da Microsoft, ela decidiu incorporar este recurso no .NET Framework, e a partir da versão 4.0 do mesmo, já há um assembly chamado System.Runtime.Caching.dll, que fornece os tipos necessários para permitir que nossas aplicações façam uso de estrutura de caching sem a necessidade de recorrer à componentes extras. A ideia deste artigo é explorar esse assembly e os principais tipos que ele fornece.

O primeiro tipo que vamos analisar neste assembly é o ObjectCache. Esta classe representa o caching em si, armazenando aqueles objetos que desejamos manter no cache. Esta classe é abstrata, e serve como base para criar qualquer nova implementação de cache em memória, fornece métodos comuns para qualquer estrutura de caching, tais como: Add, Get e Remove. Através da imagem abaixo, podemos perceber que esta classe implementa a interface IEnumerable<KeyValuePair<string, object>>, para armazenar e iterar pelos elementos que estarão contidos no cache.

Na imagem acima podemos visualizar um tipo chamado MemoryCache, que herda diretamente da classe ObjectCache. Essa classe é uma implementação para caching em memória, bem próxima aquela que utilizamos com o ASP.NET (System.Web.Caching.Cache), mas como sabemos, com o benefício de ser utilizado por qualquer tipo de aplicação.

O primeiro passo para trabalhar com o caching, é justamente criar a instância do “container”, que servirá como repositório, em memória, para todos os elementos que desejamos colocar em caching. Para o exemplo, vamos utilizar a implementação que existe no .NET Framework, que é a MemoryCache. Essa classe já fornece uma propriedade estática, chamada Default. Essa propriedade tem a finalidade de retornar sempre uma instância padrão da mesma, que para grande partes dos cenários, somente uma instância dela é usada por toda a aplicação.

A criação da instância da classe MemoryCache é criada quando se acessa a propriedade Default pela primeira vez, e a partir daí, compartilha a mesma instância com toda a aplicação, desde que ela seja sempre acessada a partir desta propriedade. Você pode criar uma nova instância da classe MemoryCache ao invés de utilizar a propriedade Default, mas você terá que saber como lidar e acessar essa nova instância na aplicação. Se optar por instanciar diretamente, você deve informar uma string contendo um nome que você dará à ela, que será utilizado pelo sistema de configuração, que será comentado mais tarde, ainda neste artigo.

Depois disso, o uso desta API é bastante trivial, bem próximo ao que já conhecemos quando manipulamos qualquer estrutura de caching. O método Add possui algumas versões (sobrecargas), onde cada uma delas permite informar um conjunto diferente de parâmetros, mas com um único propósito, o de acomodar corretamente o item dentro do caching. Uma das versões do método recebe um tipo chamado CacheItem. O CacheItem representa um elemento individual dentro do cache, que recebe duas principais informações: chave e valor, que são autoexplicativas. É importante informar que o valor sempre é do tipo System.Object, o que nos obrigará a trabalhar com conversões durante a extração do elemento do cache. O código abaixo é um exemplo de como adicionar um dado dentro do caching:

MemoryCache cache = MemoryCache.Default;

if (!cache.Contains(“chave”))
    cache.Add(new CacheItem(“chave”, DateTime.Now), null);

O método Add retorna um valor boleano indicando se o item foi ou não adicionado com sucesso. Caso a chave já exista dentro do cache, esse método retornará False. Já para a extração do item de cache, basta recorrer ao indexer passando o nome da chave, ou ainda, utilizando LINQ, já que o caching implementa a interface IEnumerable<T>.

//Via Indexer
Console.WriteLine(cache[“chave”]);

//Via LINQ
var itens =
    (from x in cache where x.Key == “chave” select x.Value).OfType<DateTime>();

Políticas de Caching

Uma das preocupações ao adicionar um item no cache, é o período de tempo em que ele deverá permanecer por lá. Isso é importante para garantir que a informação não permaneça lá por um tempo em que a torne inválida, devolvendo para os seus clientes dados que já estão defasados.

Com a finalidade de controlar a periodicidade do item no cache, temos uma classe chamada CacheItemPolicy. Cada instância desta classe estará associada com um item no cache, e deve ser informada no momento da adição do item no cache. Entre as principais propriedades desta classe, temos AbsoluteExpiration e SlidingExpiration. A primeira determina um DateTime que representa uma data precisa para expiração do item; já a SlidingExpiration determina um TimeSpan que irá renovar o item no cache se ele for acessado dentro daquele período de tempo, caso contrário, será expirado.

Há também uma propriedade chamada Priority, que como o próprio nome diz, determina a prioridade do item no caching, caso a memória seja necessária por outro recurso, o item será descartado levando em consideração a prioridade dele. Há ainda duas propriedades que determinam callbacks, e que são utilizados para interceptar alguns pontos do runtime. A propriedade RemovedCallback recebe o ponteiro para um método que segue a assinatura imposta pelo delegate CacheEntryRemovedCallback, e será invocado depois que o item for removido do cache. Isso permite você criar alguma regra para recolocar o item no cache, para evitar que você faça isso somente quando o item for novamente exigido. Abaixo temos um exemplo da utilização deste callback:

cache.Add(new CacheItem(“chave”, DateTime.Now), new CacheItemPolicy()
{
    RemovedCallback = args => RecarregarInformacao(args),
    AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(2)
});

Há uma restrição com relação a quantidade de memória total que pode ser utilizada pelo MemoryCache. O limite pode/deve ser especificado através da propriedade CacheMemoryLimit, que recebe um valor (representado por um Int64), que corresponde em megabytes, a quantidade máxima que poderá ser alocada para os itens do cache. O valor padrão (zero) permite ao próprio cache, controlar a quantidade de memória disponível pelo computador onde a aplicação está rodando.

Monitores

Nem sempre a expiração através de um período de tempo (renovável ou não) é o suficiente para determinar a remoção do item do cache. As vezes podemos nos dar o luxo de esperar até que um outro elemento seja alterado para notificar que o item do cache deve ser removido. Quando dimensionamos o tempo de expiração ele pode ser curto ou longo demais, e isso pode acarretar em problemas na visualização dos dados que lá estão.

Um exemplo clássico é manter o cache de um result-set proveniente de uma tabela do banco de dados, e manter o cache até que alguma ação (inserção, atualização ou exclusão) que justifique a mudança seja executada naquela tabela. Aqui estamos dependendo de um tabela do banco de dados. Podemos nos basear em um arquivo, que qualquer mudança nele, resulte na remoção do item do cache.

Para atender essa necessidade, a Microsoft incluiu neste assembly os monitores. Alguns monitores já foram criados por ela, e disponibilizados para usá-los em nossas aplicações. A classe abstrata ChangeMonitor é a base para qualquer monitor, fornecendo toda a estrutura necessária para que as classes derivadas possam detectar a mudança em algum recurso e, consequentemente, notificar que isso ocorreu, permitindo ao monitor notificar a nossa aplicação (cache). A imagem abaixo ilustra a hierarquia dos monitores do .NET Framework:

O SqlChangeMonitor recebe em seu construtor a instância da classe SqlDependency, que reportará qualquer alteração em uma tabela específica e, consequentemente, passará a informação para o monitor de cache, que por sua vez, removerá o item associado ao mesmo. Já o monitor CacheEntryChangeMonitor serve para monitorar as entradas no cache, e é usado quando o cache precisa monitorar seus próprios itens. FileChangeMonitor é a classe base para monitorar o sistema de arquivos. A implementação nativa para esta classe, é a classe HostFileChangeMonitor, que irá monitorar algumas ações que eventualmente aconteçam em um arquivo ou diretório, e irá repassar para o cache. Internamente esta classe recorre ao tradicional FileSystemWatcher. O exemplo abaixo ilustra a sua utilização:

CacheItemPolicy policy = new CacheItemPolicy();
policy.ChangeMonitors.Add(new HostFileChangeMonitor(new[] { @”C:TempTeste.txt” }));
cache.Add(new CacheItem(“chave”, DateTime.Now), policy);

O construtor da classe HostFileChangeMonitor recebe uma coleção de paths para arquivos e/ou diretórios que deseja monitorar. Depois de instanciado e devidamente configurado, ele deverá ser adicionado na coleção de monitores, exposta pela classe CacheItemPolicy, através da propriedade ChangeMonitors, onde um item no cache poderá depender de qualquer alteração que ocorra em algum dos monitores associados à ela.

Configuração

Algumas poucas configurações do cache, estão disponíveis para serem alteradas através do arquivo de configuração da aplicação. Isso permite que adequarmos o runtime mesmo depois da aplicação instalada, sem a necessidade de recompilar a mesma. Há uma seção chamada <system.runtime.caching />, que podemos configurar os caches que nossa aplicação utiliza.

O vínculo entre o cache criado pela aplicação e o arquivo de configuração, se dá pelo nome do mesmo. Quando utilizamos a propriedade estática Default da classe MemoryCache, a instância da mesma é criada, e o nome dela é definido como “Default”. Quando decidimos criarmos mais do que uma instância da classe MemoryCache, somos obrigados a utilizar o construtor público, que exige uma string representando o nome do cache. Esse nome poderá ser utilizado para se relacionar com o arquivo de configuração.

O exemplo abaixo permite a configuração do cache padrão. Estamos configurando informações pertinentes à memória, como já foi falado acima. Ainda temos uma outra configuração (que também é possível através do modelo imperativo), que define o intervalo (PollingInterval) em que o cache compara o que temos armazenado com o limite especificado, para evitar o constante acesso ao mesmo.

<?xml version=”1.0″?>
<configuration>
  <system.runtime.caching>
    <memoryCache>
      <namedCaches>
        <add name=”Default”
             cacheMemoryLimitMegabytes=”1024″
             pollingInterval=”00:05:00″ />
      </namedCaches>
    </memoryCache>
  </system.runtime.caching>
</configuration>

Contadores de Performance

Finalmente, para monitorar a saúde do caching que está sendo utilizado pelo aplicação, podemos recorrer à alguns contadores de performance que são instalados pelo próprio .NET Framework, que tem a finalidade de coletar e exibir informações que retratam a situação atual do caching. Para visualizar, podemos recorrer à ferramenta Performance Monitor do Windows, e procurar pela categoria .NET Memory Cache 4.0. Lá você terá uma instância para cada aplicação que utiliza este recurso.

Conclusão: No decorrer deste artigo, pudemos analisar este novo assembly que traz uma porção de tipos para agregarmos a nossa aplicação/componente recursos de caching, algo que antes era somente possível se utilizando de um componente a parte, quando não aquele fornecido para trabalhar exclusivamente com aplicações ASP.NET.

Garantindo a consistência de uma Interface

Imagine que você possui uma interface que descreve a base para alguma funcionalidade do seu sistema. Como sabemos, a ideia de isolarmos a definição em uma interface serve para facilitar os testes, diminuir o acoplamento e melhorar a estensibilidade.

Quando construímos um tipo, muitas vezes pensamos em como garantir que ele sempre fique em um estado consistente, para que seja utilizado pela aplicação sem qualquer problema. Mas como garantir a consistência de um tipo como uma interface? Interfaces não possuem qualquer implementação, e com isso não há como escrever qualquer código para validar parâmetros, garantia de estado, etc.

Com a criação da interface, também não temos controle de quantas implementações dela serão criadas. Deixar a responsabilidade de garantir a consistência para os implementadores, corre-se o risco de que um deles não se preocupe com isso, prejudicando o propósito para qual a interface foi criada.

A Microsoft recentemente publicou um projeto chamado Code Contracts, e que podemos utilizar uma funcionalidade dele para suavizar esse problema. Através dos atributos ContractClassForAttribute e ContractClassAttribute, conseguimos amarrar (e separar) a nossa interface à uma classe utilizada exclusivamente para validar as implementações, que analisará em runtime se as condições estão ou não sendo atentidas. Abaixo temos uma interface para um repositório de domínio, e logo em seguida, temos uma classe apenas para garantir a sua consistência.

[ContractClass(typeof(RepositoryContract))]
public interface IRepository
{
    T GetById<T>(Guid id) where T : AggregateRoot;

    void Save(AggregateRoot item);
}

[ContractClassFor(typeof(IRepository))]
internal sealed class RepositoryContract : IRepository
{
    public T GetById<T>(Guid id) where T : AggregateRoot
    {
        return default(T);
    }

    public void Save(AggregateRoot item)
    {
        Contract.Requires<ArgumentNullException>(item != null);
    }
}

Note que utilizamos o atributo ContractClassAttribute para apontar qual a classe que representa o contrato, e mais abaixo, utilizamos o atributo ContractClassForAttribute para indicarmos que a classe onde este atributo está sendo aplicado serve como contrato para a interface apontada nele. E, finalmente, um detalhe interessante é que a classe é definida como internal, evitando assim o acesso direto pelos consumidores da interface.

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.

Application Pool Identities

Quando o IIS 6.0 foi lançado, uma das principais mudanças que foi feita nela, foi a possibilidade de criar Application Pools, onde podemos isolar/agrupar aplicações que compartilham o mesmo worker process (w3wp.exe). Eu já falei por aqui sobre as características deles neste artigo.

Na configuração padrão, todo Application Pool criado sempre fazia uso de uma identidade chamada Network Service, que nada mais é do que uma conta com poucos privilégios, mas o suficiente para executar uma aplicação Web em um ambiente parcialmente confiável.

O maior problema é que o uso dessa identidade começou a ser largamente utilizada, entre as aplicações dentro do IIS e também em outros serviços espalhados pelo sistema operacional. Até então, se quisermos isolar um Application Pool para uma conta mais privilegiada, somos obrigados a criar uma nova conta no sistema operacional e definí-la nas propriedades do Application Pool (através da opção Custom Account) dentro do IIS, para que o runtime passe a utilizá-la. Dependendo da quantidade de aplicações que exigem esse refinamento, você poderá se deparar com uma explosão de contas sendo criadas dentro do sistema operacional, aumentando também a dificuldade no gerenciamento das mesmas.

Para melhorar um pouco isso, a Microsoft criou uma funcionalidade no IIS 7.5 (Windows 7 ou Windows Server 2008 R2) chamada de Application Pool Identity. A finalidade desta “identidade” é possibilitar ao sistema operacional criar uma espécie de conta virtual, nomeando-a com o mesmo nome do Application Pool, sem misturar com aquelas identidades efetivas que já estão criadas no sistema operacional. A imagem abaixo mostra esta nova opção, que está disponível a partir da propriedade Identity nas configurações avançadas do Application Pool

Quando você define ApplicationIdentity nas opções disponíveis, o sistema operacional criará uma conta virtual, e com ela você poderá conceder ou negar acesso à recursos do sistema operacional, como por exemplo, permitir que os sites que estejam debaixo deste Application Pool possam efetuar upload de arquivos para o diretório D:ConteudoUploads. Se for a aba Security de um diretório qualquer e tentar procurar diretamente pelo nome do Application Pool, você não encontrará; o que você precisa fazer é digitar o nome do Application Pool, mas prefixando-o com “IIS AppPool“, ficando da seguinte forma: IIS AppPoolTesteAppPool (onde TesteAppPool é o nome do Application Pool). Ao clicar no botão Check Names, você verá que a identidade já será encontrada.

A partir de agora, podemos refinar as permissões por Application Pool, sem a necessidade de criar “fisicamente” as contas no sistema operacional.

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.