文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

Golang实现程序优雅退出的方法详解

2024-04-02 19:55

关注

1. 背景

项目开发过程中,随着需求的迭代,代码的发布会频繁进行,在发布过程中,如何让程序做到优雅的退出?

为什么需要优雅的退出?

如下我们以 http 服务,gRPC 服务,单独的 woker 协程为例子,一步步说明平滑关闭的写法。

2. 常见的几种平滑关闭

为了解决退出可能出现的潜在问题,平滑关闭一般做如下一些事情

2.1 http server 平滑关闭

原来的写法

// startHttpServer start http server
func startHttpServer() {
    mux := http.NewServeMux()
    // mux.Handle("/metrics", promhttp.Handler())
    if err := http.ListenAndServe(":1608", mux); err != nil {
        log.Fatal("startHttpServer ListenAndServe error: " + err.Error())
    }
} 

带平滑关闭的写法

// startHttpServer start http server
func startHttpServer() {
    mux := http.NewServeMux()
    // mux.Handle("/metrics", promhttp.Handler())
    srv := &http.Server{
        Addr:    ":1608",
        Handler: mux,
    }
    // 注册平滑关闭,退出时会调用 srv.Shutdown(ctx)
    quit.GetQuitEvent().RegisterQuitCloser(srv)
    if err := srv.ListenAndServe(); err != nil {
        log.Fatal("startHttpServer ListenAndServe error: " + err.Error())
    }
}

把平滑关闭注册到http.Server的关闭函数中

// startHttpServer start http server
func startHttpServer() {
    mux := http.NewServeMux()
    // mux.Handle("/metrics", promhttp.Handler())
    srv := &http.Server{
        Addr:    ":1608",
        Handler: mux,
    }
    // 把平滑退出注册到http.Server中
    srv.RegisterOnShutdown(quit.GetQuitEvent().GracefulStop)
    if err := srv.ListenAndServe(); err != nil {
        log.Fatal("startHttpServer ListenAndServe error: " + err.Error())
    }
}

2.2 gRPC server 平滑关闭

原来的写法

// startGrpcServer start grpc server
func startGrpcServer() {
    listen, err := net.Listen("tcp", "0.0.0.0:9999")
    if err != nil {
        log.Fatalf("Failed to listen: %v", err)
        return
    }
    grpcServer := grpc.NewServer()
    // helloBoot.GrpcRegister(grpcServer)
    go grpcServer.Serve(listen)
    defer grpcServer.GracefulStop()
    // ...
}

带平滑关闭的写法 

// startGrpcServer start grpc server
func startGrpcServer() {
    listen, err := net.Listen("tcp", "0.0.0.0:9999")
    if err != nil {
        log.Fatalf("Failed to listen: %v", err)
        return
    }
    grpcServer := grpc.NewServer()
    // helloBoot.GrpcRegister(grpcServer)
    go grpcServer.Serve(listen)
    // 把 grpc 的GracefulStop注册到退出事件中
    quit.GetQuitEvent().RegisterStopFunc(grpcServer.GracefulStop)
    quit.WaitSignal()
}  

2.3 worker 协程平滑关闭

单独的协程启停,可以通过计数的方式注册到退出事件处理器中。

1.启动协程 增加计数

quit.GetQuitEvent().AddGoroutine()

2.停止协程 减计数 

quit.GetQuitEvent().DoneGoroutine()

3.常驻后台运行的协程退出的条件改成退出事件是否结束的条件 

!quit.GetQuitEvent().HasFired()

4.常驻后台运行的协程若通过 select 处理 chan,同时增加退出事件的chan

case <-quit.GetQuitEvent().Done()

// myWorker my worker
type myWorker struct {
}
 
// RunWorkerWithChan run Goroutine worker
func (m *myWorker) RunWorkerWithChan() {
    // 启动一个Goroutine时,增加Goroutine数
    quit.GetQuitEvent().AddGoroutine()
    defer func() {
        // 一个Goroutine退出时,减少Goroutine数
        quit.GetQuitEvent().DoneGoroutine()
    }()
    // 退出时,此次退出
    for !quit.GetQuitEvent().HasFired() {
        select {
        // 退出时,收到退出信号
        case <-quit.GetQuitEvent().Done():
            break
            //case msg := <- m.YouChan:
            // handle msg
        }
    }
}
 
// RunWorker run Goroutine worker
func (m *myWorker) RunWorker() {
    // 启动一个Goroutine时,增加Goroutine数
    quit.GetQuitEvent().AddGoroutine()
    defer func() {
        // 一个Goroutine退出时,减少Goroutine数
        quit.GetQuitEvent().DoneGoroutine()
    }()
 
    // 退出时,此次退出
    for !quit.GetQuitEvent().HasFired() {
        // ...
    }
}

2.4 实现 io.Closer 接口的自定义服务平滑关闭

实现 io.Closer 接口的结构体,增加到退出事件处理器中 

// startMyService start my service
func startMyService() {
    srv := NewMyService()
    // 注册平滑关闭,退出时会调用 srv.Close()
    quit.GetQuitEvent().RegisterCloser(srv)
    srv.Run()
}
 
// myService my service
type myService struct {
    isStop bool
}
 
// NewMyService new
func NewMyService() *myService {
    return &myService{}
}
 
// Close my service
func (m *myService) Close() error {
    m.isStop = true
    return nil
}
 
// Run my service
func (m *myService) Run() {
    for !m.isStop {
        // ....
    }
}

2.5 集成其他框架怎么做

退出信号处理由某一框架接管,寻找框架如何注册退出函数,优秀的框架一般都会实现安全实现退出的机制。

如下将退出事件注册到某一框架的平滑关闭函数中

func startMyServer() {
    // ...
    // xxx框架退出函数注册退出事件
    xxx.RegisterQuitter(func() {
        quit.GetQuitEvent().GracefulStop()
    })
}

以上就是Golang实现程序优雅退出的方法详解的详细内容,更多关于Golang程序退出的资料请关注编程网其它相关文章!

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     807人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     351人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     314人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     433人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     221人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