Idiomas: English · Nederlands · Deutsch · Español · Français · Italiano · Português · Papiamentu · Frysk

Índice / Artigos / Design de aplicações escaláveis sem magia

Design de aplicações escaláveis sem magia

Aproveitar o poder de cálculo do cliente para alto desempenho com muitos utilizadores

Publicado
Atualizado
(criado )
Escrito por
Willem L. Middelkoop
Temas
nuvem · dados · design · tutorial · iOS · lemmid-store · programação · servidor · smartphone · web · webdesign
Versão humana deste artigo
https://willem.com/pt/2020-05-11_design-de-aplicacoes-escalaveis-sem-magia/
English
Scalable application design without magic
Nederlands
Schaalbaar applicatieontwerp zonder magie
Deutsch
Skalierbares Anwendungsdesign ohne Magie
Español
Diseño de aplicaciones escalables sin magia
Français
Concevoir une application scalable sans magie
Italiano
Progettare applicazioni scalabili senza magia
Papiamentu
Desèño di aplikashon eskalabel sin magia
Frysk
Skaalbere applikaasje-ûntwerp sûnder magy
Idiomas
Traduzido do original em inglês por um modelo de linguagem em duas passagens (tradução e revisão) com um glossário e um corretor ortográfico; ainda não revisto por um falante nativo.

Resumo

No âmbito da app de pedidos de comida online que estou a construir, precisei de desenhar uma infraestrutura backend escalável, capaz de lidar com muitos utilizadores em simultâneo. A escalabilidade é vista como um problema difícil de resolver. Muitas vezes é apresentada como algo mágico, feito por empresas de milhões de dólares com ferramentas secretas. Mas magia não existe, ou existe?

Texto completo

O que é a escalabilidade?

Se estás a construir uma app, provavelmente começas bem pequeno. Só alguns utilizadores (ou só tu, o programador) a trabalhar com a app. Está tudo ótimo: a app funciona bem, é rápida e não há problemas. (aprecia este sentimento!)

Um utilizador a interagir com um servidor
Um utilizador a interagir com um servidor

Neste cenário inicial e pequeno, o único servidor que corre a app consegue aguentar o utilizador que interage com ela. De cada vez que o utilizador toca ou clica num botão, o servidor único faz algum trabalho. Se programares bem, estas cargas de trabalho podem ser otimizadas em pequenos blocos eficientes. Fazê-lo assim é um exemplo típico de uma arquitetura de software monolítica.

Mesmo que o teu projeto ou app seja bastante pequeno, a certa altura pode chegar uma afluência de utilizadores. Se o teu sistema não aguenta cargas elevadas, tem grandes probabilidades de falhar.

Servidor sob carga elevada a tratar de vários utilizadores em simultâneo
Servidor sob carga elevada a tratar de vários utilizadores em simultâneo

Por causa do design monolítico da app, todos os utilizadores interagem com o mesmo servidor. Toda a sua interação (toques, cliques, entradas) é tratada por esse servidor. Com uma carga maior, ele tem de trabalhar muito mais. A certa altura, vais reparar que as coisas abrandam, porque o servidor não consegue acompanhar a quantidade de trabalho que tem de fazer. Isto significa que a tua app não escala bem, ou seja: estás em apuros.

Falhar no "momento supremo"

Não escalar bem é um grande problema que ninguém deve subestimar. A maioria das apps (e dos seus modelos de negócio) depende de grandes volumes com pouca receita por utilizador. Se queres que a tua app seja um sucesso (financeiro), tens de garantir que funciona bem quando há um aumento súbito de tráfego. Se a tua app falha quando de repente recebe a atenção das massas, vais perder uma oportunidade "única na vida" de crescimento (orgânico).

De certa forma, desenhar para a escalabilidade é como manteres o cinto de segurança apertado quando estás sentado no teu foguetão construído por ti, com motores experimentais. Nunca sabes bem quando vai acender, mas quando acender é melhor que estejas bem preso (e aproveita a viagem!).

Formas comuns de alcançar a escalabilidade

Já se escreveu muito sobre como construir apps escaláveis, e as formas comuns de melhorar a escalabilidade são:

  • Aumentar a capacidade do servidor: mais memória, mais CPU, mais armazenamento. Funciona, mas só te leva "até certo ponto".
  • Adicionar mais servidores: em vez de um único servidor a fazer de gargalo, podes juntar mais servidores que partilhem a carga de trabalho. É mais fácil de dizer do que de fazer, porque exige balanceamento de carga e partilha de dados, o que pode ser complicado (por exemplo, se tens um contador de pedidos e dois servidores a contar, qual deles determina o número seguinte?)
  • Otimizar o código: usa as ferramentas certas! Escolhe uma linguagem de programação orientada para o desempenho e software de servidor à altura. Estuda a programação funcional para o teu código poder correr em vários núcleos de forma assíncrona. Torna-o sem estado. Faz benchmarks. Otimiza. Cada milissegundo ganho num único pedido soma-se depressa quando lidas com grandes volumes.
  • Usar a base de dados e separá-la do servidor da aplicação: se usas uma base de dados, aproveita o seu poder! Tira partido da cache de consultas, dos índices e das capacidades de pesquisa. A maioria das soluções de bases de dados oferece opções de clustering à prova de bala; não tentes reinventar a roda que outros já aperfeiçoaram.

O melhor que podes fazer é ponderar todas estas opções. Mesmo que não vás juntar mais servidores já, deves escrever o teu código para o suportar mais tarde. Fazer benchmarks e otimizar o código deve ser parte integrante do teu trabalho, e não uma ideia de última hora.

