首页 > 解决方案 > 正常应用程序关闭中的通道和信号使用

问题描述

下面的示例处理此处记录的正常服务器关闭。基于我在阅读其他帖子和示例后的研发工作,我想出了这篇实际上优雅地关闭服务器的工作。但是,由于我对 Golang 的经验/知识非常有限,我无法证明我是否正确使用了通道和信号。我只是想避免 Goroutine 泄漏和其他看不见的潜在问题。有人可以告诉我这篇作品是否是惯用的 Go 代码和通道和信号的使用吗?

内部/服务器/server.go

package server

import (
    "net/http"
)

type Server struct {
    *http.Server
}

func NewServer() Server {
    return Server{
        &http.Server{Addr: ":8080", Handler: nil},
    }
}

func (s Server) Start() error {
    if err := s.ListenAndServe(); err != http.ErrServerClosed {
        return err
    }

    return nil
}

cmd/api/main.go

package main

import (
    "api/internal/app"
    "api/internal/server"
    "context"
    "fmt"
    "os"
    "os/signal"
    "syscall"
)

func main() {
    // Some other bootstrapping and configuration code here ...
    // ....

    fmt.Printf("app starting")

    sigChan := make(chan os.Signal, 1)

    signal.Notify(sigChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)

    ctx, cancel := context.WithCancel(context.Background())

    go listenSignals(cancel, sigChan)

    if err := app.New(server.NewServer()).Start(ctx); err != nil {
        panic(err)
    }

    fmt.Print("app shutdown completed")
}

func listenSignals(cancel context.CancelFunc, sigChan chan os.Signal)  {
    sig := <-sigChan

    fmt.Printf("app shutdown started with %v signal", sig)

    cancel()
}

内部/app/api.go

package app

import (
    "api/internal/server"
    "context"
    "errors"
    "fmt"
    "time"
)

type App struct {
    srv server.Server
}

func New(srv server.Server) App {
    return App{srv: srv}
}

func (app App) Start(ctx context.Context) error {
    doneChan := make(chan struct{})

    go shutdown(ctx, doneChan, app)

    // Start HTTP server    
    if err := a.srv.Start(); err != nil {
        return errors.New("failed to start HTTP server")
    }

    // Start DB server
    // ...

    <-doneChan

    return nil
}

func shutdown(ctx context.Context, doneChan chan<- struct{}, app App) {
    <-ctx.Done()

    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()

    // Shutdown HTTP server  
    err := app.srv.Shutdown(ctx)
    if err != nil {
        fmt.Print("some active HTTP connections are interrupted at shutdown")
    } else {
        fmt.Print("no HTTP connections are interrupted at shutdown")
    }

    // Shutdown DB server
    // ...

    close(doneChan)
}

输出

INFO[0000] app starting
^C
INFO[0001] app shutdown started with interrupt signal
INFO[0001] no HTTP connections are interrupted at shutdown
INFO[0001] the application shutdown has been completed


INFO[0000] app starting
^C
INFO[0001] app shutdown started with interrupt signal
INFO[0001] some active HTTP connections are interrupted at shutdown
INFO[0001] the application shutdown has been completed

编辑

我做了一些 goroutine 分析,结果如下。我不确定结果是否正常!需要您的意见。

我不知道如何阅读这个所以两个问题:

  1. 一个请求 6 个 goroutine 正常吗?
  2. 多个并发请求的增加是否正常?
    • 认为这很正常,因为我正在使用httprouter它,它会针对每个请求启动。

标签: gosignalschannel

解决方案


推荐阅读