Interagir com o MongoDB em uma função Amazon Web Services Lambda usando o Go
Avalie esse Tutorial
Se você é um desenvolvedor de Go e quer usar a tecnologia sem servidor, o AWS Lambda é uma escolha sólida que o colocará em funcionamento em pouco tempo. Mas o que acontece quando você precisa se conectar ao seu banco de dados? Com as funções sem servidor, também conhecidas como funções como serviço (FaaS), você nunca pode ter certeza sobre o tempo de atividade de sua função ou como ela optou por escalar automaticamente de acordo com a demanda. Por esse motivo, as conexões simultâneas com seu banco de dados, que não são infinitas, acontecem de forma um pouco diferente. Em outras palavras, queremos ser eficientes na forma como as conexões e interações com o banco de dados são feitas.
Neste tutorial, veremos como criar uma função sem servidor usando a linguagem de programação Go e essa função se conectará e consultará o MongoDB Atlas de maneira eficiente.
Para limitar o escopo deste tutorial específico, há alguns pré-requisitos que devem ser atendidos antes de iniciar:
- Um clusterdo MongoDB Atlas com acesso à rede e funções de usuário já configuradas. Já tem uma conta AWS? O Atlas oferece suporte ao pagamento por uso por meio do AWS Marketplace (AWS MP) sem qualquer compromisso inicial - simplesmente Inscreva-se no MongoDB Atlas por meio do AWS Marketplace.
- Conhecimento da linguagem de programação Go.
Não abordaremos o processo de implantação de um MongoDB Atlas cluster neste tutorial, incluindo a configuração de listas de permissões de rede ou usuários. Desde que a AWS tenha acesso por meio de uma VPC ou permissão de IP global e um usuário que possa ler a partir dos bancos de dados de amostra, você ficará bem.
O objetivo deste tutorial não é explorar os detalhes do AWS Lambda, mas ver como incluir o MongoDB em nosso fluxo de trabalho. Por esse motivo, você deve ter algum conhecimento do AWS Lambda e como usá-lo antes de prosseguir.
Para começar, precisamos criar um novo projeto Go em nosso computador local. Execute os seguintes comandos a partir da sua linha de comando:
1 mkdir lambdaexample 2 cd lambdaexample 3 go mod init lambdaexample
Os comandos acima criarão um novo diretório de projeto e inicializarão o uso dos módulos Go para nossas dependências Amazon Web Services Lambda e MongoDB .
Em seguida, execute os seguintes comandos de dentro do seu projeto:
1 go get go.mongodb.org/mongo-driver/mongo 2 go get github.com/aws/aws-lambda-go/lambda
Os comandos acima farão o download do driver Go para MongoDB e do AWS Lambda SDK.
Por fim, crie um arquivoprincipal.go em seu projeto. O arquivo principal.go será onde adicionaremos todo o código do nosso projeto.
Dentro do arquivo main.go, adicione o seguinte código:
1 package main 2 3 import ( 4 "context" 5 "os" 6 7 "github.com/aws/aws-lambda-go/lambda" 8 "go.mongodb.org/mongo-driver/bson" 9 "go.mongodb.org/mongo-driver/bson/primitive" 10 "go.mongodb.org/mongo-driver/mongo" 11 "go.mongodb.org/mongo-driver/mongo/options" 12 ) 13 14 type EventInput struct { 15 Limit int64 `json:"limit"` 16 } 17 18 type Movie struct { 19 ID primitive.ObjectID `bson:"_id" json:"_id"` 20 Title string `bson:"title" json:"title"` 21 Year int32 `bson:"year" json:"year"` 22 } 23 24 var client, err = mongo.Connect(context.Background(), options.Client().ApplyURI(os.Getenv("ATLAS_URI"))) 25 26 func HandleRequest(ctx context.Context, input EventInput) ([]Movie, error) { 27 if err != nil { 28 return nil, err 29 } 30 31 collection := client.Database("sample_mflix").Collection("movies") 32 33 opts := options.Find() 34 35 if input.Limit != 0 { 36 opts = opts.SetLimit(input.Limit) 37 } 38 cursor, err := collection.Find(context.Background(), bson.M{}, opts) 39 if err != nil { 40 return nil, err 41 } 42 var movies []Movie 43 if err = cursor.All(context.Background(), &movies); err != nil { 44 return nil, err 45 } 46 47 return movies, nil 48 } 49 50 func main() { 51 lambda.Start(HandleRequest) 52 }
Não se preocupe, vamos detalhar o que o código acima faz e como ele se relaciona com sua função sem servidor.
Primeiro, você notará as duas estruturas de dados a seguir:
1 type EventInput struct { 2 Limit int64 `json:"limit"` 3 } 4 5 type Movie struct { 6 ID primitive.ObjectID `bson:"_id" json:"_id"` 7 Title string `bson:"title" json:"title"` 8 Year int32 `bson:"year" json:"year"` 9 }
Neste exemplo,
EventInput
representa qualquer entrada que possa ser enviada para nossa função Amazon Web Services Lambda . O campoLimit
representará quantos documentos o usuário deseja retornar com sua solicitação. A estrutura de dados pode incluir quaisquer outros campos que você ache que seriam úteis.A estrutura de dados
Movie
representa os dados que planejamos retornar ao usuário. Ele tem anotações BSON e JSON em cada um dos campos. A anotação BSON mapeia os campos do documento MongoDB para a variável local e a anotação JSON mapeia o campo local para dados que o AWS Lambda pode entender.Usaremos o banco de dadossample_mflix neste exemplo e esse banco de dados tem uma coleção defilmes. Nossa estrutura de dados
Movie
destina-se a mapear documentos nessa coleção. Você pode incluir quantos campos desejar, mas somente os campos incluídos serão retornados ao usuário.Em seguida, queremos lidar com uma conexão com o banco de dados:
1 var client, err = mongo.Connect(context.Background(), options.Client().ApplyURI(os.Getenv("ATLAS_URI")))
A linha acima cria um cliente de banco de dados para o nosso aplicativo. Ela usa uma variável de ambiente
ATLAS_URI
com as informações de conexão. Vamos definir isso mais tarde no AWS Lambda.Não queremos estabelecer uma conexão com o banco de dados toda vez que a função for executada. Queremos nos conectar apenas quando a função começar. Não temos controle sobre quando uma função começa, então a solução correta é conectar fora da função
HandleRequest
e fora da função main
.A maior parte de nossa mágica acontece na função
HandleRequest
:1 func HandleRequest(ctx context.Context, input EventInput) ([]Movie, error) { 2 if err != nil { 3 return nil, err 4 } 5 6 collection := client.Database("sample_mflix").Collection("movies") 7 8 opts := options.Find() 9 10 if input.Limit != 0 { 11 opts = opts.SetLimit(input.Limit) 12 } 13 cursor, err := collection.Find(context.Background(), bson.M{}, opts) 14 if err != nil { 15 return nil, err 16 } 17 var movies []Movie 18 if err = cursor.All(context.Background(), &movies); err != nil { 19 return nil, err 20 } 21 22 return movies, nil 23 }
Observe na declaração da função que estamos aceitando o
EventInput
e estamos devolvendo uma fatia de Movie
para o usuário.Quando inserimos a função pela primeira vez, verificamos se houve um erro. Lembre-se de que a conexão com o banco de dados pode ter falhado, então estamos pegando isso aqui.
Mais uma vez, para este exemplo, estamos usando o banco de dadossample_mflix e a coleção defilmes. Estamos armazenando uma referência a isso em nossa variável
collection
.Como optamos por aceitar a entrada do usuário e essa entrada está relacionada à forma como as consultas são feitas, estamos criando uma variável de opções. Uma das nossas muitas opções possíveis é o limite, portanto, se fornecermos um limite, provavelmente devemos defini-lo. Usando as opções, executamos uma operação
Find
na coleção. Para manter este exemplo simples, nosso critério de filtro é um mapa vazio que resultará no retorno de todos os documentos da coleção — é claro, o máximo é qualquer que seja o limite definido.Em vez de iterar por meio de um cursor dos resultados em nossa função, estamos optando pelo método
All
para carregar os resultados em nossa fatiamovies
.Supondo que não haja erros ao longo do caminho, retornamos o resultado e o AWS Lambda deve apresentá-lo como JSON.
Ainda não carregamos nossa função!
Como o Go é uma linguagem de programação compilada, você precisa criar um binário antes de carregá-lo no AWS Lambda. Existem certos requisitos que acompanham este trabalho.
Primeiro, precisamos nos preocupar com o sistema operacional de compilação e a arquitetura da CPU. O AWS Lambda espera Linux e AMD64, então, se você estiver usando outra coisa, você precisa usar o compilador cruzado Go.
Para melhores resultados, execute o seguinte comando:
1 env GOOS=linux GOARCH=amd64 go build
O comando acima criará o projeto para o sistema operacional e a arquitetura corretos, independentemente do computador que você estiver usando.
Não se esqueça de adicionar seu arquivo binário a um arquivo ZIP após a compilação. Em nosso exemplo, o arquivo binário deve ter o nomelambdaexample, a menos que você especifique o contrário.
No dashboard do AWS Lambda, carregue seu projeto e confirme se o manipulador e a arquitetura estão corretos.
Antes de testar a função, não se lembre de atualizar suas variáveis de ambiente no Amazon Web Services Lambda.
Você pode obter sua string URI no painel do MongoDB Atlas.
Depois de feito, você pode testar tudo usando a aba "Test" do dashboard do AWS Lambda. Forneça um "limite" opcional para o "JSON do evento" e verifique os resultados dos seus filmes!
Você acabou de ver como usar o MongoDB com AWS Lambda e o tempo de execução Go! A AWS facilita muito o uso do Go para funções sem servidor, e o driver Go para o MongoDB torna ainda mais fácil o uso com o MongoDB.
Como um exercício de leitura adicional, vale a pena conferir o Início rápido do MongoDB Go, bem como alguma documentação sobre pool de conexões em funções sem servidor.