O impacto do limitador de conexões HTTP

Internamente o .NET Framework possui uma classe chamada ServicePoint que controla as conexões HTTP que são realizadas. A sua criação é feita e gerenciada por uma outra classe, chamada de ServicePointManager, e ambas estão debaixo do namespace System.Net. A criação não se dá de forma desordenada, ou seja, ela é criada para atender um determinado recurso (página, serviço, imagem, etc.), e ao invés de desprezá-la, ela é armazenada e compartilhada para quando precisarmos, novamente, acessar aquele mesmo servidor.

Para falarmos de classes de mais alto nível, um exemplo é a classe HttpWebRequest que utilizamos para realizar requisições HTTP, que recorre internamente à classe ServicePointManager, e que por sua vez, faz todas verificações necessárias, e devolve um objeto ServicePoint pronto para ser utilizado. Como há uma única instância desta classe com aquele servidor, podemos ter a necessidade de realizar conexões simultâneas para ele. Por questões de performance, a especificação do protocolo HTTP determina que um cliente não deve manter mais do que duas conexões com um mesmo servidor.

Apesar desta sugestão, podemos ter a necessidade de aumentar esse valor para que possamos executar mais requisições concorrentes a um mesmo servidor. Pensando nisso, a Microsoft disponibiliza um configuração global, qual nos permite determinar a quantidade de conexões concorrentes que podemos realizar à um mesmo servidor. Para isso, há várias propriedades estáticas, que estão expostas a partir da classe ServicePointManager, e a propriedade que controla a quantidade de conexões é a DefaultConnectionLimit, onde o padrão é apenas 2 conexões, assim como sugere o HTTP/1.1.

Quando consumimos serviços WCF ou até mesmo serviços do tipo ASMX, não lidamos – diretamente – com as classes que vimos aqui, mas se analisarmos os bastidores das classes do WCF (ClientBase<TChannel>) e do ASMX (SoapHttpClientProtocol), veremos que eles, em algum momento, vai recorrer à classe HttpWebRequest e, consequentemente, estaremos sendo limitados caso haja a necessidade de realizar mais do que duas conexões concorrentes ao mesmo servidor.

Supondo que temos um serviço WCF e queremos consumí-lo em uma aplicação cliente, e para efeito dos testes, vamos criar 100 requisições concorrentes para o mesmo. Um pequeno detalhe, é que já definimos a quantidade mínima de threads para que o ThreadPool já as crie, nos antecipando e dizendo a ele quantas threads serão necessárias para executarmos o trabalho.

const int QtdeDeRequisicoes = 100;

ServicePointManager.DefaultConnectionLimit = 2;
ThreadPool.SetMinThreads(QtdeDeRequisicoes, QtdeDeRequisicoes);

var tasks = new Task[QtdeDeRequisicoes];
var sw = Stopwatch.StartNew();

for (int i = 0; i < QtdeDeRequisicoes; i++)
{
    tasks[i] =
        Task.Factory.StartNew<string>(o =>
        {
            Console.WriteLine(“Inicio:t” + o);

            using (var proxy = new ServiceReference1.ServiceClient())
                return proxy.GetData((int)o);
        }, i)
        .ContinueWith(t => Console.WriteLine(“Fim:t” + t.Result));
}

Task.WaitAll(tasks);
Console.WriteLine(sw.Elapsed);

Note que no código acima estamos explicitamente definindo que a aplicação cliente pode somente estabelecer duas conexões concorrentes com um mesmo servidor. Para o serviço de teste, ele levará, na minha máquina, cerca de 48 segundos. O gráfico abaixo ilustra a execução destas requisições. Enquanto a linha vermelha mostra a quantidade de requisições que chegaram até o serviço, a linha verde exibe a quantidade de requisições que estão sendo executadas por ele. Tudo isso sendo extraído do servidor onde o mesmo está hospedado.

Se mudarmos ligeiramente o código acima, definindo para 15 a propriedade DefaultConnectionLimit, o tempo para executar as mesmas 100 requisições cai para 20 segundos, ou seja, temos um ganho considerável. Novamente, através do gráfico abaixo temos as mesmas requisições em um tempo bem mais reduzido:

Observação: Apesar de facilitar a vida do cliente, esta configuração poderá comprometer o desempenho do serviço/servidor, pois estamos atendendo várias requisições que partem de um mesmo cliente, exacerbando o serviço com requisições que poderiam estar distribuídas entre outros interessados em consumir este mesmo serviço.

É importante mencionar que o cliente criado para consumir o serviço WCF de exemplo, é uma aplicação Windows (Console). Quando a aplicação cliente é uma aplicação Web (ASP.NET), também podemos ser influenciado por esta configuração, mas neste caso, ele está dimensionado de uma forma diferente, ou seja, em sua configuração padrão, no interior da classe HttpRuntime, ele define a propriedade DefaultConnectionLimit como 12 * quantidade de CPUs da máquina atual. Sendo assim, qualquer conexão HTTP que você realize através desta aplicação Web, você estará “limitado” à esta quantidade de conexões.

Finalmente, para efeito de testes, você não pode ter o cliente e o serviço na mesma máquina, pois a classe ServicePointManager avalia se o serviço está hospedado localmente, e se estiver, retorna a constante Int32.MaxValue (2147483647) como quantidade de conexões simultâneas, sendo um valor que dificilmente será atingido.

