基于 errgroup 实现一个 http server 的启动和关闭 ,以及 linux signal 信号的注册和处理,要保证能够一个退出,全部注销退出。
实现方案根据描述信息,可以简单汇总成3块内容:
实现 HTTP server 的启动和关闭监听 linux signal信号,支持 kill -9 或 Ctrl+C 的中断 *** 作 *** 作errgroup 实现多个 goroutine 的级联退出按照实现方案,我们将任务拆分成多个小的功能。
实现 http server 的启动和关闭功能使用 chan 实现对 linux signal 中断的注册和处理通过 errgroup + context 的形式,对 1、2中的 goroutine 进行级联注销 具体过程 实现 http server 的启动和关闭一般的http server 启动
func helloServer(w http.ResponseWriter, req *http.Request){
io.WriteString(w,"hello,word!")
}
func main(){
http.HandleFunc("/hello", helloServer)
if err := http.ListenAndServer(":8080",nil); err!=nil{
log.Fatal("server start error: ", err)
}
}
http.ListenAndServer 其实最后调用的是 func (srv *Server) ListenAndServe() error {…},所以我们可以直接使用 srv *Server 来调用,代码如下
srv := &http.Server{Addr: ":9090"}
http.HandleFunc("/hello", HelloServer2)
fmt.Println("http server start")
if err := srv.ListenAndServer(); err!=nil{
log.Fatal("server start error: ", err)
}
//关闭 server
// srv.Shutdown(context.TODO())
使用 chan 实现对中断的注册和处理
通过 signal.Notify(…) 实现对中断信号量的监听,完整代码如下
func main(){
c := make(chan os.Signal, 1)
signal.Notify(c)
//block until a signal is received.
s := <-c
fmt.Println("Go signal:", s) // Got signla: terminal
}
通过 errgroup + context 的形式 管理 goroutine
errgroup 是借助 waitgroup、context 以及sync.Once 这3个组件实现的,可以把第一个出错的 goroutine 的错误信息传递出来。
Wait() 通过 waitgroup.Wait() 实现阻塞等待;WithContext() 通过 context.WithCancel(ctx), 设定返回的cancel 方法,来级联取消其他 child context 的goroutineGo() 通过 Once 实现执行一次 cancel() *** 作,并记录第一个出错信息一个简单的demo
func main(){
ctx := context.Backgorund()
group, errCtx := errgroup.WithGroup(ctx)
for index :=0; index<3; index++ {
indexTemp := index;
group.Go(func() err{
time.Sleep(indexTemp * time.Second)
fmt.Printf("goroutine %d done!\n", indeTemp)
if( indexTemp == 2){
return errors.New(" index == 2")
}
return nil
})
}
if err := group.Wait(); err != nil{
fmt.Println("done errors: ", err)
}else{
fmt.Println("all done successfully!")
}
}
最终的实现
//启动 HTTP server
func StartHttpServer(srv *http.Server) error {
http.HandleFunc("/hello", HelloServer2)
fmt.Println("http server start")
err := srv.ListenAndServe()
return err
}
// 增加一个 HTTP hanlder
func HelloServer2(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, "hello, world!\n")
}
//1. 基于 errgroup 实现一个 http server 的启动和关闭 ,以及 linux signal 信号的注册和处理,要保证能够一个退出,全部注销退出。
func main() {
ctx := context.Background()
// 定义 withCancel -> cancel() 方法 去取消下游的 Context
ctx, cancel := context.WithCancel(ctx)
// 使用 errgroup 进行 goroutine 取消
group, errCtx := errgroup.WithContext(ctx)
//http server
srv := &http.Server{Addr: ":9090"}
group.Go(func() error {
return StartHttpServer(srv)
})
group.Go(func() error {
<-errCtx.Done() //阻塞。因为 cancel、timeout、deadline 都可能导致 Done 被 close
fmt.Println("http server stop")
return srv.Shutdown(errCtx) // 关闭 http server
})
chanel := make(chan os.Signal, 1) //这里要用 buffer 为1的 chan
signal.Notify(chanel)
group.Go(func() error {
for {
select {
case <-errCtx.Done(): // 因为 cancel、timeout、deadline 都可能导致 Done 被 close
return errCtx.Err()
case <-chanel: // 因为 kill -9 或其他而终止
cancel()
}
}
return nil
})
if err := group.Wait(); err != nil {
fmt.Println("group error: ", err)
}
fmt.Println("all group done!")
}
小结
基于 errgroup 实现一个 http server 的启动和关闭 ,以及 linux signal 信号的注册和处理,要保证能够一个退出,全部注销退出。
我们可以分析并定义好结束内容,以终为始,根据交付内容做好任务分解。这里我们分解成了3个小功能逐个击破:
实现 HTTP server 的启动和关闭监听 linux 的 signal信号,支持 kill -9 或 Ctrl+C 的中断 *** 作 *** 作errgroup 实现多个 goroutine 的级联退出可以先从简单的Demo入手,学习使用 关键技术,然后最后拼装起来,打包要求的内容
Referenceshttps://blog.csdn.net/yzf279533105/article/details/97039688
https://studygolang.com/articles/32169?fr=sidebar
https://www.cnblogs.com/ricklz/p/14500392.html
https://www.jianshu.com/p/3e23e46f5a40
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)