Go 語言實現優雅關機和重啓的示例很詳細

在生產環境中,服務的更新和維護是不可避免的。粗暴地終止服務會導致:

本文將深入探討如何使用 Go 語言實現:

優雅關機實現

核心機制

完整實現代碼

package main



import(

"context"

"log"

"net/http"

"os"

"os/signal"

"syscall"

"time"

)



funcmain(){

// 創建路由

	mux := http.NewServeMux()

	mux.HandleFunc("/cook-hotpot",func(w http.ResponseWriter, r *http.Request){

// 模擬長時間處理任務(如煮火鍋)

		time.Sleep(5* time.Second)

		w.Write([]byte("您的火鍋已準備好,請慢用!"))

})



// 創建HTTP服務器

	srv :=&http.Server{

		Addr:":8080",

		Handler: mux,

}



// 啓動服務器

gofunc(){

		log.Println("火鍋店開始營業,監聽端口 8080...")

if err := srv.ListenAndServe(); err !=nil&& err != http.ErrServerClosed {

			log.Fatalf("服務器錯誤: %v\n", err)

}

}()



// 等待中斷信號

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

// 捕獲SIGINT(CTRL+C)和SIGTERM信號

	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

<-quit

	log.Println("收到關閉信號,火鍋店準備停止營業...")



// 設置5秒超時上下文

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

defercancel()



// 優雅關閉服務器

if err := srv.Shutdown(ctx); err !=nil{

		log.Fatalf("服務器關閉失敗: %v\n", err)

}

	log.Println("火鍋店已安全關閉,所有顧客都已用餐完畢")

}

關鍵點解析

  1. 信號處理

    :監聽SIGINT(Ctrl+C) 和SIGTERM(kill 默認信號)

  2. 超時控制

    :通過context.WithTimeout設置最大等待時間

  3. 資源釋放

    Shutdown()會關閉所有空閒連接和監聽 socket

測試方法

  1. 啓動服務:go run main.go

  2. 發起請求:curl http://localhost:8080/cook-hotpot

  3. 立即發送停止信號:Ctrl+C

  4. 觀察日誌輸出,確認請求完成後再關閉

優雅重啓實現

核心機制

使用 endless 庫實現

package main



import(

"fmt"

"log"

"net/http"

"time"



"github.com/fvbock/endless"

)



funcmain(){

	mux := http.NewServeMux()

	mux.HandleFunc("/greet",func(w http.ResponseWriter, r *http.Request){

// 模擬處理時間

		time.Sleep(3* time.Second)

		w.Write([]byte("歡迎光臨,小區新保安在此!"))

})



// 使用endless創建服務器

	srv := endless.NewServer(":8080", mux)



// 設置連接關閉超時時間

	srv.BeforeBegin =func(add string){

		log.Printf("當前進程PID: %d", os.Getpid())

}



// 啓動服務

	log.Println("小區保安開始值班...")

if err := srv.ListenAndServe(); err !=nil{

if err != endless.ErrServerClosed {

			log.Printf("服務器錯誤: %v\n", err)

}

}

	log.Println("保安交接班完成,老保安下班")

}

關鍵點解析

  1. 平滑過渡

    :新進程啓動後接管監聽端口

  2. 請求處理

    :舊進程繼續處理已接收的請求

  3. 進程管理

    :自動處理父子進程關係

測試方法

  1. 首次啓動:go run main.go,記錄 PID

  2. 發起請求:curl http://localhost:8080/greet

  3. 修改代碼並重新編譯

  4. 發送重啓信號:kill -1 <PID>

  5. 觀察新請求返回新內容,舊請求不受影響

進階實現方案

自定義優雅重啓實現

package main



import(

"context"

"log"

"net"

"net/http"

"os"

"os/exec"

"os/signal"

"syscall"

"time"

)



var(

	listener net.Listener

	server   *http.Server

)



funcmain(){

// 初始化服務器

setupServer()



// 監聽重啓信號

gowatchSignals()



// 啓動服務

if err := server.Serve(listener); err !=nil&& err != http.ErrServerClosed {

		log.Fatalf("服務器錯誤: %v\n", err)

}

}



funcsetupServer(){

	mux := http.NewServeMux()

	mux.HandleFunc("/greet",func(w http.ResponseWriter, r *http.Request){

		time.Sleep(3* time.Second)

		w.Write([]byte("自定義優雅重啓實現!"))

})



	server =&http.Server{Handler: mux}



// 複用監聽socket

var err error

	listener, err = net.Listen("tcp",":8080")

if err !=nil{

		log.Fatal(err)

}

}