WCF Web API – Suporte à JSONP

Uma das grandes necessidades no consumo de serviços por parte das aplicações Web, é consumir serviços criados e que estão além da própria aplicação cliente, que provavelmente estará debaixo de um outro domínio, e por questões de segurança, isso não será permitido diretamente, já que se utilizado de forma maliciosa e sem o consentimento do usuário, pode por em risco as informações e a navegação dos usuários.

Para contornar essa “limitação”, então precisamos de alguma técnica para possibilitar que essa tarefa seja realizada. Uma opção seria a criação de um serviço nesta aplicação cliente, que serviria como um wrapper, e este por sua vez, consumiria diretamente o serviço, sem as imposições de chamada entre domínios que o navegador impõe. Com isso, o código JavaScript irá consumir este serviço local, que por sua vez, encaminharia a requisição para o serviço remoto, em outro domínio.

Apesar desta técnica funcionar, ela acaba sendo uma solução ruim, já que teremos que envolver outros elementos para realizar uma tarefa relativamente simples. Felizmente o jQuery fornece nativamente o suporte a uma técnica conhecida como JSONP (JSON with Padding).

O seu funcionamento não é muito complicado. Como disse acima, requisições entre domínios não são permitidas, mas há uma única exceção: a tag , ou seja, podemos definir no elemento src (source) a URL para um recurso que está além do nosso domínio, que o navegador não irá proibir o acesso. O que o JSONP faz é justamente o uso dela, criando dinamicamente esta tag, e definindo no atributo src a URL do serviço que estamos tentando acessar.

Na verdade isso não funciona sozinho, ou seja, precisa de uma certa colaboração por parte do serviço, para que o cliente possa processar o resultado da forma correta. Além dos parâmetros que são exigidos pelo método do serviço, o jQuery inclui um parâmetro chamado callback, que é o nome de uma função criada temporariamente no cliente. Aqui entra em cena a infraestrutura do serviço, que deve ser capaz de retornar o resultado (dados) envolvido nesta função, e quando o mesmo chegar ao cliente, ele invocará para capturar o resultado e encaminhá-lo para o nosso código, e assim iremos manipular da forma que acharmos mais conveniente. Falaremos da implementação por parte do serviço mais tarde.

