Como criar Lambda com Java
Spring Cloud Function x SDK AWS

Você sabe o que é Lambda? Se você quiser entender um pouco mais sobre esse serviço da AWS e por a mão na massa, esse post é pra você. AWS Lambda é um serviço (serveless) que executa o código de tempos em tempos dado um estímulo/evento de “entrada”. O serviço tem suporte às principais linguagens e plataforma do mercado, tais como: Java, Pyhton, Node.JS, .NET Core, e Go.
Além disso, os serviços Lambda só são cobrados pelo consumo da execução deles, ou seja, você só paga por aquilo que usar. Mas cuidado! Quanto maior o tempo de duração e maior o consumo de memória, maior será o custo, então é necessário fazer um estudo de viabilidade, mesmo que simples.
O que é serveless?
Serverless é um modelo de serviço de nuvem que, com ele, você não precisa se preocupar com a infraestrutura da sua aplicação. O servidor ainda existe, entretanto ele é totalmente gerenciado pelo provedor de nuvem, o que te permite focar somente na lógica do seu negócio. (Treinaweb, 2020).
Empresas que oferencem serviços serveless, tais como Google, Amazon, Azure, IBM, entre outras, oferecem um ambiente em que o cliente se preocupa minimamente com infraestrutura. O máximo que ele vai ter que informar é quantidade de memória que precisa e/ou alguma configuração relativa ao que vai consumir ou que espera que esteja disponível para sua função durante a execução do seu código.
Isso permite que o cliente consiga escalar seu sistema com maior facilidade e ter um serviço com alta disponibilidade e maior segurança, entre outros benefícios.
Mão na massa!
Depois dessa introdução, bora pra prática. Vamos implementar uma função que usa Java e Spring Cloud Function e executá-la no ambiente AWS, comparar o tempo de execução com funções Node.js e Python e, por fim, vamos avaliar os prós e contras de usar o Spring Cloud Function.
Neste exercício, vamos implementar dois Lambdas que buscam informações referentes ao CEP na API ViaCep.
Primeiro vamos criar o projeto, implementar (utilizando Spring Cloud Function) e testar (localmente e no ambiente AWS).
Implementando um AWS Lambda com Java e Spring Cloud Function:
Passo 1:
Gerar o projeto, utilizando o Spring Initializr

Passo 2:
Configurar o projeto
Antes de tudo é necessário termos os adapters Spring Cloud Function Web or Stream em tempo de execução do Lambda. Para isso, precisamos excluí-los antes de criar o JAR. Uma aplicação Lambda também precisa ser sombreada, por isso precisamos fazer essa configuração para que sejam gerados dois arquivos jar: um com terminação “aws” e outro sem. O arquivo com a aws será utilizado para fazer o deploy na AWS e outro para ser executado localmente.
Segue o exemplo:
Passo 3:
Implementar a função:
Vejamos a estrutura do projeto.

Como ficou a classe principal?
O Spring Cloud Function nos oferece um jeito simples de criar nossas funções por meio da annotation @Bean, porém seu desempenho não é dos melhores e, seguindo a recomendação da própria documentação, o jeito mais eficiente é implementando a classe ApplicationContextInitializer e o método initialize. Nesse método, vamos instanciar a nossa classe que vai criar nossa Function.
Nosso model de exemplo:
Precisamos ter o construtor vazio para que, no momento em que nossa função for receber o evento, ele não tenha um comportamento inesperado. Se não tivermos o construtor vazio, ele acaba gerando uma Exception no momento em que a Classe Handler recebe a requisição.
Agora podemos implementar também nossa function. Vejamos:
Aqui nós implementamos a interface Function, na qual precisamos dizer quais são os tipos dos objetos que ela vai receber e devolver (request e response da function). O primeiro tipo de objeto é o que ela recebe, e o segundo é o tipo de objeto que vai devolver, além é claro do método apply que precisamos implementar.
Agora precisamos criar um Handler para que possamos referenciá-lo no momento do deploy.
Apesar de ser uma classe vazia, quando extendemos o SpringBootRequestHandler ele fica responsável e implementa o RequestHandler, além de enviar a solicitação para nossa função. A única razão pela qual precisamos implementá-lo é especificar o tipo de entrada e os parâmetros de saída da função, para que a AWS possa serializá-los ou desserializá-los para nós. Assim nós finalizamos a implementação com Spring Cloud Function.
Implementando um AWS Lambda com Java e SDK AWS.
Para nosso segundo meio de implementar uma função, eu acabei usando um novo projeto com as mesmas configurações, model e classe principal (só retirei o método com o @Bean), e também não precisaremos da classe no package functions, pois tudo vai acontecer dentro do Handler.

