Go1.16中新函数的信号。NotifyContext的使用方法

介绍

本篇内容主要讲解“Go1.16中新函数的信号。NotifyContext的使用方法”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Go1.16中新函数的信号。NotifyContext的使用方法”吧!

os/signal 这个包大家可能用的不多。但自从 Go1.8 起,有些人开始使用这个包了,原因是 Go1.8 在 net/http  包新增了一个方法:

func (srv *Server) Shutdown(ctx context.Context) error

有了它就不需要借助第三方库实现优雅关闭服务了。具体怎么做呢?

func main() {  server = http.Server{   Addr: ":8080",  }   http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {   time.Sleep(time.Second * 10)   fmt.Fprint(w, "Hello world!")  })     go server.ListenAndServe()   // 监听中断信号(CTRL + C)  c := make(chan os.Signal, 1)  signal.Notify(c, os.Interrupt)  <-c   // 重置 os.Interrupt 的默认行为  signal.Reset(os.Interrupt)   fmt.Println("shutting down gracefully, press Ctrl+C again to force")   // 给程序最多 5 秒时间处理正在服务的请求  timeoutCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)  defer cancel()   if err := server.Shutdown(timeoutCtx); err != nil {   fmt.Println(err)  } }
  • 这里利用 os/signal 包监听 Interrupt 信号;

  • 收到该信号后,16 行 <-c 会返回;

  • 为了可以再次 CTRL + C 强制退出,通过 Reset 恢复 os.中断的默认行为,(这不是必须的)

优雅退出的关键:1)新请求进不来;2)已有请求给时间处理完,所以,在接收到信号后,调用服务器。Shutdown 方法,阻止新请求进来,同时给 5  秒等待时间,让已经进来的请求有时间处理。

在 Go1.16 中,os/signal 包新增了一个函数:

func NotifyContext(parent context.Context, signals ...os.Signal) (ctx context.Context, stop context.CancelFunc)

功能和 Notify 类似,但用法上有些不同。上面的例子改用 NotifyContext:

func after() {  server = http.Server{   Addr: ":8080",  }   http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {   time.Sleep(time.Second * 10)   fmt.Fprint(w, "Hello world!")  })   go server.ListenAndServe()     // 监听中断信号(CTRL + C)  ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)  <-ctx.Done()    // 重置 os.Interrupt 的默认行为,类似 signal.Reset  stop()  fmt.Println("shutting down gracefully, press Ctrl+C again to force")    // 给程序最多 5 秒时间处理正在服务的请求  timeoutCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)  defer cancel()   if err := server.Shutdown(timeoutCtx); err != nil {   fmt.Println(err)  } }

和上面的写法有区别,完成的功能一样的。其实 NotifyContext 的内部就是基于 Notify 实现的:

func NotifyContext(parent context.Context, signals ...os.Signal) (ctx context.Context, stop context.CancelFunc) {  ctx, cancel := context.WithCancel(parent)  c := &signalCtx{   Context: ctx,   cancel:  cancel,   signals: signals,  }  c.ch = make(chan os.Signal, 1)  Notify(c.ch, c.signals...)  if ctx.Err() == nil {   go func() {    select {    case <-c.ch:     c.cancel()    case <-c.Done():    }   }()  }  return c, c.stop }

Go1.16中新函数的信号。NotifyContext的使用方法