[提案] server/server.go中Serve优化
⚠️ 验证
- [x] 我已经搜索过 issues,确信这不是一个重复的提案。
✨ 提案摘要
使用select{}阻塞后无法响应外部信号,关闭不优雅
🛠️ 实现方法
使用信号监听或者context等处理
📚 附加上下文
No response
1 select{} 确实不优雅; 2 在 select{} 之前把 lock 给 unlock 掉
你这段代码的最后一行:
select {}
的确是一个 阻塞主 goroutine 的“土办法”。它会让当前 goroutine 永远卡住,CPU 占用很低,但从工程角度看有几个问题:
- 可读性差 —— 别人看代码会疑惑:为什么这里空的
select {}? - 不可控 —— 没有退出机制,服务关闭或收到信号时无法优雅退出。
- 不利于维护 —— 很难扩展,比如需要监听系统信号、控制 shutdown 时,得另写一套逻辑。
常见的改进方式
1. 使用 context 控制生命周期(推荐)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// 监听信号,优雅退出
go func() {
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM)
<-sigCh
cancel()
}()
<-ctx.Done()
return nil
这样,服务会在收到中断信号时自动退出。
2. 使用 sync.WaitGroup 等待后台 goroutine
如果 exportServices() / RegisterServiceInstance() 启动了 goroutine,可以用 sync.WaitGroup 来等待它们完成,而不是简单 select {}:
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
// 运行你的服务逻辑
}()
wg.Wait()
3. 明确暴露 Serve 的退出通道
你可以在 Server 结构里定义一个退出通道,让 Serve 阻塞在这里,外部可控:
type Server struct {
stop chan struct{}
}
func (s *Server) Serve() error {
s.stop = make(chan struct{})
// ... init & export
<-s.stop
return nil
}
func (s *Server) Stop() {
close(s.stop)
}
✅ 总结:
select {} 虽然能用,但不够优雅。更好的做法是结合 context 或退出通道,让 Serve() 可以 优雅退出。
要不要我帮你改写一份 优雅退出的 Serve() 版本,带 context + 信号处理?
当前在 server.NewServer() 时可以配置监听系统信号关闭 server:
internalSignal := true
shutdownCfg := &global.ShutdownConfig{
InternalSignal: &internalSignal,
}
srv, _ := server.NewServer(
server.SetServerShutdown(shutdownCfg),
)
srv.Serve()
当前提供的方式还不够优雅:
- 不能使用自定义的 context
- NewServer() 时就启动了一个 go 携程监听系统信号
@junfa
I'm here