使用Golang的Context管理上下文的方法
golang 1.7版本中context庫(kù)被很多標(biāo)準(zhǔn)庫(kù)的模塊所使用,比如net/http和os的一些模塊中,利用這些原生模塊,我們就不需要自己再寫(xiě)上下文的管理器了,直接調(diào)用函數(shù)接口即可實(shí)現(xiàn),利用context我們可以實(shí)現(xiàn)一些比如請(qǐng)求的聲明周期內(nèi)的變量管理,執(zhí)行一些操作的超時(shí)等等。
保存上下文對(duì)象
這里我們通過(guò)一個(gè)簡(jiǎn)單的例子來(lái)看一下如何使用context的特性來(lái)實(shí)現(xiàn)上下文的對(duì)象保存,這里我們寫(xiě)了一個(gè)簡(jiǎn)單的http server,具有登錄和退出,狀態(tài)檢查路由(檢查用戶(hù)是否登錄)
func main(){ mux:=http.NewServeMux() mux.HandleFunc("/",StatusHandler) mux.HandleFunc("/login",LoginHandler) mux.HandleFunc("/logout",LogoutHandler) contextedMux:=AddContextSupport(mux) log.Fatal(http.ListenAndServe(":8080",contextedMux)) }
其中的AddContextSupport是一個(gè)中間件,用來(lái)綁定一個(gè)context到原來(lái)的handler中,所有的請(qǐng)求都必須先經(jīng)過(guò)該中間件后才能進(jìn)入各自的路由處理中。具體的實(shí)現(xiàn)代碼如下:
func AddContextSupport(next http.Handler)http.Handler{ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Println(r.Method, "-", r.RequestURI) cookie, _ := r.Cookie("username") if cookie != nil { ctx := context.WithValue(r.Context(), "username", cookie.Value) // WithContext returns a shallow copy of r with its context changed // to ctx. The provided ctx must be non-nil. next.ServeHTTP(w, r.WithContext(ctx)) } else { next.ServeHTTP(w, r) } }) }
該中間件可以打印每次請(qǐng)求的方法和請(qǐng)求的url,然后獲得請(qǐng)求的cookie值,如果cookie為空的話(huà)則繼續(xù)傳遞到對(duì)應(yīng)的路由處理函數(shù)中,否則保存cookie的值到Context, 注意這里的Context()是request對(duì)象的方法, 將創(chuàng)建一個(gè)新的上下文對(duì)象(如果context為空),context.WithValue()函數(shù)將key和value保存在新的上下文對(duì)象中并返回該對(duì)象。
其余的路由處理函數(shù)代碼如下, 分別用來(lái)保存cookie的登錄路由LoginHandler(),還有刪除cookie的退出路由處理函數(shù)LogoutHandler()。
func LoginHandler(w http.ResponseWriter,r *http.Request){ expitation := time.Now().Add(24*time.Hour) var username string if username=r.URL.Query().Get("username");username==""{ username = "guest" } cookie:=http.Cookie{Name:"username",Value:username,Expires:expitation} http.SetCookie(w,&cookie) } func LogoutHandler(w http.ResponseWriter, r *http.Request) { expiration := time.Now().AddDate(0, 0, -1) cookie := http.Cookie{Name: "username", Value: "alice_cooper@gmail.com", Expires: expiration} http.SetCookie(w, &cookie) }
這里我們?cè)谡?qǐng)求/login的時(shí)候,可以傳遞一個(gè)參數(shù)username到函數(shù)中,比如/login?username=alice , 默認(rèn)為”guest”用戶(hù). 設(shè)置的cookie有效期為1天,刪除的時(shí)候我們只需要設(shè)置cookie為之前的日期即可。
另外一個(gè)關(guān)鍵的部分是讀取上下文保存內(nèi)容的 StatusHandler() 路由處理函數(shù),該函數(shù)將調(diào)用r.Context()獲得request的上下文,如果我們執(zhí)行了login后,那我們?cè)谥虚g件中已經(jīng)設(shè)置了該對(duì)象,所以請(qǐng)求將查看是否上下文對(duì)象中保存了一個(gè)名為username的對(duì)象,如果有的話(huà)則回應(yīng)一個(gè)歡迎頁(yè)面。否則告知用戶(hù)沒(méi)有登錄。
func StatusHandler(w http.ResponseWriter,r *http.Request){ if username:=r.Context().Value("username"); username!=nil{ w.WriteHeader(http.StatusOK) w.Write([]byte("Hi username:"+username.(string)+"\n")) }else{ w.WriteHeader(http.StatusNotFound) w.Write([]byte("Not Logged in")) } }
我們不僅僅可以在上下文中保存簡(jiǎn)單的類(lèi)型,我們可以保存任何類(lèi)型的數(shù)據(jù),因?yàn)閂alue() 返回的對(duì)象是一個(gè)interface{}對(duì)象,所以我們需要轉(zhuǎn)換一下才能使用。
超時(shí)處理
對(duì)于簡(jiǎn)單的保存和傳遞對(duì)象,使用context的確很方便,但是該庫(kù)的使用不僅僅是保存變量,還可以創(chuàng)建一個(gè)超時(shí)和取消的行為,比如說(shuō)我們web端去請(qǐng)求了其他的資源,但是該資源的處理比較耗時(shí),我們無(wú)法預(yù)見(jiàn)什么時(shí)候能夠返回,如果讓用戶(hù)超時(shí)的話(huà),實(shí)在是不太好,所以我們需要?jiǎng)?chuàng)建一個(gè)超時(shí)的操作,主動(dòng)判斷是否超時(shí),然后傳遞一個(gè)合適的行為給用戶(hù)。
這里我們現(xiàn)在路由中增加一個(gè)長(zhǎng)期運(yùn)行的job路由
mux.HandleFunc("/longjob",jobWithCancelHandler)
具體的處理如下,我們的handler會(huì)利用WithCancel() 返回一個(gè)新的(如果沒(méi)有創(chuàng)建)或者原來(lái)已保存的上下文,還有一個(gè)cancel對(duì)象,這個(gè)對(duì)象可以用來(lái)手動(dòng)執(zhí)行取消操作。另外我們的url中可以指定這個(gè)任務(wù)模擬執(zhí)行的長(zhǎng)度,比如/longjob?jobtime=10則代表模擬的任務(wù)將會(huì)執(zhí)行超過(guò)10秒。 執(zhí)行任務(wù)的函數(shù)longRunningCalculation()返回一個(gè)chan該chan會(huì)在執(zhí)行時(shí)間到期后寫(xiě)入一個(gè)Done字符串。
handler中我們就可以使用select語(yǔ)句監(jiān)聽(tīng)兩個(gè)非緩存的channel,阻塞直到有數(shù)據(jù)寫(xiě)到任何一個(gè)channel中。比如代碼中我們?cè)O(shè)置了超時(shí)是5秒,而任務(wù)執(zhí)行10秒的話(huà)則5秒到期后ctx.Done()會(huì)因?yàn)閏ancel()的調(diào)用而寫(xiě)入數(shù)據(jù),這樣該handler就會(huì)因?yàn)槌瑫r(shí)退出。否則的話(huà)則執(zhí)行正常的job處理后獲得傳遞的“Done”退出。
func longRunningCalculation(timeCost int)chan string{ result:=make(chan string) go func (){ time.Sleep(time.Second*(time.Duration(timeCost))) result<-"Done" }() return result } func jobWithCancelHandler(w http.ResponseWriter, r * http.Request){ var ctx context.Context var cancel context.CancelFunc var jobtime string if jobtime=r.URL.Query().Get("jobtime");jobtime==""{ jobtime = "10" } timecost,err:=strconv.Atoi(jobtime) if err!=nil{ timecost=10 } log.Println("Job will cost : "+jobtime+"s") ctx,cancel = context.WithCancel(r.Context()) defer cancel() go func(){ time.Sleep(5*time.Second) cancel() }() select{ case <-ctx.Done(): log.Println(ctx.Err()) return case result:=<-longRunningCalculation(timecost): io.WriteString(w,result) } return }
這就是使用context的一些基本方式,其實(shí)context還有很多函數(shù)這里沒(méi)有涉及,比如WithTimeout和WithDeadline等,但是使用上都比較相似,這里就不在舉例。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Golang中interface{}轉(zhuǎn)為數(shù)組的操作
這篇文章主要介紹了Golang中interface{}轉(zhuǎn)為數(shù)組的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2021-04-04golang并發(fā)編程中Goroutine 協(xié)程的實(shí)現(xiàn)
Go語(yǔ)言中的協(xié)程是一種輕量級(jí)線(xiàn)程,通過(guò)在函數(shù)前加go關(guān)鍵字來(lái)并發(fā)執(zhí)行,具有動(dòng)態(tài)棧、快速啟動(dòng)和低內(nèi)存使用等特點(diǎn),本文就來(lái)詳細(xì)的介紹一下,感興趣的可以了解一下2024-10-10GoLang中socket心跳檢測(cè)的實(shí)現(xiàn)
本文主要介紹了GoLang中socket心跳檢測(cè)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2025-02-02Go語(yǔ)言網(wǎng)站使用異步編程和Goroutine提高Web的性能
作為一門(mén)現(xiàn)代化編程語(yǔ)言,Go語(yǔ)言提供了強(qiáng)大的異步編程能力,使得程序員可以以更高效的方式處理并發(fā)任務(wù),在Go語(yǔ)言中,使用Goroutine在單個(gè)進(jìn)程中實(shí)現(xiàn)多任務(wù)并行處理,以及如何使用協(xié)程池來(lái)進(jìn)一步提高Web服務(wù)器的處理能力,2024-01-01Go語(yǔ)言中實(shí)現(xiàn)打印堆棧的errors包的用法詳解
因?yàn)镚o語(yǔ)言提供的錯(cuò)誤太簡(jiǎn)單了,以至于簡(jiǎn)單的我們無(wú)法更好的處理問(wèn)題,所以誕生了很多對(duì)錯(cuò)誤處理的庫(kù),github.com/pkg/errors是比較簡(jiǎn)潔的一樣,本文就來(lái)聊聊它的具體用法吧2023-07-07用go gin server來(lái)做文件上傳服務(wù)
今天小編就為大家分享一篇關(guān)于用go gin server來(lái)做文件上傳服務(wù),小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-04-04