Serverless na prática: como um evento vira resposta, e quando não usar
O que é serverless de verdade, o caminho de uma requisição sem servidor provisionado, dois exemplos com AWS Lambda e os cenários em que um servidor comum ainda ganha.
Serverless é um nome ruim. Os servidores continuam existindo; a diferença é que não é você quem cuida deles. Você entrega uma função, diz o que deve disparar essa função, e o provedor de nuvem se encarrega de provisionar, escalar, monitorar e desligar a máquina.
Este texto nasceu de um seminário que apresentei em grupo na disciplina de Engenharia de Software, no curso de Análise e Desenvolvimento de Sistemas da UDESC. O deck está em serverless-soft.vercel.app. Aqui eu organizo o conteúdo em formato de artigo e acrescento o que aprendi usando isso fora da sala.
As peças
- Evento: o que dispara a execução. Uma requisição HTTP, um arquivo enviado, uma mensagem numa fila, um horário agendado.
- API Gateway: a porta de entrada HTTP. Recebe a requisição, autentica e encaminha para a função certa.
- Função: a lógica de negócio. Pequena, isolada e sem estado.
- Banco e armazenamento: onde o estado mora, já que a função não guarda nada entre uma execução e outra. O armazenamento também pode gerar eventos.
- Provedor: provisiona, escala, monitora e mantém toda a infraestrutura.
Do evento até a resposta
Uma requisição passa por cinco etapas: o usuário envia, o gateway autentica e roteia, a função executa, lê ou grava no banco, e a resposta volta. A função só existe enquanto executa. Terminou o trabalho, o ambiente é liberado.
A consequência mais interessante aparece com carga. Mil requisições simultâneas viram mil execuções em paralelo, sem nenhuma configuração de autoscaling. Num servidor tradicional, esse pico exigiria máquinas que passam o resto do dia ociosas.
Exemplo 1: miniatura a partir de um upload
O caso clássico. Uma imagem chega no S3, o S3 emite um evento, a Lambda gera a miniatura e grava o registro.
def handler(event, context):
info = event["Records"][0]["s3"]
bucket, key = info["bucket"]["name"], info["object"]["key"]
obj = s3.get_object(Bucket=bucket, Key=key)
img = Image.open(io.BytesIO(obj["Body"].read()))
img.thumbnail((200, 200))
buf = io.BytesIO()
img.save(buf, format="JPEG")
# grava em OUTRO bucket: no mesmo, a miniatura dispararia a função de novo
s3.put_object(Bucket="miniaturas", Key=key, Body=buf.getvalue())
tabela.put_item(Item={"arquivo": key})Exemplo 2: um relatório que roda uma vez por dia
Uma regra do EventBridge dispara a função todo dia de manhã. A função consulta as vendas do dia anterior, monta o relatório e guarda no S3.
def handler(event, context):
ontem = date.today() - timedelta(days=1)
vendas = consulta_vendas(ontem)
relatorio = {"data": str(ontem), "total": sum(v["valor"] for v in vendas)}
s3.put_object(
Bucket="relatorios",
Key=f"vendas/{ontem}.json",
Body=json.dumps(relatorio),
)cron(0 11 * * ? *)
# minutos horas dia mês dia-da-semana ano
# 11h UTC = 8h em BrasíliaO código roda só nos minutos em que o trabalho acontece. Um servidor comum ficaria ligado 24 horas para trabalhar alguns minutos por dia. E repare no fuso: a expressão é em UTC, um erro fácil de cometer e chato de descobrir.
O que se ganha e o que se paga
- Menos infraestrutura para gerenciar
- Escala automática com a demanda
- Pagamento pelo uso, sem máquina ociosa
- Publicação rápida de funcionalidades
- Dependência do provedor
- Limites de tempo e memória por execução
- Cold start na primeira chamada
- Depuração difícil num sistema distribuído
- Custo que cresce com o volume
O cold start merece atenção. Quando a função não tem um ambiente pronto, o provedor precisa criar um antes de executar, e essa primeira chamada pode levar centenas de milissegundos a mais que as seguintes. Para um relatório diário, não importa. Para uma API que precisa responder rápido em toda requisição, importa muito.
Quando faz sentido
A pergunta não é se serverless é melhor. É em qual cenário as características dele ajudam.
- APIs e back-ends de apps com demanda irregular
- Processamento de arquivos e imagens
- Tarefas agendadas e automações
- Sistemas orientados a eventos
- A aplicação precisa rodar continuamente
- A latência precisa ser baixa e previsível
- O processamento passa dos limites de execução
- A carga é alta e constante: um servidor dedicado sai mais barato
No meu trabalho, o padrão que mais uso é um meio-termo: a aplicação principal roda num servidor comum (Django, filas, banco) e as peças orientadas a evento, como upload de arquivo, ficam em serviços gerenciados como o S3. Um caso parecido aparece no artigo sobre o Ducktix, onde o banco serverless exigiu um driver diferente para conseguir abrir transações.
Resumo
Serverless troca controle de infraestrutura por agilidade e escala automática. O provedor cuida do servidor; o time cuida do código. Resolve ociosidade, picos e provisionamento manual. Cold start, limites de execução e dependência do provedor continuam pesando. É uma escolha de cenário, não um upgrade universal.
Para aprofundar: a página de computação sem servidor da AWS e a documentação sobre o ciclo de vida do ambiente de execução da Lambda, que explica o cold start em detalhe.