从开源项目学习优雅的关闭 HTTP 服务

从开源项目学习优雅的关闭 HTTP 服务

服务器难免遇到重启,升级等问题。

当服务器关闭的时候,为了避免服务突然中断,产生的不可控和冗余数据,需要有以下考虑。

  1. 摘掉流量,通过网关或负载均衡控制
  2. 服务拒绝新的请求
  3. 等待处理中的请求结束
  4. 如果上一步超时,强制关闭
  5. 释放资源
  6. 关闭服务
  7. 强制关闭,比如多次退出信号

实现

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package httpserver

import (
	"context"
	"net"
	"net/http"
	"time"
)

const (
	defaultReadTimeout     = 10 * time.Second
	defaultWriteTimeout    = 10 * time.Second
	defaultAddr            = ":8080"
	defaultShutdownTimeout = 3 * time.Second
)

// Server HTTP 服务
type Server struct {
	server          *http.Server
	notify          chan error
	shutdownTimeout time.Duration
}

// NewServer 初始化并启动路由
func NewServer(handler http.Handler, opts ...Option) *Server {
	httpSer := http.Server{
		Addr:         defaultAddr,
		Handler:      handler,
		ReadTimeout:  defaultReadTimeout,
		WriteTimeout: defaultWriteTimeout,
	}

	s := &Server{
		server:          &httpSer,
		notify:          make(chan error, 1),
		shutdownTimeout: defaultShutdownTimeout,
	}

	for _, opt := range opts {
		opt(s)
	}
	go s.start()
	return s
}

func (s *Server) start() {
	s.notify <- s.server.ListenAndServe()
	close(s.notify)
}

// Notify .
func (s *Server) Notify() <-chan error {
	return s.notify
}

// Shutdown 关闭服务
func (s *Server) Shutdown() error {
	ctx, cancel := context.WithTimeout(context.Background(), s.shutdownTimeout)
	defer cancel()
	return s.server.Shutdown(ctx)
}

// Option 修改 server 相关参数
type Option func(*Server)

// Port 修改端口
func Port(v string) Option {
	return func(s *Server) {
		s.server.Addr = net.JoinHostPort("", v)
	}
}

使用

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
func main(){
  handler := gin.New()
	server := httpserver.New(handler)

	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt, syscall.SIGTERM)
	// 等待信号
	select {
	case s := <-interrupt:
		fmt.Println("app - Run - signal: " + s.String())
	case err = <-httpServer.Notify():
		fmt.Printf("app - Run - httpServer.Notify: %w", err)
	}
	// 关闭
	err = httpServer.Shutdown()
	if err != nil {
		fmt.Printf("app - Run - httpServer.Shutdown: %w", err)
	}
}

参考

go-clean-template

Licensed under CC BY-NC-SA 4.0
本文阅读量 次, 总访问量 ,总访客数
Built with Hugo .   Theme Stack designed by Jimmy