funcwatchSignals(){

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

	signal.Notify(sig, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)



for s :=range sig {

switch s {

case syscall.SIGHUP:// 優雅重啓

			log.Println("收到重啓信號,開始優雅重啓...")

restartServer()

case syscall.SIGINT, syscall.SIGTERM:// 優雅關閉

			log.Println("收到關閉信號,開始優雅關閉...")

shutdownServer()

return

}

}

}



funcrestartServer(){

// 啓動新進程

	execFile := os.Args[0]

	cmd := exec.Command(execFile)

	cmd.Stdout = os.Stdout

	cmd.Stderr = os.Stderr

	cmd.Env = os.Environ()



if err := cmd.Start(); err !=nil{

		log.Printf("啓動新進程失敗: %v\n", err)

return

}



// 關閉舊進程

gofunc(){

		time.Sleep(5* time.Second)// 給新進程啓動時間

shutdownServer()

}()

}



funcshutdownServer(){

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

defercancel()



if err := server.Shutdown(ctx); err !=nil{

		log.Printf("服務器關閉錯誤: %v\n", err)

}

	log.Println("服務器已安全關閉")

}

生產環境最佳實踐

1. 健康檢查接口

mux.HandleFunc("/health",func(w http.ResponseWriter, r *http.Request){

    w.WriteHeader(http.StatusOK)

    w.Write([]byte("OK"))

})

2. 優雅關閉超時控制

// 分階段關閉

gofunc(){

<-quit

    log.Println("開始優雅關閉...")



// 第一階段:停止接收新請求

if err := server.Shutdown(context.Background()); err !=nil{

        log.Printf("第一階段關閉失敗: %v", err)

}



// 第二階段:強制關閉剩餘連接

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

defercancel()

if err := server.Shutdown(ctx); err !=nil{

        log.Printf("第二階段關閉失敗: %v", err)

}

}()

3. 多服務協調關閉

// 使用sync.WaitGroup等待多個服務關閉

var wg sync.WaitGroup



// HTTP服務器

wg.Add(1)

gofunc(){

defer wg.Done()

if err := httpServer.Shutdown(ctx); err !=nil{

        log.Printf("HTTP服務器關閉錯誤: %v", err)

}

}()



// gRPC服務器

wg.Add(1)

gofunc(){

defer wg.Done()

    grpcServer.GracefulStop()

}()



wg.Wait()

4. 容器化部署注意事項

# Dockerfile示例

FROM golang:1.18 as builder

WORKDIR /app

COPY . .

RUN CGO_ENABLED=0 GOOS=linux go build -o server .



FROM alpine:latest

WORKDIR /root/

COPY--from=builder /app/server .

CMD ["./server"]

使用docker stop時會發送SIGTERM信號,確保正確處理:

signal.Notify(quit, syscall.SIGTERM, syscall.SIGINT)

常見問題解決方案

1. 殭屍進程處理

// 在父進程退出前等待子進程

cmd := exec.Command(...)

cmd.SysProcAttr =&syscall.SysProcAttr{

    Setpgid:true,

    Pgid:0,

}



// 父進程退出時殺死整個進程組

syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)

2. 端口複用

// 設置SO_REUSEPORT

lc := net.ListenConfig{

    Control:func(network, address string, c syscall.RawConn)error{

var opErr error

        err := c.Control(func(fd uintptr){

            opErr = syscall.SetsockoptInt(

int(fd),

                syscall.SOL_SOCKET,

                syscall.SO_REUSEPORT,

1,

)

})

return opErr

},

}

listener, err := lc.Listen(context.Background(),"tcp",":8080")

3. 長連接處理

// 服務器配置

server :=&http.Server{

    ReadTimeout:5* time.Second,

    WriteTimeout:10* time.Second,

    IdleTimeout:15* time.Second,// 自動關閉空閒連接

}

性能對比與選擇建議

總結與擴展閱讀

本文詳細介紹了 Go 語言實現優雅關機和重啓的多種方法,關鍵要點包括:

  1. 優雅關機

    通過http.Server.Shutdown()實現,確保完成現有請求

  2. 優雅重啓

    可通過 endless 庫或自定義實現,實現零停機部署

  3. 生產環境

    需要考慮健康檢查、超時控制和多服務協調

擴展學習方向:

通過合理使用這些技術,可以構建出高可用、易維護的 Go 語言微服務架構。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/4dxSLhyDFH_irNLBuN6FsQ