Pense duas vezes antes de utilizar Sessions

Session é um recurso utilizado em inúmeras situações no ASP.NET, porém já faz algum tempo que seu uso não é mais recomendado, conheça os principais motivos e como você pode substituir as sessions em seu projeto.

Session é um assunto muito abordado em inúmeros fóruns de discussões em todo mundo. Foi introduzido no ASP clássico e sua utilização está presente até os dias de hoje no ASP.NET.

ASP.NET Sessions

Armazenar informações de usuário logado no sistema, itens do carrinho de compras, resultado de uma pesquisa em banco de dados e demais cenários geralmente são resolvidos através do armazenamento em sessions. Por que isso não é recomendado?

O conceito de Web é Stateless

Uma aplicação web não deve manter estado, uma aplicação web deve usar sempre que possível recursos assíncronos no client-side e server-side, isso proporcionará performance pois não satura o pipeline da aplicação e escalabilidade pois não depende de recursos disponíveis em um determinado servidor.

Sessions utilizam a memória do pool do IIS

Por padrão as sessions são armazenadas no pool do IIS e isso é uma péssima opção para armazenar dados de usuários, pois o pool do IIS recicla constantemente e isto está fora do controle do desenvolvedor, por diversos motivos o pool pode ser reciclado e todos os usuários perderão as informações armazenadas, isso é muito frustrante para o usuário da aplicação e nunca deveria acontecer.

Uma solução para isso seria utilizar sessions out-of-proc (armazenando as sessions em um SQL Server ou outro State Server), mas não significa que que é uma boa alternativa, existem alguns problemas nessa abordagem:

  1. Irá custar 2 requisições extras de rede, uma para carregar a session antes do request ser processado e outra para armazenar novamente o estado da session após o request. E isso irá ocorrer a cada request mesmo que não esteja utilizando a session em determinada página.
  2. Não é performático, a cada request a session precisa ser serializada e deserializada, isso irá custar mais recursos de CPU e memória.
  3. Bancos de dados relacionais não são mais rápidos que o acesso de memória, não é performático realizar 2 hits no banco a cada request de cada usuário.
  4. Poderá saturar o pipeline da aplicação, uma vez que um request depende da leitura de um banco de dados para finalizar seu ciclo de vida.

Sessions são de acesso exclusivo

Sessions irão prejudicar a performance de requests concorrentes. Suponha que uma página via AJAX dispare 2 requests para o mesmo usuário, o acesso de leitura da session é exclusivo, logo o ASP.NET irá forçar que o segundo request aguarde enquanto o primeiro faz a leitura da session, isso implica na escalabilidade da aplicação.
Recursos do HTML 5 permitem conexões permanentes (Server Sent Events, WebSockets) isso significa que problemas podem ocorrer no caso de requests concorrentes.

Sessions não são escaláveis

Sessions trabalhando no modo in-proc (habilitado por padrão) irão prejudicar a escalabilidade da aplicação, uma vez que a memória utilizada pela session é de um servidor específico. Suponha que existam N servidores da mesma aplicação que estejam sendo suportados por um load balancing, uma vez que a session foi criada no servidorX a aplicação só funcionará se todos os requests forem redirecionados para o servidorX, existem meios de realizar isto, porém essa abordagem desconfigura o conceito de escalabilidade.

Sessions degradam a performance da aplicação

Em muitas aplicações é possível notar o IIS batendo o topo de utilização de memória, muitas vezes a utilização da memória não é causada pelos recursos do IIS e sim de alocação de dados via sessions. Desenvolvedores realizam uma pesquisa no banco e guardam o resultado numa session para poder reaproveitar a pesquisa, porém quase sempre esquecem de destruir aquela sessão. Essa facilidade que as sessions proveem acaba sendo uma armadilha para boa performance da aplicação uma vez que a memória disponível para rodar a aplicação é dividida e consumida pelas sessions.

Sessions não são necessárias? Como posso substituí-las? Quais recursos utilizar?