Para fazer tudo isso funcionar, precisamos nos atentar à alguns detalhes do lado do cliente e do lado do serviço. Do lado do cliente, tudo o que precisamos fazer é indicar para a API de AJAX do jQuery que ela deve utilizar JSONP. Para isso, devemos recorrer ao atributo dataType, que indica o tipo de dado/formato que você está esperando que o servidor te retorne. No nosso caso, vamos apontar jsonp, assim como é mostrado no código abaixo:

    $(document).ready(function () {
        $.ajax(
        {
            type: ‘GET’,
            url: ‘http://ipv4.fiddler:1191/cotacoes&#8217;,
            dataType: ‘jsonp’,
            success: function (cotacoes) {
                var itens = [];

                $.each(cotacoes, function (indice, cotacao) {
                    itens.push(‘

  • ‘ + cotacao.Moeda + ‘: ‘ + cotacao.ValorEmReal + ‘
  • ‘);
                    });

                    $(‘

      ‘, { html: itens.join(”) }).appendTo($(‘#Cotacoes’));
              }
          });
      });

      </head>
      <body>
         

         

      </body>
      </html>

      Por parte do cliente, o jQuery fornece parâmetros para realizarmos a configuração via JSONP. Como podemos perceber no código acima, em negrito temos do tipo da requisição sendo definido como JSONP, o que faz com que a requisição seja formatada da seguinte forma (repare o que temos em negrito):

      GET http://127.0.0.1:1191/cotacoes?callback=jQuery15109440240194135102_1328569080908&_=1328569080915 HTTP/1.1
      Accept: application/javascript, */*;q=0.8
      Referer: http://ipv4.fiddler:1185/Index.htm
      Accept-Language: en-US,pt-BR;q=0.5
      User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
      Accept-Encoding: gzip, deflate
      Host: 127.0.0.1:1191
      Connection: Keep-Alive

      Como disse acima, é necessário que o serviço também de suporte à esta funcionalidade. A versão atual do WCF Web API, que está na 0.6, já traz esse recurso nativamente, e com poucas (talvez nenhuma) configurações, podemos construir e expor o serviço para que ele seja consumido através de um cliente que esteja hospedado em um outro domínio. Para o exemplo, temos um serviço “dummy” de cotação de câmbio, que possui os dados estáticos. Apenas como observação, não é mais necessário que o serviço seja decorado com o atributo ServiceContractAttribute.

      public class ServicoDeCotacoes
      {
          [WebGet]
          public IEnumerable<Cotacao> RecuperarCotacoes()
          {
              return new[]
              { 
                  new Cotacao() { Moeda = “Dólar Americano”, ValorEmReal = 1.7263M },
                  new Cotacao() { Moeda = “Euro”, ValorEmReal = 2.2653M },
                  new Cotacao() { Moeda = “Libra”, ValorEmReal = 2.7308M }
              };
          }
      }

      Finalmente, tudo o que precisamos fazer com o serviço é configurá-lo através da classe WebApiConfiguration, algo como já era realizado nas versões anteriores desta API. Abaixo temos um código simples, que ilustra como procedemos para realizar tal configuração:

      public class MvcApplication : System.Web.HttpApplication
      {
          protected void Application_Start()
          {
              ConfigurarServico();
          }

          private static void ConfigurarServico()
          {
              RouteTable.Routes.MapServiceRoute<ServicoDeCotacoes>(
                  “cotacoes”,
                  new WebApiConfiguration()
                  {
                      CreateInstance = (t, ic, msg) => new ServicoDeCotacoes()
                  });
          }
      }

      Ao executar a requisição para o serviço e interceptando-a, teremos na resposta da mesma, a função criada dinamicamente no cliente que será executada automaticamente pelo próprio jQuery. Mais uma vez, repare o que temos negritado abaixo. Note que o nome da função que foi criado pelo jQuery e repassado para o serviço, é devolvido para o mesmo.

      HTTP/1.1 200 OK
      Server: ASP.NET Development Server/10.0.0.0
      Date: Mon, 06 Feb 2012 22:58:00 GMT
      X-AspNet-Version: 4.0.30319
      jsonp-callback: jQuery15109440240194135102_1328569080908
      Content-Length: 170
      Cache-Control: private
      Content-Type: application/javascript; charset=utf-8
      Connection: Close

      jQuery15109440240194135102_1328569080908([{“Moeda”:”Dólar Americano”,”ValorEmReal”:1.7263},{“Moeda”:”Euro”,”ValorEmReal”:2.2653},{“Moeda”:”Libra”,”ValorEmReal”:2.7308}])

      Nenhuma configuração extra foi realizada por parte do serviço, porque a Microsoft entende que isso seja uma necessidade comum, o que faz com que já venha devidamente configurado. Internamente temos um operation response handler chamado JsonpHttpResponseHandler, que é responsável por avaliar se a requisição é ou não uma requisição do tipo JSONP, e para isso, verifica se há uma querystring chamada callback. Caso queira alterar o nome desta querystring, pode-se recorrer à propriedade CallbackQueryParameter deste próprio handler.

      Além disso, ainda temos um media type customizado, chamado de JsonpMediaTypeFormatter, que envolve o resultado gerado pelo serviço, pela função (callback) criada pelo jQuery, que entenderá isso como o resultado que será repassado para a função de sucesso, configurada na chamada do método $.ajax.

    Nome da Aplicação na ConnectionString

    Muitas vezes temos várias aplicações que consomem o mesmo banco de dados. Cada uma dessas aplicações atuam em uma porção de dados, realizando uma tarefa específica. Nestes casos, é muito comum eles precisarem dos mesmos dados, e não há nada errado nisso.

    Como há várias aplicações penduradas em cima de uma mesma base de dados, utilizando a mesma string de conexão, e na maioria das vezes, o mesmo usuário e senha, é difícil monitorar as requisições que estão sendo executadas no SQL Server, pois fica difícil identificar qual a origem (aplicação) da consulta/comando.

    O Activity Monitor do SQL Server, ferramenta que nos permite monitorar em tempo real as conexões que estão abertas no momento, fornece várias colunas que detalham as informações de um processo específico. Entre essas colunas, temos uma chamada Application. Essa coluna, na configuração padrão da string de conexão para o SQL Server, sempre exibirá .Net Sql Client Data Provider.

    Você pode customizar isso, através do atributo Application Name, que pode ser colocado na própria string de conexão, assim como podemos visualizar no código abaixo. Isso pode variar de acordo com cada aplicação, descrevendo para o monitor qual a aplicação que está aguardando ou executando o comando/consulta, independentemente se estiver utilizando um ORM ou não.

    Initial Catalog=DBTeste;Data Source=.;Integrated Security=true;Application Name=Aplicacao01

    Finalmente, depois dessa configuração devidamente realizada, se começarmos a realizar chamadas para o banco de dados SQL Server, essa configuração será levada até ele, e ao abrir a console de monitoramento, podemos visualizar tal informação, e rapidamente identificar qual é a aplicação que está realizando-a. A imagem abaixo ilustra a configuração em ação:

    WCF Web API Test Client

    Uma das ferramentas que a Microsoft criou para facilitar o teste de serviços WCF baseados em SOAP foi o WCF Test Client. Este utilitário permite consumir serviços simples através de alguns protocolos, sem a necessidade de ter que escrever uma aplicação cliente para apenas testá-lo.

    Já para testar serviços baseandos em REST, esse utilitário não ajuda. Podemos recorrer ao navegador. Mas ele também não vai ajudar. Não é possível testar métodos POST diretamente, ele não consegue interpretar o formato JSON, não é possível customizar headers, etc. Por conta de todas essas “limitações”, precisamos de uma ferramenta extra para nos auxiliar nos testes destes tipos de serviços. Isso nos leva a instalar uma aplicação de terceiros, como por exemplo, o Fiddler.

    Apesar do Fiddler ajudar imensamente, ainda é um pouco complicado, pois precisamos descobrir quais são as URIs e seus respectivos métodos HTTP que foram disponibilizados pelo serviço. Para facilitar os testes, a Microsoft incorporou na WCF Web API uma ferramenta, escrita em jQuery, para que possamos testar os serviços tão logo quando eles forem criados, já listando os endereços disponíveis e com uma interface acessível no navegador que conduz facilmente os testes. Esta ferramenta é chamada de WCF Web API Test Client.

    Por padrão, esse recurso está desabilitado. Para habilitá-lo precisamos customizar as configurações do serviço, utilizando a classe HttpConfiguration. Essa classe expõe uma propriedade boleana chamada EnableTestClient. O código abaixo ilustra como efetuar a configuração no arquivo Global.asax:

    RouteTable.Routes.MapServiceRoute<ServicoDeUsuarios>
        (“usuarios”, new HttpConfiguration() { EnableTestClient = true });

    Ao definir esta propriedade como True, podemos acrescentar o sufixo “/test” na endereço do serviço, e a interface qual foi mencionada acima é exibida no navegador, e dali em diante, podemos utilizá-la para efetuar os testes. As imagens abaixo exibem alguns dos testes que foram capturados em um exemplo simples, que posta informações e também captura os dados que foram inseridos.

     

     

    Recursos da Palestra do TechEd Brasil 2011

    Ontem eu fiz uma palestra no TechEd Brasil 2011, e o principal assunto abordado lá foi a nova API que a Microsoft está criando para facilitar a construção, hospedagem e consumo de serviços WCF baseados em REST.

    O exemplo principal foi concetrado um projeto que nomeie de Techitter, que é composto por algumas aplicações simulando – de muito longe – o Twitter. A solução é composta por 4 projetos, onde um é o serviço, e onde concentra-se grande parte das novidades, um site em ASP.NET MVC que via jQuery posta e carrega as mensagens; outro projeto que consome a mesma API como um widget e, finalmente, uma aplicação WPF que também faz uso do serviço, consumindo o mesmo serviço via HttpClient, que também compõe a nova API. Para incrementar, existe o consumo de um serviço criado em outra tecnologia (NodeJs.exe), que foi consumido pelo serviço.

    Gostaria de agradecer a todos os presentes, e também ao Rogerio Cordeiro pela oportunidade. Para aqueles interessados, o download do projeto pode ser baixado clicando aqui.

    Caching em Serviços REST

    Uma das principais técnicas que são utilizadas na Web em busca de performance, é a utilização de técnicas de caching. Com elas, podemos armazenar em algum local uma cópia da página (HTML), arquivo, imagem, etc., que estamos acessando, para quando precisarmos novamente daquele recurso (URL) mais tarde, sermos atendidos de uma forma muito mais rápida.

    A redução da latência e a diminuição do tráfego na rede são dois detalhes que alguma estratégia de caching pode ajudar a melhorar. O controle do caching pode estar definido do lado do servidor ou diretamente no cliente que consome o recurso. Sendo assim, ao acessar um recurso que já foi acessado recentemente, o usuário será servido quase que instantaneamente com o – mesmo – resultado.

    As estratégias de caching são parte da especificação do protocolo HTTP, que como sabemos, foi desenhado para ser utilizado para acessar informações de sistemas distribuídos. A ideia principal do caching do HTTP, é tentar eliminar, quando possível, a necessidade de fazer novas requisições para um mesmo recurso, ou quando a requisição é necessária, tentar diminuir a quantidade de dados devolvidos.

    Na verdade, dois detalhes importantes são definidos na especificação do protocolo, onde a estratégia de caching deve/pode controlar, a saber: expiração e validação. A expiração consiste em verificar se o recurso que está sendo solicitado já está em cache e continua sendo válido (ainda não expirado). Já a validação verifica se o recurso que está sendo re-solicitado ao servidor, sofreu ou não mudanças desde a última requisição, e caso não tenha, nada será retornado, afinal já temos a versão mais atualizada, e isso reduz a quantidade de informações – desnecessárias – sendo trafegadas.

    Serviços REST, por estarem fortemente ligados ao protocolo HTTP, podem também fazer uso do caching oferecido pelo protocolo. A ideia deste artigo é demonstrar as opções que temos para configurar e, consequentemente, fazer uso de todos os benefícios oferecidos pelo caching. Para o exemplo, teremos um serviço que gerencia produtos, e haverá um único método que dado um identificador, retorna a instância do produto correspondente. Abaixo temos o código do serviço que utilizaremos pelos exemplos:

    [ServiceContract]
    [
        AspNetCompatibilityRequirements
        (
            RequirementsMode = AspNetCompatibilityRequirementsMode.Required
        )
    ]
    public class Servico
    {
        [WebGet(UriTemplate = “produtos/{produtoId}”)]
        public Produto RecuperarProduto(string produtoId)
        {
            var produto =
                (
                    from p in produtos
                    where p.Id == Convert.ToInt32(produtoId)
                    select p
                ).SingleOrDefault();

            return produto;
        }
    }

    Vou omitir a configuração do serviço aqui, mas neste primeiro momento estaremos utilizando a infraestrutura fornecida pelo WCF para a construção de serviços baseado em REST, que está disponível desde a versão 3.5 do .NET Framework (WCF Web Http). Ao subir este serviço, ele estará acessível e toda e qualquer requisição realizada pelo navegador chegará até o ele.

    Todo o controle do caching do HTTP é realizado através de headers que foram criados e são assegurados pelo navegador e pelo servidor, para controlar o caching criado. Os serviços WCF fornecem alguns recursos que nos permitem acessar de forma mais simples estes headers, manipulando-os de acordo com a nossa necessidade. Os headers são divididos em cartegorias para manipular a expiração e a validação, e inicialmente vamos analisar aqueles que são utilizados para determinar a expiração de um recurso.

    Expiração

    O HTTP 1.1 introduziu um novo header, que é responsável por controlar a expiração. Este header é o Cache-Control, que pode receber vários valores que combinados, instruem o cliente a como efetuar e controlar o caching do recurso que está sendo devolvido. Um dos valores que podemos definir no Cache-Control é private, que determina que aquele conteúdo é exclusivo à um usuário (navegador), e pode eventualmente ser cacheado por ele. Podemos combiná-lo com outro valor que é o max-age, que define um número inteiro, que determina quantos segundos depois da requisição o conteúdo deverá ser mantido no cache.

    Sendo assim, podemos recorrer ao contexto da requisição atual (WebOperationContext) dentro do método que está sendo disparado, e lá incluir o header que mencionamos acima. Temos que acessar a coleção de headers a partir da propriedade OutgoingResponse, que permite acessar as informações pertinentes à resposta.

    WebOperationContext.Current.OutgoingResponse.Headers.Add(“Cache-Control”, “private, max-age=30”);

    Já quando não quiser que o cliente efetue o cache do recurso, podemos recorrer ao mesmo código acima para explicitamente determinar isso, determinando o valor do header Cache-Control como no-cache. O código abaixo exibe esta configuração:

    WebOperationContext.Current.OutgoingResponse.Headers.Add(“Cache-Control”, “no-cache”);

    Note que para configurarmos o header Cache-Control, utilizamos uma classe pertinente ao WCF, que independe do host que estamos utilizando para hospedar o serviço. Quando utilizamos o IIS e, consequentemente, a infraestrutura do ASP.NET, podemos fazer uso de um recurso que já existe há algum tempo, que é o OutputCache.

    A configuração do caching é através de cache profiles, que é um recurso onde você define todas as características do caching no arquivo de configuração (Web.config), e vincula essa configuração através do atributo AspNetCacheProfileAttribute. Abaixo temos o arquivo de configuração do serviço, e podemos notar a configuração do caching. A primeira configuração importante é habilitar o modelo de compatibilidade com o ASP.NET, e fazemos isso através do atributo aspNetCompatibilityEnabled do elemento serviceHostingEnvironment, definindo-o como True.

    Em seguida, habilitamos o OutputCache definindo o atributo enableOutputCache como True. Logo após, temos um profile criado, que agrupa o conjunto de configurações de cache e o nomeia. Repare que estamos definindo o local do cache como “Server”, o que determina que uma vez atendida a requisição para um determinado recurso, o cache será armazenado no servidor, e qualquer requisição subsequente, partindo ou não do mesmo cliente, será devolvida do cache criado, respeitando a duração de 60 segundos, ali também configurada. Isso fará com que qualquer complexidade que temos na execução daquele recurso, o preço será pago apenas uma única vez.

    <configuration>
      <system.web>
        <caching>
          <outputCache enableOutputCache=”true” />
          <outputCacheSettings>
            <outputCacheProfiles>
              <add name=”CachingNoServidor”
                   location=”Server”
                   duration=”60″
                   varyByParam=”none”/>
            </outputCacheProfiles>
          </outputCacheSettings>
        </caching>
      </system.web>
      <system.serviceModel>
        <serviceHostingEnvironment aspNetCompatibilityEnabled=”true”/>
        <services>
          <service name=”ViaWcfWebHttp.Servico”>
            <endpoint address=””
                      binding=”webHttpBinding”
                      contract=”ViaWcfWebHttp.Servico”/>
          </service>
        </services>
      </system.serviceModel>
    </configuration>

    Como disse anteriormente, a amarração da configuração com o método do serviço é realizada através do atributo AspNetCacheProfileAttribute, que recebe em seu construtor uma string que representa o nome do profile que você quer aplicar para ele. Abaixo temos o serviço que foi ligeiramente modificado, decorado com o respectivo atributo:

    [AspNetCacheProfile(“CachingNoServidor”)]
    [WebGet(UriTemplate = “produtos/{produtoId}”)]
    public Produto RecuperarProduto(string produtoId) { }

    Como esse caching refere-se ao servidor, os clientes nada sabem sobre ele. Inclusive na resposta ao cliente, por padrão, ele define o header Cache-Control como no-cache. Como já era de se esperar, podemos criar um novo profile, e lá definirmos o local do cache no cliente. Isso fará com que ao acessar o serviço, o cliente receberá o header Cache-Control definido como private e o atributo max-age definido como 60 (segundos), fazendo com que o navegador faça o caching do recurso. Abaixo temos o trecho do arquivo de configuração que exibe a adição deste profile de caching para o cliente:

    <outputCacheSettings>
       <outputCacheProfiles>
          <add name=”CachingNoCliente”
               location=”Client”
               duration=”60″
               varyByParam=”none”/>
       </outputCacheProfiles>
    </outputCacheSettings>

    Validação

    Acima vimos as opções que temos para controlarmos a expiração. Notamos que a expiração controla o período em que o recurso ficará disponível no caching do cliente (ou do servidor). Isso faz com que alguns recursos não sejam requisitados a todo momento, diminuindo a quantidade de informações que trafegam entre as partes. Só que dependendo do conteúdo que está sendo armazenado no cache, nem sempre podemos confiar cegamente no conteúdo que está salvo do lado do cliente.

    Em certos cenários, o custo do round-trip é ignorado, em busca dos dados mais recentes possíveis, onde o prejuízo de visualizar uma informação defasada é muito maior. Só que as vezes, vamos até o servidor em busca dessa nova informação, e ela ainda não foi alterada, retornando para o cliente o mesmo conteúdo, atualizando a informação que temos localmente com exatamente, a mesma informação, ou seja, trafegamos uma informação que já tínhamos do lado do cliente. Para otimizar isso, temos uma técnica que chamamos de “GET condicional”.

    Essa técnica faz uso de entity tags (ETags), que consiste na adição de um header que é adicionado na resposta à uma requisição, que caracteriza a “versão” do conteúdo que foi devolvido ao cliente. Ao receber esse conteúdo e encaminhar esta tag nas requisições subsequentes, o serviço deverá ser capaz de identificar se houve ou não mudanças no objeto; caso exista, o serviço irá retornar a versão mais recente, do contrário, retornará o status 304 do HTTP, que indica que o recurso não teve mudanças, sem qualquer conteúdo na resposta, poupando assim que informações desnecessárias sejam enviadas ao cliente, que por sua vez, já possue a versão mais atual.

    A implementação consiste em duas partes, sendo a primeira onde devemos nos preocupar em passar ao cliente as informações que caracterizam a “versão” do conteúdo, e a segunda parte, onde devemos criar dentro do serviço, a lógica para determinar se a versão que o cliente possui já é ou não a mais recente.

    Para nosso exemplo, vamos incrementar a classe Produto com uma propriedade do tipo Guid chamada Versao, que define a versão do mesmo. Qualquer alteração que ocorra em qualquer uma das propriedades do produto, deverá atualizar o GUID. Já que essa propriedade irá caracterizar o versionamento, devemos apontar na resposta para o cliente o seu valor para que ele possa encaminhar ao mesmo através do header ETag. E para isso, vamos novamente recorrer a classe WebOperationContext, acessando a resposta que será enviada ao cliente e, finalmente, teremos o método SetETag, que como o próprio nome diz, permite definirmos a tag:

    [WebGet(UriTemplate = “produtos/{produtoId}”)]
    public Produto RecuperarProduto(string produtoId)
    {
        var produto =
            (
                from p in produtos
                where p.Id == Convert.ToInt32(produtoId)
                select p
            ).SingleOrDefault();

        WebOperationContext.Current.OutgoingResponse.SetETag(produto.Versao);
        return produto;
    }

    Ao utilizar uma ferramenta para monitorar o tráfego HTTP, podemos visualizar o resultado da requisição para o serviço acima. Note a presença do header ETag definido com o GUID gerado para nosso objeto:

    HTTP/1.1 200 OK
    Server: ASP.NET Development Server/10.0.0.0
    Date: Sun, 28 Aug 2011 20:26:53 GMT
    X-AspNet-Version: 4.0.30319
    Content-Length: 202
    ETag: “8e548670-8b75-4187-aedc-8182f46c3216”
    Cache-Control: private
    Content-Type: application/xml; charset=utf-8
    Connection: Close

    <Produto xmlns=”http://schemas.datacontract.org/2004/07/ViaWcfWebHttp&#8221; xmlns:i=”http://www.w3.org/2001/XMLSchema-instance”><Descricao>Mouse Microsoft</Descricao><Id>1</Id><Valor>120.00</Valor></Produto>

    Uma vez que o cliente recepciona essa requisição, as requisições subsequentes repassam o valor do header ETag para o serviço através de um outro header, chamado de If-None-Match, onde o serviço deverá averiguar se o produto foi alterado nesta janela de tempo, para determinar se o conteúdo do cliente deve ou não ser atualizado. Abaixo temos a requisição sendo realizada ao serviço depois de receber a ETag:

    GET http://localhost:3911/Servico.svc/produtos/1 HTTP/1.1
    Accept: text/html, application/xhtml+xml, */*
    Accept-Language: en-US,pt-BR;q=0.5
    User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
    Accept-Encoding: gzip, deflate
    Host: localhost:3911
    If-None-Match: “8e548670-8b75-4187-aedc-8182f46c3216”
    Connection: Keep-Alive

    Para efetuar essa validação do lado do serviço, há um método chamado CheckConditionalRetrieve, que está disponível a partir da propriedade IncomingRequest da classe WebOperationContext. Esse método recebe como parâmetro a ETag do produto. Se o produto não foi modificado, então a tag da requisição será a mesma, o que faz com que o método, internamente, aborte a requisição, devolvendo ao cliente o resultado 304 do HTTP, que caracteriza que nada foi mudado (NotModified). O código a seguir exibe a utilização deste método, e logo na sequência, temos a imagem que ilustra esse procedimento:

    [WebGet(UriTemplate = “produtos/{produtoId}”)]
    public Produto RecuperarProduto(string produtoId)
    {
        var produto =
            (
                from p int produtos
                where p.Id == Convert.ToInt32(produtoId)
                select p
            ).SingleOrDefault();

        WebOperationContext.Current.IncomingRequest.CheckConditionalRetrieve(produto.Versao);
        WebOperationContext.Current.OutgoingResponse.SetETag(produto.Versao);

        return produto;
    }

    WCF Web API

    Como já comentei aqui, a Microsoft trabalha em um novo projeto chamado WCF Web API, que torna o desenvolvimento e consumo de serviços REST mais simples. Estes tipos de serviços também podem tirar proveito dos recursos de caching fornecidos pelo HTTP que vimos neste artigo.

    Ao utilizar os objetos que representam as mensagens de requisição e resposta diretamente, você encontrá propriedades que configuram o caching no cliente. Você também pode optar por utilizar um message handler, para separar o que corresponde ao negócio do que compete a parte de infraestrutura.

    Coletando e analisando arquivos de Dump

    Ao instalar uma aplicação em seu local de funcionamento, começa uma nova etapa, que é o monitoramento da mesma, que permite diagnosticar eventuais falhas e tentar trabalhar sempre de uma forma preventiva. Só que muitas vezes, estes problemas podem acontecer durante a execução, problemas estes que podem ser por falta de testes que atendiam aquelas condições, ou ainda, referente à algum problema de infraestrutura.

    Se você toma o devido cuidado de catalogar as exceções que estão sendo disparadas, você pode recorrer aos arquivos onde elas estão armazenadas e inspecionar o que aconteceu e, consequentemente, encontrar a região do código responsável por aquele problema, e resolvê-lo, para em seguida, distribuir a correção para ele. Só que para detectar o problema, geralmente precisamos muito mais do que simplesmente o tipo da exceção que foi disparada, mas também quais foram os parâmetros informados, os valores que as variáveis estavam definidas naquele momento, e assim por diante.

    Quando estamos em ambiente de desenvolvimento, podemos recorrer ao debugger do Visual Studio para executar a aplicação passo à passo, parando nos pontos que são importantes e naqueles que podem estar ocasionando o problema, e atenciosamente analisar e detectar o problema. Só que se essa aplicação já estiver em funcionamento, isso não é possível, pelo menos não de uma forma simples. Neste caso podemos recorrer a criação de um arquivo de dump da aplicação em questão. Ao gerar este arquivo, ele “fotografa” o estado atual do processo e gera várias informações para que possamos depurá-la mais tarde, algo mais ou menos parecido com o reporte de bugs do Windows.

    Esse recurso é algo que podemos utilizar tanto em aplicações cliente como aquelas que rodam em um servidor, como é o caso de aplicações ASP.NET ou de serviços WCF. No caso de aplicações cliente, você pode recorrer ao Doctor Watson do Windows ou indo diretamente através da Task Manager. Como eu quero demonstrar a coleta do dump de um serviço WCF, vamos recorrer à segunda opção. Como sabemos, ao hospedar um serviço WCF no IIS, quem será responsável por executá-lo é um processo, chamado de w3wp.exe, que pode ser configurado através da opção Application Pools. Abaixo temos a imagem que ilustra como extrair o dump do processo do IIS:

    Basicamente, este serviço recebe uma requisição e gera uma massa de informações para o respectivo cliente. Depois do arquivo de dump criado (extensão *.dmp), você receberá uma notificação informando onde ele foi salvo. Tudo o que precisamos fazer agora, é abrir o arquivo no Visual Studio 2010. A partir desta versão, o Visual Studio é capaz de interpretar esses arquivos. Ao abrí-lo, você irá se deparar com uma página, que detalha as informações sobre o processo. Podemos visualizar o nome do processo de onde o dump foi extraído, versão do sistema operacional, da CLR, etc. Outro ponto importante são os módulos que foram carregados à este processo, que aqui, por questões de espaço, não estão sendo exibidos todos eles. A imagem abaixo ilustra este resumo:

    Como este arquivo contém o estado da aplicação naquele momento, o Visual Studio nos permite analisar o estado das informações (variáveis, parâmetros, etc.) que tínhamos, de uma forma tão simples quanto depurar a aplicação. Só que para isso funcionar, é necessário definirmos os símbolos, que são responsáveis por armazenarem informações para guiar o debugger até o código fonte. A opção “Set symbol paths” nos permite definir o endereço até o arquivo *.pdb, gerado durante o desenvolvimento do serviço.

    Depois disso definido, podemos clicar na opção “Debug with Mixed”, e o debugger do Visual Studio entra em ação. Ele lerá as informações contidas no arquivo de dump e irá exibí-las através das ferramentas de depuração que o próprio Visual Studio já nos fornece. A imagem que veremos a seguir já é uma dessas ferramentas: a janela Parallel Stacks. Essa janela é útil para depurarmos aplicações multi-thread, como é o caso de serviços WCF. Como podemos visualizar, temos a stack da execução e dentro dela, a chamada para o método Executar, que faz parte do serviço criado para o teste.

    Ao clicar com o botão direito em um dos itens, podemos alternar entre as threads que estavam em execução naquele momento, acessando a operação Executar. Note que entre parênteses há o valor do parâmetro que está sendo passado para a operação. Abaixo temos a imagem que ilustra as três threads que temos em execução:

    Finalmente, ao selecionar uma das opções listadas na imagem acima, somos encaminhados para o código correspondente, conseguindo visualizar o estado das informações, simplesmente passando o mouse por cima das mesmas. Além disso, você ainda pode recorrer as janelas de depuração que já existem há algum tempo no Visual Studio, como é caso das janelas de Threads, Modules e Locals, que trazem informações pertinentes aquele código que está sendo depurado no momento. A imagem abaixo exibe o estado das variáveis e parâmetros da operação Executar no momento em que o arquivo de dump foi gerado:

    Utilizando o utilitário netstat

    Quando hospedamos mais que um serviço em uma mesma máquina, pode ser difícil gerenciar, e de uma forma simples, visualizar o que está ativo e aceitando requisições. Se não temos o luxo de utilizar algum sistema de gerenciamento mais eficiente, como é o caso do AppFabric, podemos recorrer à um utilitário de linha de comando que existe no sistema operacional chamado netstat.

    Este popular utilitário exibe uma lista com as conexões de rede estabelecidas no computador onde o mesmo está sendo executado. Se tivermos serviços WCF hospedados em uma determinada máquina, é possível visualizá-las através deste utilitário, e no mínimo, enxergar em qual porta eles encontram, se ela (porta) está aberta ou não e seu status. Abaixo temos um código de teste, que cria e abre vários hosts dentro de uma aplicação Console.

    static void Main(string[] args)
    {
        ServiceHost[] hosts = new ServiceHost[10];

        for (int i = 0; i < 10; i++)
        {
            var host = 
                new ServiceHost(typeof(Servico), 
                    new Uri(string.Format(“http://localhost:987{0}”, i)));

            host.AddServiceEndpoint(typeof(IContrato), new BasicHttpBinding(), “srv”);
            host.Open();
            hosts[i] = host;
        }

        Console.ReadLine();
    }

    Ao rodar esta aplicação, dez hosts são criados e abertos. Ao executar o utilitário netstat, essas conexões já são listadas, assim como podemos perceber na imagem abaixo:

    Utilizando o evento FirstChanceException

    Dependendo da tecnologia que estamos utilizando, há opções diferentes para o tratamento global de exceções, onde na maioria das vezes, nos preocupamos em efetuar o log de exceções não tratadas pela nossa aplicação. Por exceção não tratadas, entendam que sejam aquelas exceções que são disparadas e não estão envolvidas por um bloco try/catch.

    Aquelas exceções que sabemos como lidar, prontamente interceptamos e exibimos uma mensagem amigável para o usuário, podendo ele ou o sistema, resolver ou sugerir a solução para o mesmo. Muitas vezes, para essas exceções “amigáveis”, não nos preocupamos em catalogá-las e, consequentemente, não sabemos com qual frequência elas estão sendo disparadas. Informação essa que poderíamos utilizar para melhorar e tentar contornar o problema, para assim evitar que ela seja disparada frequentemente.

    Como podemos perceber, há várias situações em que nos deparamos com o tratamento de exceções. Centralizar o tratamento facilita o log, mas não permite resolver o problema no local onde ela ocorre; já o tratamento local nos permite refinar a mensagem e como resolver o problema, mas acaba com uma explosão de blocos de tratamento de erros e, eventualmente, inclui um código para logar estas exceções.

    Para facilitar isso, a partir da versão 4.0 do .NET Framework, temos um novo evento que foi adicionado à classe AppDomain, chamado de FirstChanceException. Esse evento é disparado momentos antes da CLR procurar dentro da call stack um tratador (bloco try/catch) para este tipo de exceção. Mesmo que o código que dispara a exceção estiver envolvido por um bloco catch com uma exceção específica, primeiramente o evento em questão será disparado, para em seguida, entregar a exceção para o respectivo tratador. Isso permitirá interceptar toda e qualquer exceção que aconteça, mesmo aquelas que já estejam devidamente tratadas.

    É importante dizer que este evento é meramente informativo, ou seja, isso quer dizer que se a exceção não estiver sendo tratada, ela irá danificar a sua aplicação. Não há nenhum flag que nos permita definí-la como tratada. O código abaixo ilustra a sua utilização:

    AppDomain.CurrentDomain.FirstChanceException += (obj, e) => EfetuarLog(e.Exception);
    try
    {
        throw new Exception(“Algum Erro.”);
    }
    catch (Exception)
    {
        Console.WriteLine(“Tratando o Erro.”);
    }

    Revisão do Livro: Silverlight 4.0 Curso Completo

    Eu tenho acompanhado a evolução do Silverlight ao longo de sua existência, ainda quando ele se chamava WPF/E. Eu tenho me preocupado em estudar as stacks de comunicação para o consumo de serviços WCF, deixando alguns detalhes interessantes para focar especificamente na área de comunicação.

    Para explorar um pouco melhor as capacidades do Silverlight, decidi ler um livro sobre o assunto. Como eu conheço os trabalhos do Luis Abreu, eu optei por utilizar o livro que ele escreveu sobre a versão 4.0 do Silverlight. O livro inicia com uma visão do que é o XAML e detalhes de sua estrutura. Depois dá uma volta pela customização e análise dos controles fornecidos pela tecnologia. DataBinding que também é um recurso extremamente rico em funcionalidades e facilidades, ele aborda com um carinho especial. Além disso, é falado sobre as opções de comunicação com serviços WCF. Há ainda também alguns recursos para aqueles que desejam explorar as possibilidades relacionadas à multimídia e animações. E como se não bastasse, ainda há alguns outros temas interessantes, que são essenciais para a execução e customização deste tipo de aplicação, e que possuem capítulos exclusivos, como é o caso do Out Of Browser e interação com o Javascript.

    Mesmo para aqueles que usam WPF, podem tirar proveito deste livro, onde algumas seções são comuns para ambas tecnologias. Um livro curto, fácil de ler e bem ilustrado, que conta com uma didática simples, e que ao mesmo tempo, não deixa de abordar detalhes interessantes de mais baixo nível, para aqueles que gostam de entender como as coisas funcionam nos bastidores. Minha avaliação final é 9/10.