Go 实现程序优雅退出

发布于:2024-05-21 ⋅ 阅读:(126) ⋅ 点赞:(0)

在Go语言中,实现程序的优雅退出是一项重要的任务,特别是在涉及到HTTP服务器、gRPC服务器、以及其他后台工作的情况下。 

HTTP Server 平滑关闭

Go 1.8及以上版本提供了 http.Server 结构的 Shutdown 方法,用于平滑关闭HTTP服务器。

案例一: 

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"time"
)

func main() {
	// 创建一个新的 ServeMux 对象,它是HTTP请求多路复用器,用于将不同的请求路由到不同的处理函数
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Hello, World!"))
	})

	server := &http.Server{
		Addr:    ":8088", //监听端口
		Handler: mux,     //监听的处理器
	}

	//监听并服务HTTP请求,可以想办法开一个8088端口来占用,比如在java起一个服务
	go func() {
		if err := server.ListenAndServe(); err != nil {
			if err != http.ErrServerClosed {
				// 处理监听失败的错误
				// 记录错误
				log.Printf("HTTP服务器失败: %v", err)

				// 执行清理工作,如有必要

				// 可选:尝试重启服务器
				time.Sleep(10 * time.Second) //等待10秒再重启
				if !attemptRestart(server) {
					//os.Exit(1) 将导致程序立即退出,并返回状态码 1 表示发生了错误。在实际应用中,你可能需要根据错误的性质和程序的设计来决定是否退出程序,或者采取其他的错误恢复策略
					// 优雅地退出程序
					os.Exit(1)
				}
			}
		}
	}()

	// 等待中断信号来优雅地关闭服务器
	stop := make(chan os.Signal, 1)
	// 用 signal.Notify 来监听 os.Interrupt 信号,这是用户向程序发送中断信号(如Ctrl+C)时产生的信号
	signal.Notify(stop, os.Interrupt)

	<-stop // 程序在此处阻塞,直到接收到一个中断信号

	//当有中断信号来,创建一个带有超时的 context.Context 对象,超时时间为5秒
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	// 确保在函数返回时取消这个上下文,释放相关资源
	defer cancel()

	//当接收到中断信号时,调用 server.Shutdown 方法并传入上面创建的 ctx 对象,以优雅地关闭服务器
	if err := server.Shutdown(ctx); err != nil {
		// 如果在关闭过程中出现错误
		fmt.Println("处理关闭服务器时的错误")
	}
}

// attemptRestart 尝试重启服务器
func attemptRestart(server *http.Server) bool {
	// 这里可以添加任何需要的清理或重启前的准备工作
	log.Println("正在尝试重新启动服务器。。。")

	// 尝试重新启动服务器
	err := server.ListenAndServe()
	if err != nil && err != http.ErrServerClosed {
		log.Printf("无法重新启动服务器: %v", err)
		return false
	}
	log.Println("重启成功。。。")
	return true
}

案例二:持续监听 

package main

import (
	"context"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/signal"
	"time"
)

func main() {
	// 创建一个新的 ServeMux 对象,它是HTTP请求多路复用器,用于将不同的请求路由到不同的处理函数
	mux := http.NewServeMux()
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("Hello, World!"))
	})

	server := &http.Server{
		Addr:    ":8088", //监听端口
		Handler: mux,     //监听的处理器
	}

	go func() {
		// 等待中断信号来优雅地关闭服务器
		stop := make(chan os.Signal, 1)
		// 用 signal.Notify 来监听 os.Interrupt 信号,这是用户向程序发送中断信号(如Ctrl+C)时产生的信号
		signal.Notify(stop, os.Interrupt)

		<-stop // 程序在此处阻塞,直到接收到一个中断信号

		//当有中断信号来,创建一个带有超时的 context.Context 对象,超时时间为5秒
		ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
		// 确保在函数返回时取消这个上下文,释放相关资源
		defer cancel()

		//当接收到中断信号时,调用 server.Shutdown 方法并传入上面创建的 ctx 对象,以优雅地关闭服务器
		if err := server.Shutdown(ctx); err != nil {
			// 如果在关闭过程中出现错误
			fmt.Println("处理关闭服务器时的错误")
		}
	}()

	//监听并服务HTTP请求,可以想办法开一个8088端口来占用,比如在java起一个服务
	for {
		if err := server.ListenAndServe(); err != nil {
			if err != http.ErrServerClosed {
				// 处理监听失败的错误
				// 记录错误
				log.Printf("HTTP服务器失败: %v", err)

				// 执行清理工作,如有必要

				// 可选:尝试重启服务器
				time.Sleep(5 * time.Second) //等待10秒再重启
				attemptRestart(server)

				//os.Exit(1) 将导致程序立即退出,并返回状态码 1 表示发生了错误。在实际应用中,你可能需要根据错误的性质和程序的设计来决定是否退出程序,或者采取其他的错误恢复策略
				// 优雅地退出程序
				//os.Exit(1)
			}
		}
	}
}

// attemptRestart 尝试重启服务器
func attemptRestart(server *http.Server) bool {
	// 这里可以添加任何需要的清理或重启前的准备工作
	log.Println("正在尝试重新启动服务器。。。")

	// 尝试重新启动服务器
	err := server.ListenAndServe()
	if err != nil && err != http.ErrServerClosed {
		log.Printf("无法重新启动服务器: %v", err)
		return false
	}
	return true
}

 


网站公告

今日签到

点亮在社区的每一天
去签到