Com certeza as sessions não são necessárias e obviamente não devem ser utilizadas pelos motivos apresentados acima. Porém a abordagem para substituir as sessions depende do cenário. Apresentarei algumas possibilidades.

  • Controle de usuários logados, armazenamento de informações de usuários.
    A utilização de Cookies é uma excelente alternativa para persistência de usuários logados, a informação fica no client-side e permite escalabilidade e ao mesmo tempo segurança. Grandes sites como Facebook utilizam Cookies para persistência de usuários.
    Caso queira armazenar informações de usuários no Cookie é possível porém existe uma limitação de tamanho (4K), talvez seja interessante armazenar uma chave no Cookie onde através dela seja possível localizar mais informações em outro recurso de armazenamento.
    O ASP.NET Identity é uma ótima alternativa para esse cenário, ele trabalha com Cookies para persistência do usuário e também com Claims para armazenamento de dados (nome, e-mail, permissões e etc…).

  • Controle de carrinho de compras.
    Carrinho de compras requerem um tratamento especial, pois não importa se a compra foi concluída ou não é sempre importante obter informações sobre carrinhos abandonados para trabalhar na analise dessas informações.
    Minha recomendação é armazenar no banco, em uma tabela destinada para esse tipo de controle, uma vez que o usuário logado retorna ao site é possível oferecer que ele restaure o carrinho abandonado, entre outras possibilidades.
    É possível também controlar o carrinho de usuários não logados através do recurso Anonymous Identification Module do ASP.NET onde é criado um Cookie com um ID único e pode ser recuperado através do Request.AnonymousID.

  • Armazenamento de objetos complexos.
    A solução para isto é cache. Existem diversas soluções para trabalhar com Cache (ASP.NET Data Cache, NCache, Memcached, Redis Cache), um destaque especial para o Redis Cache que se demonstrou uma solução muito eficiente, performática e fácil de implementar, inclusive existe recursos no Azure e AWS para utilização desta ferramenta para cache distribuído e etc.
    O recurso de Cache sempre esteve presente no ASP.NET, sua utilização requer um pouco mais de esforço de implementação do que no uso das sessions, porém convenhamos, programar é um exercício intelectual, pratique isto!

Outras soluções para considerar

Existem outras soluções a serem consideradas, algumas muito simples como Hidden Fields e QueryStrings, onde muitas vezes são o suficiente para resolver o problema.

Outra ótima alternativa no ASP.NET MVC é utilizar ViewData, ViewBag e TempData

Existe uma espécie de armazenamento local (muito maior que um Cookie) chamado WebStorage onde é possível trabalhar com sessionStorage e localStorage, é muito interessante conhecer esse recurso, recomendo a todos que pesquisem sobre o assunto.

Para finalizar…

Elimine a possibilidade de utilizar sessions em sua aplicação, a Web está cada vez mais moderna, escalável e responsiva e é importante tomar decisões que não nos façam andar na contra-mão da Web.

Eu me reuni com outros colegas para bater um papo sobre esse assunto, confira como foi o bate papo.

* Assine meu canal no Youtube 🙂

Vamos continuar com a troca de conhecimentos, utilize o formulário abaixo para postar sua opinião, crítica e lógico seu feedback (adoramos feedback 🙂 ).

Referência

