ggzeng
11/20/2019 - 6:34 AM

gracefull exit

golang优雅退出的方法

Channel关闭原则:不要在消费端关闭channel,不要在有多个并行的生产者时对channel执行关闭操作。

https://github.com/Shitaibin/golang_goroutine_exit

https://www.toutiao.com/a6506067818100818445/

// 一个循环里只从一个工作channel里读数据
go func(in <-chan int) {
    for x := range in {  // 当channel "in"被关闭时会结束循环
        fmt.Printf("Process %d\n", x)
    }
}(inCh)


// 一个循环里需要从一个工作channel里读数据,同时还需响应控制channel
go func() {
    for {
        select {
            case x, ok := <-in:  // channel里读数据,处理数据。当channel关闭时ok为false
                if !ok {
                    return
                }
                fmt.Printf("Process %dn", x)
                processdCnt++
            case <-t.C:          // 这个channel可以用来打印当前处理了多少次了
                fmt.Printf("working, processdCnt = %d\n", processedCnt)
        }
    }
}()

// 一个循环中要处理多个工作channel并且只有某些工作channel都被关闭了才退出
go func() {
    for {
        select {
            case x, ok := <-in1:
                if !ok {
                    in1 = nil     // 设置为nil后,后面select不会再进入
                }
                // process ...
            case y, ok := <-in2:
                if !ok {
                    in2 = nil
                }
                // process ...
            case <-t.C:
                fmt.Printf("working, processdCnt = %d\n", processedCnt)
        }
        if in1 == nil && in2 == nil {
            return              // 所关心的通道都为nil,所以退出
        }
    }
}()

// 使用专门的退出channel主动退出,此时的工作channel可能还为关闭
func worker(stopCh <-chan struct{}) {
    go func() {
        def fmt.Println("worker exit")
        for {
            select {
            case <-stopCh:
                fmt.Println("Recv stop signal")
                return
            case <-t.C:
                fmt.Println("Working ...")
            }
        }
    }()
    return
}
package main

import (
	"context"
	"log"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/gorilla/mux" // need to use dep for package management
)

func TestEndpoint(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(200)
	w.Write([]byte("Test is what we usually do"))
}

func main() {
	router := mux.NewRouter()
	router.HandleFunc("/test", TestEndpoint).Methods("GET")

	srv := &http.Server{
		Addr:    ":8080",
		Handler: router,
	}

	done := make(chan os.Signal, 1)
	signal.Notify(done, os.Interrupt, syscall.SIGINT, syscall.SIGTERM)

	go func() {
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			log.Fatalf("listen: %s\n", err)
		}
	}()
	log.Print("Server Started")

	<-done
	log.Print("Server Stopped")
	
	ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
	defer func() {
		// extra handling here
		cancel()
	}()

	if err := srv.Shutdown(ctx); err != nil {
		log.Fatalf("Server Shutdown Failed:%+v", err)
	}
	log.Print("Server Exited Properly")
}
// https://medium.com/honestbee-tw-engineer/gracefully-shutdown-in-go-http-server-5f5e6b83da5a