golang 監(jiān)聽(tīng)服務(wù)的信號(hào),實(shí)現(xiàn)平滑啟動(dòng),linux信號(hào)說(shuō)明詳解
監(jiān)聽(tīng)服務(wù)的信號(hào),實(shí)現(xiàn)平滑啟動(dòng),linux信號(hào)說(shuō)明
package main import ( "context" "fmt" "golang.org/x/sync/errgroup" "net/http" "os" "os/signal" "syscall" ) func main() { g, ctx := errgroup.WithContext(context.Background()) fmt.Println("服務(wù)啟動(dòng)start!") addr := ":9091" s :=&http.Server{ Addr: addr, Handler:http.DefaultServeMux, } g.Go(func() error { http.HandleFunc("/test1", func(writer http.ResponseWriter, request *http.Request) { fmt.Println("tes1") writer.Write([]byte("tes1")) }) return s.ListenAndServe() }) g.Go(func() error { exit := make(chan os.Signal) //監(jiān)聽(tīng) Ctrl+C 信號(hào) signal.Notify(exit, syscall.SIGINT, syscall.SIGTERM) select { case <-exit: fmt.Println("進(jìn)程已被取消~") return s.Shutdown(ctx) } }) err := g.Wait() if err != nil { fmt.Println(err) } fmt.Println("服務(wù)啟動(dòng)成功!") if ctx.Err() !=nil { fmt.Println(ctx.Err()) fmt.Println("服務(wù)關(guān)閉成功!") os.Exit(0) } }
補(bǔ)充:golang http服務(wù)實(shí)現(xiàn)平滑重啟
看代碼吧~
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() 這個(gè)位置hang住整個(gè)進(jìn)程的 // 可以把這個(gè)程序看成兩部分,1個(gè)是web服務(wù)的監(jiān)聽(tīng)部分,一個(gè)是處理部分, 如果web服務(wù)器不開(kāi)啟了,那么就不能處理新進(jìn)來(lái)的請(qǐng)求了(可以理解為一個(gè)帶路的) // 真正讓這個(gè)請(qǐng)求斷掉 是因?yàn)橹鬟M(jìn)程(main)被kill // 所以平滑重啟的原理就是,先kill掉web服務(wù)器,不讓新的請(qǐng)求進(jìn)來(lái),等現(xiàn)有的全部請(qǐng)求完了,然后結(jié)束當(dāng)前進(jìn)程 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("目前還有進(jìn)行中的請(qǐng)求,請(qǐng)稍等") 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()//請(qǐng)求寫(xiě)日志 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 { //追加寫(xiě)入 為什么O_APPEND 模式無(wú)法寫(xiě)入? todo n, _ := f.Seek(0, 2) infostr, _ := json.Marshal(info) _,err=f.WriteAt([]byte(string(infostr) +"\n"), 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 }
主要思路:
對(duì)于每個(gè)請(qǐng)求都做記錄,處理完成之后做刪除。 用一個(gè)協(xié)程去監(jiān)控中斷信號(hào),有中斷信號(hào)先把http服務(wù)關(guān)閉。
如果這個(gè)時(shí)候還有請(qǐng)求沒(méi)有處理完,那么就輪訓(xùn)等待,等全部處理完那么就 發(fā)出終止信號(hào)結(jié)束main進(jìn)程的執(zhí)行
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。
相關(guān)文章
Go語(yǔ)言題解LeetCode1266訪問(wèn)所有點(diǎn)的最小時(shí)間示例
這篇文章主要為大家介紹了Go語(yǔ)言題解LeetCode1266訪問(wèn)所有點(diǎn)的最小時(shí)間示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01Go語(yǔ)言映射內(nèi)部實(shí)現(xiàn)及基礎(chǔ)功能實(shí)戰(zhàn)
這篇文章主要為大家介紹了Go語(yǔ)言映射的內(nèi)部實(shí)現(xiàn)和基礎(chǔ)功能實(shí)戰(zhàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪<BR>2022-03-03GoLang bytes.Buffer基礎(chǔ)使用方法詳解
Go標(biāo)準(zhǔn)庫(kù)中的bytes.Buffer(下文用Buffer表示)類(lèi)似于一個(gè)FIFO的隊(duì)列,它是一個(gè)流式字節(jié)緩沖區(qū),我們可以持續(xù)向Buffer尾部寫(xiě)入數(shù)據(jù),從Buffer頭部讀取數(shù)據(jù)。當(dāng)Buffer內(nèi)部空間不足以滿(mǎn)足寫(xiě)入數(shù)據(jù)的大小時(shí),會(huì)自動(dòng)擴(kuò)容2023-03-03Go語(yǔ)言Gin框架實(shí)現(xiàn)HTML頁(yè)面渲染
Web開(kāi)發(fā)中,我們經(jīng)常要面對(duì)如何將數(shù)據(jù)渲染到前端的問(wèn)題,這就涉及到了模板引擎的知識(shí),Go語(yǔ)言的Gin框架就提供了強(qiáng)大的HTML模板渲染功能,本文就來(lái)為大家介紹,有需要的朋友可以借鑒參考下,希望能夠有所幫助2024-01-01golang?gorm的Callbacks事務(wù)回滾對(duì)象操作示例
這篇文章主要為大家介紹了golang?gorm的Callbacks事務(wù)回滾對(duì)象操作示例,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步早日升職加薪2022-04-04Go語(yǔ)言中Struct與繼承與匿名字段和內(nèi)嵌結(jié)構(gòu)體全面詳解
這篇文章主要介紹了Go語(yǔ)言中Struct與繼承與匿名字段和內(nèi)嵌結(jié)構(gòu)體,Go語(yǔ)言中通過(guò)結(jié)構(gòu)體的內(nèi)嵌再配合接口比面向?qū)ο缶哂懈叩臄U(kuò)展性和靈活性,感興趣的可以了解一下2023-04-04