CDN: como usar para reduzir o custo de infra, aumentar a escalabilidade e melhorar a performance
Imagine um portal com uma grande taxa de atualização de conteúdo, na casa das centenas de atualizações por dia, e com um grande volume de acessos, chegando ou até passando de um milhão por dia. Parece um caso bem complexo para se lidar, certo? E é mesmo!
Esse tipo de volume pode fazer os custos de infraestrutura deste portal subirem de maneira indefinida e insustentável. Por isso é de extrema importância que se considerem os riscos e desafios da escalabilidade na hora de criar uma arquitetura, para que uma grande audiência signifique algo bom para o negócio, ao invés de um empecilho.
E como uma CDN pode nos ajudar nesse processo? É isso que veremos neste artigo, mas antes, vamos entender mais sobre os riscos e os impactos para chegarmos nas estratégias
Quais os riscos de ter uma grande audiência em portais de conteúdo?
Sobrecarga por utilização
Conforme a audiência aumenta, o mesmo acontece com o número de requisições ao portal. Dependendo de quais operações forem necessárias para responder tais requisições (renderização de páginas, consulta a banco de dados, até mesmo terceirizar uma parte do resultado a serviços externos), o custo de manter uma infraestrutura compatível pode subir exponencialmente, podendo provocar a inviabilidade de manter o produto.
Repetição de operações idênticas
Está diretamente relacionado ao ponto anterior. Quando falamos em portais de conteúdo, geralmente a intenção é que o conteúdo chegue ao máximo de pessoas possível.
Aqui podemos ver um desafio fundamental de desempenho de sistemas deste tipo: evitar operações idênticas, afinal, não é necessário fazer a mesma consulta ao banco de dados para retornar o mesmo conteúdo para diversos acessos. Evitar que isso aconteça requer uma combinação de estratégias, das quais falaremos mais no decorrer do post.
Como uma grande taxa de atualização de conteúdo pode impactar um portal?
Atraso entre atualização no sistema e visualização por usuários
Quando nos deparamos com o problema de execução de operações idênticas, a resposta pode ser considerada óbvia: cache. Seja algum tipo de índice com resultados de busca a um banco de dados (evitando fazer relacionamentos complexos), algum serviço que possa fazer o próprio servidor responder uma requisição idêntica, como já responde a anterior (evitando consultar o backend do portal novamente), ou qualquer outra solução do tipo.
O cache resolve um problema, porém, gera outro: ele precisa ser invalidado corretamente, o que, em um portal de conteúdo, pode atingir um altíssimo nível de complexidade.
Falhas na invalidação podem fazer com que o conteúdo não seja atualizado corretamente, ao mesmo tempo que uma estratégia muito simples pode fazer com que o conteúdo tenha um tempo de “limbo”, onde deve esperar por minutos, ou até horas, pela próxima invalidação.
Tempo de carregamento lento para as páginas
Por outro lado, um sistema que invalida cache com muita frequência pode resultar numa experiência de lentidão para quem o acessa. Este seria um “sub-problema” da invalidação: como não perder desempenho enquanto o cache precisa ser reconstruído?
Estratégias comuns para tratar problemas de desempenho num servidor
Escalabilidade vertical
Computer History Museum
Fonte: wikimedia.org
Consiste em aumentar a capacidade local da(s) máquina(s) que hospeda(m) o sistema, o aumento de hardware: memória ram, processadores mais rápidos ou com mais capacidade de paralelismo, ou até mesmo discos com melhores velocidades de leitura e escrita.
Escalabilidade horizontal
Fonte: Foto de Manuel Geissinger no Pexels
Escalar horizontalmente significa, a grosso modo, usar réplicas de um servidor para atender a uma maior demanda. Geralmente inclui o posicionamento geográfico estratégico dos servidores, visando diminuir a quantidade de estrutura e nós necessários para que clientes e servidores se comuniquem.
Camadas de cache
Como abordado anteriormente, cache é uma estratégia praticamente padrão para endereçar sobrecargas. Também é importante mencionar que esta estratégia pode ser abordada em vários níveis. Pode haver um cache criado pelo backend dentro de uma tabela ou índice no banco de dados da aplicação, algum serviço que crie cache dos objetos criados pela aplicação na própria memória ram.
Ou até mesmo as requisições podem ser cacheadas no nível do servidor, evitando que seja necessário rodar a aplicação para respondê-las.
Essa última abordagem traz uma vantagem interessante: se estamos fazendo cache das requisições, basta seguirmos o protocolo e o sistema de cache poderá ficar em qualquer lugar, especialmente em um servidor diferente! Isso possibilita, também, que seja feito de forma distribuída, e é aí que entram as CDNs, que são o motivo deste artigo e irei aprofundar mais pra frente.
CDNs também ajudam noutro problema: a complexidade acidental. Pelo fato de um serviço de CDN consistir numa camada agnóstica e distribuída, ele pode fazer o papel de outras camadas de cache que estariam espalhadas entre os serviços da aplicação e provavelmente com um alto nível de acoplamento.
Arquiteturas desacopladas
Quando não temos todas as partes do sistema centralizadas no mesmo lugar, temos oportunidade de fazer controles de cache mais direcionados às necessidades específicas de cada serviço.
Por exemplo: se temos um servidor para renderização de páginas e outro como API de conteúdo, uma alteração de algum retorno que só é feito pelo servidor de renderização não precisa disparar nenhum tipo de invalidação de cache que possa invalidar o conteúdo da API.
Onde fica a responsabilidade de tratar tais problemas?
Nível da aplicação
Uma aplicação pode implementar várias maneiras de controlar cache, e geralmente os frameworks o fazem. A aplicação pode modelar sua própria tabela de cache no banco de dados, conectar-se a algum serviço que faça cache na memória ou implementar algum padrão para não ler os dados no mesmo lugar que os grava, por exemplo.
Soluções agnósticas
Eu chamo de solução agnóstica a aplicação de algum programa ou serviço que possa fazer cache das requisições, independentemente de qual aplicação (qualquer linguagem ou framework) as faz ou consome, apenas fazendo uso do protocolo no qual as requisições são feitas.
Nesse caso, a responsabilidade pode ficar no próprio servidor da aplicação ou em algum programa mais especializado que possa proporcionar uma solução mais avançada.
Soluções distribuídas
Se é possível fazer cache de uma aplicação apenas olhando para suas requisições, é possível fazer isso em máquinas diferentes daquelas utilizadas pela mesma. Nessa estratégia, podemos evitar fazer escalabilidade horizontal de servidores e usar uma rede que faça esse cache de maneira que apenas um mínimo necessário de requisições chegue à(s) máquina(s) da aplicação.
Isso não necessariamente elimina a necessidade de escalabilidade horizontal, mas pode ajudar a diminuir drasticamente o número de máquinas necessárias com a estratégia correta.
E se isso fosse delegado a um serviço?
Nossa recomendação
Em termos de infraestrutura, estamos falando de uma CDN (Content Delivery Network) ou, em Português, rede de fornecimento de conteúdo, que significa uma rede capaz de guardar um cache das requisições de um ou mais domínios. Esse conceito se encaixa nos parâmetros agnóstico (não depende de detalhes de implementação da aplicação para existir) e distribuído (o cache está disponível em vários servidores espalhados pelo mundo) discutidos acima.
Existem vários serviços de CDN hoje em dia, e podemos contratá-los para usá-los como camada de cache de qualquer site ou API. Estes serviços podem ter abordagens diferentes para tratar o mesmo problema, e a Taller recomenda um de nossos parceiros: a Fastly, um dos serviços de CDN mais usados no mundo hoje em dia.
Fonte: Elaborada pelo autor.
Legenda: exemplo do painel de configurações de um serviço na Fastly. Nele podemos observar algumas funcionalidades, como a capacidade de gerenciar múltiplos domínios ou o controle de versão das configurações.
Fastly e seus diferenciais
A Fastly oferece um produto de CDN que combina muito bem com as necessidades de um grande portal de conteúdo. Algumas das principais vantagens que identificamos em nossa experiência foram:
Camada de edge programável e total controle dos cabeçalhos
Na Fastly, temos domínio das lógicas na camada de edge através de snippets escritos em VCL, ou podendo até mesmo usar algumas linguagens de programação (https://www.fastly.com/edge-cloud-platform/). Dessa forma, podemos implementar regras de cache baseadas nas informações transmitidas com as requisições. Além disso, há total controle dos cabeçalhos das requisições, facilitando a criação e combinação de políticas de cache.
Versionamento de configurações
O sistema de gerenciamento das configurações programáveis funciona de maneira versionada, ou seja, cada modificação gera uma nova versão da configuração e podemos escolher qual versão ficará ativa, bem como fazer rollback para versões anteriores ou criar uma versão de rascunho.
Soft-purge + stale-while-revalidate e stale-if-error
Ao invalidar cache, temos duas abordagens principais: hard-purge e soft-purge. A primeira quer dizer que queremos invalidar o cache e parar de entregá-lo aos usuários. A segunda significa invalidar o cache porém ainda com a possibilidade de entregar ele aos usuários dependendo da política daquela requisição.
Aí que entra o stale-while-revalidate: com essa política ativada, o cache inválido é entregue aos usuários enquanto apenas uma requisição em background é enviada da Fastly para o servidor da aplicação para criar um novo cache válido.
O stale-if-error funciona de maneira parecida, entregando um cache antigo caso a requisição para o cache novo retorne algum erro.
Consideramos essa combinação de recursos uma das estratégias mais poderosas para se endereçar problemas de desempenho em portais de conteúdo.
Origin Shielding
Uma característica fundamental de uma CDN é o fato dela ser uma rede de servidores espalhados pelo mundo. Isso quer dizer que, dependendo de onde o usuário acessa, o cache será servido a partir de um local diferente.
A funcionalidade de shielding faz com que o cache que está na Fastly seja compartilhado entre esses servidores usando a própria rede da CDN, sem que todos eles precisem enviar requisições ao servidor da aplicação quando ocorre alguma invalidação.
Invalidação por tags
Tendo em mente todos esses recursos de como manter, gerenciar e revalidar cache, resta uma questão: como invalidar o conteúdo de maneira eficiente?
Um dos recursos mais avançados conhecidos para isso é adicionar tags junto aos cabeçalhos de uma requisição para identificar quais conteúdos ela carrega, fazendo com que seja possível identificar se aquele conteúdo específico tem ou não um cache válido para ser entregue.
Dessa maneira, ao limpar o cache dizemos apenas para o sistema invalidar alguma tag que representa o conteúdo que está sendo atualizado.
E a Fastly oferece suporte a essa estratégia! Através do cabeçalho Surrogate-Key informamos quais tags devem ser consideradas na hora de checar se é necessário atualizar o cache de uma determinada requisição. Isso possibilita que uma implementação dessa estratégia feita em algum CMS, por exemplo, seja seguida pela CDN de maneira transparente.
Conluindo
Usando esses recursos de maneira alinhada com as necessidades dos produtos de nossos clientes, fomos capazes de desonerar servidores evitando que executem uma grande quantidade de tarefas repetitivas, como renderizar a mesma página com o mesmo conteúdo, ou então repetir uma busca em uma base de dados usando as mesmas variáveis diversas vezes.
Dessa forma, conseguimos chegar num cenário onde a audiência em si não é o que faz ser necessária uma grande capacidade de servidores, e sim outros fatores como a quantidade de conteúdo e frequência de expiração do mesmo.
Acompanhe nossos outros conteúdos aqui no blog sobre Desenvolvimento de Software:
E nossos posts recomendados: