package main import ( "context" "encoding/json" "fmt" "math/rand" "net/http" "os" "os/signal" "time" ) var logChan = make(chan map[string]interface{}) var requestStatusMap = map[int]bool{} var done = make(chan bool, 1) var quit = make(chan os.Signal, 1) //为什么这样可以平滑重启? // 正常情况下是server.ListenAndServe() 这个位置hang住整个进程的 // 可以把这个程序看成两部分,1个是web服务的监听部分,一个是处理部分, 如果web服务器不开启了,那么就不能处理新进来的请求了(可以理解为一个带路的) // 真正让这个请求断掉 是因为主进程(main)被kill // 所以平滑重启的原理就是,先kill掉web服务器,不让新的请求进来,等现有的全部请求完了,然后结束当前进程 func main() { server := newServer() signal.Notify(quit, os.Interrupt) go monitorKill(server, quit) server.ListenAndServe() <-done } func newServer() *http.Server { router := http.NewServeMux() router.HandleFunc("/hello", sayHello) return &http.Server{ Addr: ":8262", Handler: router, } } func monitorKill(server *http.Server, quit <-chan os.Signal) { <-quit go shutDown(server) for { if len(requestStatusMap) != 0 { fmt.Println("目前还有进行中的请求,请稍等") time.Sleep(time.Second * 1) continue } else { close(done) break } } } func shutDown(server *http.Server) { if err := server.Shutdown(context.Background()); err != nil { fmt.Println(err) } } func sayHello(w http.ResponseWriter, r *http.Request) { go WriteInfo()//请求写日志 var uniqueId = GenerateRangeNum(1, 1000) requestStatusMap[uniqueId] = false url := r.URL.Path query := r.URL.RawQuery method := r.Method a := map[string] interface{}{ "url" : url, "method" : method, "query" : query, "response": "hello world!", } logChan<-a w.Write([]byte("hello world!")) time.Sleep(time.Second * 10) delete(requestStatusMap, uniqueId) } func WriteInfo() { info := <-logChan fileName := "/tmp/weekhomework.log" _, err := os.Stat(fileName) if err != nil || os.IsNotExist(err) { _, _ = os.Create(fileName) } f,err := os.OpenFile(fileName, os.O_WRONLY, 0644) defer f.Close() if err !=nil { fmt.Println(err.Error()) } else { //追加写入 为什么O_APPEND 模式无法写入? todo n, _ := f.Seek(0, 2) infostr, _ := json.Marshal(info) _,err=f.WriteAt([]byte(string(infostr) +" "), n) } } func GenerateRangeNum(min int, max int) int { if min == max { return min } rand.Seed(time.Now().Unix()) randNum := rand.Intn(max-min) + min return randNum }
主要思路:对于每个请求都做记录,处理完成之后做删除。 用一个协程去监控中断信号,有中断信号先把http服务关闭。
如果这个时候还有请求没有处理完,那么就轮训等待,等全部处理完那么就 发出终止信号结束main进程的执行