Aproveitar o poder de cálculo do cliente

Depois de ponderares todas as formas comuns de alcançar a escalabilidade, há "mais uma coisa".

Os smartphones (e tablets, PC) tornaram-se mais poderosos ao longo dos anos (imagem: Apple)
Os smartphones (e tablets, PC) tornaram-se mais poderosos ao longo dos anos (imagem: Apple)

Poucos programadores pensam nisto: o problema da escalabilidade contém a solução! Quando tens 1000 utilizadores, tens 1000 computadores com CPU potentes e muita memória. Os computadores e smartphones modernos tornaram-se cada vez mais capazes.

Só tens de encontrar uma forma de tirar partido do poder de cálculo do cliente. Pode parecer uma maldade (usar o poder de cálculo do utilizador), mas vai dar-lhe uma experiência muito melhor e mais rápida. O melhor é que o poder de cálculo deles é gasto na experiência fantástica deles, e resolve o teu problema de escalabilidade como feliz efeito secundário.

Aproveitar o dispositivo do utilizador: o telefone do utilizador faz a maior parte do trabalho que originalmente era do servidor
Aproveitar o dispositivo do utilizador: o telefone do utilizador faz a maior parte do trabalho que originalmente era do servidor

Ao aproveitares o poder de cálculo do utilizador, reduzes a quantidade de trabalho que o servidor tem de fazer. Em vez de o servidor fazer um pouco de trabalho de cada vez que o utilizador toca ou clica num botão, este trabalho passa a ser feito sobretudo pelo dispositivo do utilizador. Isto é mais eficiente do que comunicar com um servidor por Wi-Fi/4G (poupa bateria e tempo).

Exige repensar um pouco, mas este princípio também se pode aplicar a aplicações web. Em vez de tratares de tudo no servidor, podes fazer muito trabalho no cliente. Coisas como pesquisar, navegar, filtrar, mover-se pela página e até adicionar artigos ao carrinho podem ser feitas com JavaScript do lado do cliente. Tu, caro programador, tens de procurar formas de o fazer funcionar, mas se conseguires terás escalabilidade quase infinita ao teu alcance.

Na prática: a app de pedidos de comida

Para a app de pedidos de comida procurei formas de aproveitar o poder de cálculo do cliente para alcançar alto desempenho. O principal desafio dos pedidos de entrega e para levar é que têm picos à hora do jantar. Muita gente usa a app mais ou menos à mesma hora, e isso é uma receita para problemas de escalabilidade.

Pedir comida online
Pedir comida online

Se pensares em pedir comida online, podes dividir todo o processo em blocos mais pequenos:

  • 1) Abrir a página: carregar a app/página
  • 2) Explorar as diferentes opções: ver a lista de produtos, as descrições, os preços, as fotografias, etc.
  • 3) Procurar algo específico: pesquisar por categoria, nome do produto, etc.
  • 4) Escolher um produto: juntá-lo ao teu pedido
  • 5) Personalizar um produto: escolher um molho, um acompanhamento, uma cobertura, etc.
  • 6) Introduzir os teus dados: o teu nome, número de telefone, dados de entrega, etc.
  • 7) Pagar o pedido: escolher um método de pagamento, ligar ao banco ou ao cartão de crédito
  • 8) Fechar a app

A maior parte destes passos pode ser programada de forma a que o servidor não seja necessário. Só o passo 1 (carregar) e o passo 7 (pagar) exigem contacto com a infraestrutura backend. É importante perceber que os passos 2 a 5 se repetem várias vezes, pois é normal os utilizadores juntarem vários produtos ao pedido.

Separar a carga de trabalho restante do servidor: servir produtos e tratar de pedidos
Separar a carga de trabalho restante do servidor: servir produtos e tratar de pedidos

Separando a carga de trabalho restante por tarefa, o software pode ser ainda mais otimizado. O "servidor de produtos" está otimizado para servir recursos estáticos (textos, preços, imagens) que permitem ao cliente criar a experiência de navegação pelos produtos. Este tipo de servidor pode ser muito otimizado, com cache, HTTP/2, compressão, etc.

Só os utilizadores que concluem o processo de pedido são encaminhados para o "servidor de pedidos", que trata do pagamento. Este liga-se ao prestador de pagamentos. Quando um pagamento é concluído, o prestador de pagamentos contacta o servidor de pedidos para o informar do estado do pagamento. É algo sobre o qual queres ter controlo total (do lado do servidor), como fiz quando desenhei um sistema de pagamentos há algum tempo. A carga de trabalho restante no "servidor de pedidos" é muito menor, pois nem toda a gente faz um pedido, e grande parte do seu processamento pode ser feita em segundo plano.

Lidar com muito tráfego através de computação distribuída
Lidar com muito tráfego através de computação distribuída

Para maximizar a escalabilidade, podes facilmente juntar vários servidores (ou usar uma rede de distribuição de conteúdos) para servir os recursos estáticos. Para a app de pedidos de comida uso um "servidor de produtos" separado por restaurante. Isto dá alto desempenho e reduz os tempos de carregamento. As pessoas notam que é rápido, algumas nem acreditam que é tecnologia web, é quase mágico.

Conclusão

É possível desenhar uma aplicação escalável sem magia nem orçamentos de milhões. Só tens de pensar bem e procurar as oportunidades que surgem juntamente com os desafios.

O problema contém a solução. Lidar com muitos utilizadores em simultâneo não é só um problema: eles trazem o seu próprio poder de cálculo, é a solução! Só tens de o usar.

Formatos legíveis por máquinas desta página