Vejamos o nosso como ficou a nossa classe:
Agora nós mesmos implementamos o RequestHandler e para isso precisamos implementar o método handlerRequest, que é o responsável por processar o objeto de entrada e enviar o objeto de saída.
Obs.: Os testes locais dessa aplicação só foram possíveis com uso das ferramentas AWS SAM CLI, Plugin AWS Toolkit e Docker. Eu descrevo como testar nesse outro post.
Agora vamos aos testes:
Primeiro vamos testar localmente, começando pela função exposta na classe principal:

Obs: estou rodando a aplicação pela própria IDE e executando os testes com o Postman.
Teste no console AWS:
Primeiro precisamos buildar o projeto, para gerar o jar que vamos inserir no console da AWS.

Agora, com o Lambda já criado (aqui nesse tópico não vamos abordar a criação do Lambda do início), podemos subir nosso código e configurá-lo:

No campo do “Manipulador” devemos colocar o path da nossa classe Handler e o nome dela. No nosso exemplo ficou assim:
com.example.viacep.handler.aws.ViaCepHandler
Clicamos no botão com círculo vermelho e selecionamos o jar com terminação “aws.jar”.

Agora, precisamos adicionar algumas variáveis de ambiente para o funcinamento da nossa função. Primeiro vamos testar a função ViaCep.

Obs: a variável MAIN_CLASS só é necessária caso a classe principal não seja encontrada no jar. A documentação não deixou claro o porquê de isso acontecer.
Agora, na parte superior da página, podemos configurar um teste “manual”:

Basta clicar em “configurar eventos de teste” e vai abrir um modal no qual vamos inserir o JSON com formato do objeto que esperamos receber na função:

Agora é só criar/salvar e testar.

Para testarmos o Lambda implementado diretamente no Handler, não precisamos das variavéis de ambiente, basta indicar o path completo do arquivo manipulador (com.function.sample.handler.GetViaCepHandler)

Impressões:
Resumidamente, é uma ótima ferramenta e possibilidade para implementação. Ajuda bastante em casos mais complexos em que necessitamos criar uma arquitetura para o serviço, além de utilizar uma linguagem já conhecida e um framework de mercado, o que facilita para usuários da linguagem na implementação de testes unitários e, claro, testes locais.
Por outro lado, o desempenho não foi dos melhores. Mas temos que entender o contexto de cada caso e aplicação dessa stack. Se for só um disparo simples, que ocorre várias vezes ao dia, talvez não seja o mais adequado. Mas se for algo que precise de um pouco mais de robustez e que só rode uma vez ao dia ou semana ou mês, pode ser que funcione melhor.
Outro ponto negativo é que há uma grande complexidade na configuração, e talvez pessoas com menos experiência demorem a entender como funciona ou talvez nem entendam. Se o foco for só usar a configuração já fornecida, acredito que não tenha problema, a implementação é relativamente simples e intuitiva, há uma flexibilidade para usar Function, Consumer e Supplier.
Outros lambdas que fazem a mesma coisa tiveram um desempenho melhor, como podemos ver a seguir:
Lambda node.js (javascript)

Lambda python (3.8)

Se você quiser mais detalhes, pode dar uma olhada nos repositórios:
Ficou alguma dúvida ou tem algum feedback do que poderia ser melhorado nos projetos? É só deixar um comentário. Quer aprender continuamente em um time que adora compartilhar? Saiba mais aqui e se candidate! Vamos aprender juntos. Até a próxima!