19 ideias sobre “Pense duas vezes antes de utilizar Sessions

  1. Olá Eduardo!

    Parabéns pelo post!

    Vejo que vc sempre bate na tecla de não se utilizar Sessions e eu mesmo as utilizou em um cenário específico e gostaria de debatermos sobre ele até mesmo para vc me dar uma luz sobre uma melhor alternativa, pode ser? 🙂

    Um cenário real na qual participei foi o seguinte:

    – Tenho um cadastro de clientes
    – 1 cliente pode ter N endereços
    – 1 cliente pode ter N contatos
    – 1 cliente pode ter N qualquer coisa…

    Então, pensando já na tela em si, eu tinha várias abas e cada aba era uma função, então eu tinha a aba para adicionar os endereços, tinha a aba para adicionar os contatos etc.

    Tomando a adição de endereço, como exemplo, tudo bem? Então, tenho minha aba para adicionar N endereços para aquele cliente. E, cada vez que eu adicionava um cliente, ele ia sendo mostrado num grid temporariamente e onde essa lista de endereços era armazenada temporariamente? Em Session! E, isso era feito em todas as abas (contatos, etc). E, nesta tela de cliente, eu tenho um botão “Salvar” geral que pegava todos os dados principais do cliente e mais todas essas listas temporárias das Sessions e gravava no banco.

    Neste tipo de cenário, onde eu tenho uma adição 1-N e que eu não posso já adicionar no BD direto, como não utilizar Session? Eu acho que vc meio que deu a resposta ao falar sobre Cache, porém nunca utilizei. Seria a melhor alternativa? A aplicação “sofre” menos se utilizarmos Cache ao invés da Session neste caso?

    Abs!

    E

    • Não querendo responder pelo Eduardo..rsrss
      Mas você pode utilizar um helper chamado BeginCollectionItem para fazer essa adição de inúmeros campos e então sim, fazer o post

      É o que eu utilizo, =) rs

      Também há a opção de utilizar knockoutjs porexemplo, também faz a mesma coisa, procure por crud master details

    • Rapaz, esta é uma das coisas que gostaria de ver como se faz no MVC. Gravar tabelas master/detail numa única transação, inclusive utilizando o Entity Framework. Você conseguiu fazer utilizando Cache? Teria um exemplo?

      Obrigado.

  2. Como sempre um ótimo artigo Eduardo Pires, sei que esse assunto é algo que você sempre menciona nos seus cursos e no AspNetCast.

    Obrigado por compartilha conosco esse conhecimento.

  3. Fala Eduardo! Muito bom o post e o vídeo também foi uma discussão bem bacana.

    Mas acho que o grande desafio que o desenvolvedor tem é saber “dosar” o que colocar no client e o que colocar no servidor, e com a quantidade de opções que se tem hoje em dia as vezes se torna uma tarefa difícil.
    Já vi discussões justamente sobre sobrecarregar o client com diversos scripts e processamentos, sendo um problema principalmente para aplicações mobile.

    Abraço

  4. Nossa estavamos discultindo exatamente isso uns dias atrás, esse artigo irá me ajudar a exemplificar meu ponto com mais detalhes. Aliás, o usu do Redis, está sendo sensacional. Parabéns e obrigado =)

  5. Olá Eduardo.
    Cara meus parabéns, eis o post que estava procurando já a algum tempo. Já tive muitos problemas relacionados a reciclagem de sessões, principalmente quando se trata de ambientes compartilhado (ex: esses planos baratos de hospedagens), era algo muito irritante, creio que grande parte disso seria o fato de utilizar o nhibernate nas aplicações pois sabemos que o mesmo consome “um pouco” de memória ao cachear os mapeamentos. Devido a todos esses problemas, resolvi não mais utilizar sessões em momento algum, e esse post veio a cair muito bem. Obrigado cara.

  6. Eduardo, ótimo post, muito bom mesmo.

    Estou em uma aplicação multi tenant que precisavamos particinar o tenantId, eu escolhi utilizar o próprio cookie do identity, rs

    E quanto ao RedisSessionStateProvider ? Que utiliza session e redis? O que você acha? PS: Não tenho conheicmento quanto ao RedisSessionStateProvider, li um pouco e me pareceu uma alternativa.

    Abraço

  7. Sou Arquiteto de Software e sempre dei foco para o não uso da session. Inclusive uma forma volátil de uso é a serialização de objetos usando JSON e armazenando em um hiddenField. Em testes de performance se mostrou extremamente rápido e simples de usar.

  8. Mto bom o post.
    No meu caso utilizo sessions para guardar valores inseridos pelo usuário na página X que serão salvos no BD ao clicar em um botão da página Y.

    Teria alguma idéia pra esse cenário?

    Obrigado

  9. Eu tenho uma aplicação hospedada na nuvem (Azure). Configurei a SessionState para o Cache do Azure. Como consigo testar se uma aplicação com várias instâncias realmente não perde a Sessão do usuário logado ao existir sobrecarga de um Servidor?

    Obrigada

    –Adriana

  10. Olá.

    Estou iniciando no desenvolvimento de um “mini erp online” em .net, queria uma dica de como estruturar o projeto, por exemplo, é indicado que cada usuario tenha seu próprio banco de dados? Porém quando ele for realizar login no sistema, como posso conectar no banco de dados dele que estará na nuvem?

  11. Penso que a opção com cookies não é viável, pois depende de estarem habilitados na máquina do usuário e são uma brecha de segurança, dá para roubar os cookies dos usuários e pelo menos ‘presumir’ onde as pessoas estão navegando, fazer um tipo de bisbilhotagem alheia, embora também seja possível roubar outros objetos armazenados do browser. O meu problema é alcançar uma autenticação satisfatória de segurança. Não cheguei ainda a uma conclusão sobre qual tecnologia seria a melhor, mas talzez os websockts sejam uma alternativa para reconhecer a autenticação do usuário, estou estudando se é viável fazer um tipo de conexão SOAP através do browser ao invés de usar somente REST. Ainda estou estudando uma melhor forma de autenticar com segurança com menos programação possível, é uma boa discussão de ideias.

  12. Olá pessoal, outra coisa que vocês poderiam compartilhar, seria como vocês fazem para autenticar usuários, independente de plataforma, algo que funcione cross platform. O ASP.NET Identity pode ser muito bom para autenticação, mas não tenho como usar já que o server precisa ser Microsoft e tenho server linux, logo uso APACHE entre opções. Já pensei também em outras situações, em usar o MONO (http://www.mono-project.com) ligado ao APACHE, já fiz alguns testes em C# com MONO para rodar no APACHE em linux, algumas coisas funcionam e outras não, alguém aí já tem alguma experiência de manter algum site desta forma ?

  13. Bom dia, a melhor forma de controlar usuários logados no ASP .NET MVC então é a utilização de Cookies? Existem casos em que o Cookie não está habilitado? Alguém teria um exemplo em MVC de controle de acesso?

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *