Explore o novo chatbot do Developer Center! O MongoDB AI chatbot pode ser acessado na parte superior da sua navegação para responder a todas as suas perguntas sobre o MongoDB .

Saiba por que o MongoDB foi selecionado como um líder no 2024 Gartner_Magic Quadrupnt()
Desenvolvedor do MongoDB
Central de desenvolvedor do MongoDBchevron-right
Idiomaschevron-right
Gochevron-right

Simultaneidade e fechamento gracioso do cliente MDB

Jorge D. Ortiz-Fuentes5 min read • Published Sep 04, 2024 • Updated Sep 04, 2024
Go
APLICATIVO COMPLETO
Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
No artigo anterior e no vídeo correspondente, aprenderam a persistir os dados que foram trocados com nosso servidor HTTP usando o MongoDB. Usamos o driver do MongoDB para Go para acessar um MongoDB Atlas clustergratuito e usar instâncias de nossos dados diretamente com ele.
Neste artigo, vamos nos concentrar em um tópico mais avançado que muitas vezes é ignorado: como desligar corretamente nosso servidor. Isso pode ser usado com o WaitGroups fornecido pelo pacotesync, mas decidi fazê-lo usando goroutines e canais para abordá-los em um caso de uso mais realista, mas compreensível.
Na versão mais recente do código desse programa, definimos uma maneira de fechar adequadamente a conexão com o banco de dados. No entanto, não tinhamos como parar normalmente o servidor da web. Usar Control+C fechou o servidor imediatamente e esse código nunca foi executado.

Usar multiplexador personalizado

  1. Antes de podermos personalizar a forma como nosso servidor HTTP é encerrado, precisamos organizar a forma como ele é construído. Primeiro, as rotas que criamos são adicionadas ao DefaultServeMux. Em vez disso, podemos criar nosso próprio roteador e adicionar rotas a ele (em vez das antigas).
    1router := http.newservemux()
    2router.handlefunc("get /", func(w http.responsewriter, r *http.request) {
    3 w.write([]byte("HTTP caracola"))
    4})
    5router.handlefunc("post /notes", createNote)
  2. O roteador que acabamos de criar, juntamente com outros parâmetros de configuração, pode ser usado para criar um http.Server. Outros parâmetros também podem ser definidos: Leia a documentação deste.
    1server := http.Server{
    2 Addr: serverAddr,
    3 Handler: router,
    4}
  3. Use esse servidor para escutar as conexões, em vez do servidor padrão. Aqui, não precisamos de parâmetros na função porque eles são fornecidos com a instânciaservere estamos invocando um de seus métodos.
    1log.Fatal(server.ListenAndServe())
  4. Se você compilar e executar esta versão, ela deverá se comportar exatamente da mesma forma que antes.
  5. A funçãoListenAndServe()retorna um erro específico quando o servidor é fechado com Shutdown(). Vamos lidar com isso separadamente.
    1if err := server.ListenAndServe(); !errors.Is(err, http.ErrServerClosed) {
    2 log.Fatalf("HTTP server error %v\n", err)
    3}

Usar a função de desligamento na interrupção do sinal

Sinal de parada
  1. O tipoServer tem outros métodos que podemos usar. Entre outros, podemos definir a função que será executada após Shutdown() ser invocado. Isso deve ser feito antes que ListenAndServe() seja invocado.
    1server.RegisterOnShutdown(func() {
    2 fmt.Println("Signal shutdown")
    3})
  2. Em seguida, definimos uma função anônima que aguarda o sinal de interrupção e inicia o desligamento adequado do servidor. Começamos com uma função vazia.
    1func() {
    2}
  3. O Go lida com sinais POSIX usando signal.Notify(). Essa função usa um canal que será usado para notificar e o sinal que você deseja que seja tratado. Um canal é como um pipe no Go com um tipo associado que é definido quando o canal é criado. Os dados são enviados para um canal usando esta notação: channel <- data. E é lido usando esta outra notação: data <- channel. Se você ler de um canal que não tem dados, o "thread de execução" atual será interrompido e aguardará que os dados estejam disponíveis. Se você escrever dados em um canal, o "thread de execução" atual será interrompido e aguardará que os dados sejam lidos. Devido a esse comportamento específico, eles são comumente usados como mecanismo de sincronização. Os canais também podem ter um buffer de tamanho fixo. A gravação em um canal não bloqueia até que o buffer esteja cheio. Vamos criar o canal que comunica sinais (os.Signal) com um buffer de um elemento e usá-lo com a função para lidar com o sinal.
    1sigint := make(chan os.Signal, 1)
    2signal.Notify(sigint, os.Interrupt)
  4. A leitura deste canal aguardará até que um sinal de interrupção (Control+C) seja recebido.
    1<-sigint
  5. E quando isso acontecer, poderemos iniciar o processo de desligamento. Se recebermos um erro, vamos registrá-lo e panic. Poderíamos (deveríamos?) ter um tempo limite nesse contexto.
    1if err := server.Shutdown(context.Background()); err != nil {
    2 log.Fatalf("Server shutdown error: %v", err)
    3}
  6. Agora que definimos a função anônima, colocando parênteses no final, invocamos a função. Entretanto, se fizermos isso, essa função será executada no thread de execução "atual" e nosso programa aguardará o sinal e encerrará o servidor sem sequer iniciá-lo. Precisamos criar outro thread de execução "" . Felizmente, isso é trivial em Go: você pode criar outra thread de execução "" usando a palavra-chavegoantes de executar uma função. Isso é chamado de goroutine.
    1go func() {
    2 // ...
    3}()

Aguarde o término do desligamento

  1. Se executarmos esta versão do programa, ela deverá funcionar bem. No entanto, há uma ressalva. Quando server.Shutdown() for invocado, o servidor parará de ouvir e sairá. Ele também executará a função que registramos com RegisterOnShutdown() em outra goroutine. E, dependendo da ordem de execução e do comprimento da função registrada, ela pode sair main antes que a função registrada termine seu trabalho. Quando o programa sai da função principal, quaisquer outras goroutines são canceladas. Podemos usar outro canal para evitar que isso aconteça. Criamos este novo canal sem dados (estrutura vazia), pois ele serve apenas para sincronização.
    1done := make(chan struct{})
  2. Vamos ler deste canal logo antes de sair da funçãomain. Se ainda não tivermos escrito para ele, bloquearemos lá.
    1<-done
  3. Quando começamos a executar a função que será executada no desligamento, adiamos a gravação nesse canal, garantindo que será a última coisa a ser feita quando a função for concluída, desbloqueando o fim da execução do programa.
    1defer func(){
    2 done<-struct{}{}
    3}()
  4. Vamos adicionar algum atraso à função para verificar se isso está funcionando.
    1time.Sleep(5 * time.Second)
  5. Isso deve resolver a situação. Compile e teste.

Evite bloquear quando não estiver saindo mais cedo

  1. No entanto, se o servidor falhar devido a algum erro, ele permanecerá lá, esperando que o canal doneseja gravado. Uma maneira de resolver isso é fechar o canal, pois a leitura de um canal fechado não bloqueia. A outra é usar a função de registro adequada para trigger um pânico quando o erro for detectado.log.Fatal() imprime e usa os.Exit(), enquanto log.Panic() imprime uma mensagem e aciona um pânico, que faz com que funções adiadas sejam executadas.

Conclusão

Com este artigo final, abordamos:
  • As possibilidades de configuração oferecidas pelo servidor HTTP da biblioteca padrão.
  • A criação de goroutines que permitem a execução simultânea de código.
  • Uso de canais para sincronização de goroutines.
O repositório tem todo o código desta série para que você possa acompanhar. Os tópicos abordados são os fundamentos que você precisa conhecer para produzir REST API completas, servidores back-end ou até mesmo microsserviços escritos em Go. A caminho está à sua frente e estamos ansiosos para aprender o que você criará com esse conhecimento.
Mantenha-se atento. Hackeie seu código. Até a próxima!
Principais comentários nos fóruns
Ainda não há comentários sobre este artigo.
Iniciar a conversa

Ícone do FacebookÍcone do Twitterícone do linkedin
Avalie esse Tutorial
star-empty
star-empty
star-empty
star-empty
star-empty
Relacionado
Tutorial

Usando Go para AI


Nov 07, 2024 | 19 min read
Tutorial

Servidores HTTP persistindo dados no MongoDB


Sep 04, 2024 | 5 min read
Início rápido

Reagindo às mudanças do banco de dados com o MongoDB Change Streams e Go


Feb 03, 2023 | 5 min read
Tutorial

Interagir com o MongoDB em uma função Amazon Web Services Lambda usando o Go


Aug 29, 2024 | 6 min read
Sumário