Go1.16 中的新函数 Signal.NotifyContext 怎么用?

开发 后端
从封装上看,NotifyContext 做的更好。而且,如果在某些需要 Context 的场景下,它把监控系统信号和创建 Context 一步搞定。

[[403053]]

大家好,我是 polarisxu。

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

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

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

  1. func main() { 
  2.  server = http.Server{ 
  3.   Addr: ":8080"
  4.  } 
  5.  
  6.  http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 
  7.   time.Sleep(time.Second * 10) 
  8.   fmt.Fprint(w, "Hello world!"
  9.  }) 
  10.    
  11.  go server.ListenAndServe() 
  12.  
  13.  // 监听中断信号(CTRL + C) 
  14.  c := make(chan os.Signal, 1) 
  15.  signal.Notify(c, os.Interrupt) 
  16.  <-c 
  17.  
  18.  // 重置 os.Interrupt 的默认行为 
  19.  signal.Reset(os.Interrupt) 
  20.  
  21.  fmt.Println("shutting down gracefully, press Ctrl+C again to force"
  22.  
  23.  // 给程序最多 5 秒时间处理正在服务的请求 
  24.  timeoutCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second
  25.  defer cancel() 
  26.  
  27.  if err := server.Shutdown(timeoutCtx); err != nil { 
  28.   fmt.Println(err) 
  29.  } 
  • 这里利用 os/signal 包监听 Interrupt 信号;
  • 收到该信号后,16 行 <-c 会返回;
  • 为了可以再次 CTRL + C 强制退出,通过 Reset 恢复 os.Interrupt 的默认行为;(这不是必须的)

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

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

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

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

  1. func after() { 
  2.  server = http.Server{ 
  3.   Addr: ":8080"
  4.  } 
  5.  
  6.  http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { 
  7.   time.Sleep(time.Second * 10) 
  8.   fmt.Fprint(w, "Hello world!"
  9.  }) 
  10.  
  11.  go server.ListenAndServe() 
  12.   
  13.   // 监听中断信号(CTRL + C) 
  14.  ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt) 
  15.  <-ctx.Done() 
  16.  
  17.   // 重置 os.Interrupt 的默认行为,类似 signal.Reset 
  18.  stop() 
  19.  fmt.Println("shutting down gracefully, press Ctrl+C again to force"
  20.  
  21.   // 给程序最多 5 秒时间处理正在服务的请求 
  22.  timeoutCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second
  23.  defer cancel() 
  24.  
  25.  if err := server.Shutdown(timeoutCtx); err != nil { 
  26.   fmt.Println(err) 
  27.  } 

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

  1. func NotifyContext(parent context.Context, signals ...os.Signal) (ctx context.Context, stop context.CancelFunc) { 
  2.  ctx, cancel := context.WithCancel(parent) 
  3.  c := &signalCtx{ 
  4.   Context: ctx, 
  5.   cancel:  cancel, 
  6.   signals: signals, 
  7.  } 
  8.  c.ch = make(chan os.Signal, 1) 
  9.  Notify(c.ch, c.signals...) 
  10.  if ctx.Err() == nil { 
  11.   go func() { 
  12.    select { 
  13.    case <-c.ch: 
  14.     c.cancel() 
  15.    case <-c.Done(): 
  16.    } 
  17.   }() 
  18.  } 
  19.  return c, c.stop 

只是在返回的 stop 被调用时,会执行 os/signal 包中的 Stop 函数,这个 Stop 函数的功能和 Reset 类似。因此上面 Notify 的例子,Reset 的地方可以改为 Stop。

从封装上看,NotifyContext 做的更好。而且,如果在某些需要 Context 的场景下,它把监控系统信号和创建 Context 一步搞定。

NotifyContext 的用法,优雅的关闭服务,你掌握了吗?希望你实际动手试验下,启动服务,通过 curl http://localhost:8080/ 访问,然后按 CTRL + C,看看具体效果。只看不动手,基本知识不是你的。

关于 NotifyContext 函数的文档可以在这里查看:https://docs.studygolang.com/pkg/os/signal/#NotifyContext。

本文转载自微信公众号「polarisxu」,可以通过以下二维码关注。转载本文请联系polarisxu公众号。

 

责任编辑:武晓燕 来源: polarisxu
相关推荐

2021-02-02 09:10:12

Go语言二进制

2021-02-19 09:01:37

Go项目模块

2024-02-21 08:33:27

GoReadDir性能

2021-04-16 20:47:42

Go 指令函数

2020-11-24 13:05:35

Go语言泛型

2021-11-01 20:17:07

Go项目目录

2021-02-22 11:30:07

Golang 1.16ModuleGolang

2024-11-19 09:10:19

迭代器Go语言

2023-10-23 19:27:21

Go函数

2022-07-04 14:41:31

Go 语言变长参数变长参数函数

2022-07-03 23:07:48

Go语言参数

2018-11-05 14:53:14

Go函数代码

2021-09-15 07:56:33

函数类型Go

2020-05-06 20:40:03

Go编程语言

2022-02-16 08:59:43

Go方法Title

2023-10-27 11:27:14

Go函数

2021-07-28 09:07:49

Go版本项目

2021-01-20 16:26:17

Go编程语言

2022-02-11 21:01:18

GoNetip网络库

2021-09-05 18:25:30

Go命令仓库
点赞
收藏

51CTO技术栈公众号