<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在業務程式碼開發過程中,我們會有很大概率使用go語言的goroutine來開啟一個新的goroutine執行另外一段業務,或者開啟多個goroutine來並行執行多個業務邏輯。所以我為hade框架增加了兩個方法goroutine.SafeGo 和 goroutine.SafeGoAndWait。
SafeGo 這個函數,提供了一種goroutine安全的函數呼叫方式。主要適用於業務中需要進行開啟非同步goroutine業務邏輯呼叫的場景。
// SafeGo 進行安全的goroutine呼叫 // 第一個引數是context介面,如果還實現了Container介面,且繫結了紀錄檔服務,則使用紀錄檔服務 // 第二個引數是匿名函數handler, 進行最終的業務邏輯 // SafeGo 函數並不會返回error,panic都會進入hade的紀錄檔服務 func SafeGo(ctx context.Context, handler func())
呼叫方式參照如下的單元測試用例:
func TestSafeGo(t *testing.T) { container := tests.InitBaseContainer() container.Bind(&log.HadeTestingLogProvider{}) ctx, _ := gin.CreateTestContext(httptest.NewRecorder()) goroutine.SafeGo(ctx, func() { time.Sleep(1 * time.Second) return }) t.Log("safe go main start") time.Sleep(2 * time.Second) t.Log("safe go main end") goroutine.SafeGo(ctx, func() { time.Sleep(1 * time.Second) panic("safe go test panic") }) t.Log("safe go2 main start") time.Sleep(2 * time.Second) t.Log("safe go2 main end") }
SafeGoAndWait 這個函數,提供安全的多並行呼叫方式。該函數等待所有函數都結束後才返回。
// SafeGoAndWait 進行並行安全並行呼叫 // 第一個引數是context介面,如果還實現了Container介面,且繫結了紀錄檔服務,則使用紀錄檔服務 // 第二個引數是匿名函數handlers陣列, 進行最終的業務邏輯 // 返回handlers中任何一個錯誤(如果handlers中有業務邏輯返回錯誤) func SafeGoAndWait(ctx context.Context, handlers ...func() error) error
呼叫方式參照如下的單元測試用例:
func TestSafeGoAndWait(t *testing.T) { container := tests.InitBaseContainer() container.Bind(&log.HadeTestingLogProvider{}) errStr := "safe go test error" t.Log("safe go and wait start", time.Now().String()) ctx, _ := gin.CreateTestContext(httptest.NewRecorder()) err := goroutine.SafeGoAndWait(ctx, func() error { time.Sleep(1 * time.Second) return errors.New(errStr) }, func() error { time.Sleep(2 * time.Second) return nil }, func() error { time.Sleep(3 * time.Second) return nil }) t.Log("safe go and wait end", time.Now().String()) if err == nil { t.Error("err not be nil") } else if err.Error() != errStr { t.Error("err content not same") } // panic error err = goroutine.SafeGoAndWait(ctx, func() error { time.Sleep(1 * time.Second) return errors.New(errStr) }, func() error { time.Sleep(2 * time.Second) panic("test2") }, func() error { time.Sleep(3 * time.Second) return nil }) if err == nil { t.Error("err not be nil") } else if err.Error() != errStr { t.Error("err content not same") } }
實現方面,有幾個難點記錄下。
可以看到handler函數在兩個介面中是不一樣的。在SafeGo介面中,handler定義為func()
而在SafeGoAndWait中,定義為func() error
兩者的區別就在於SafeGo這個介面是沒有能力處理error的,因為它go出去一個goroutine就直接進行接下來的操作了。而SafeGoAndWait是必須等到所有的請求結束,所以它是有能力接收到error的。
所以SafeGo的handler沒有必要設定error返回值,而SafeGoAndWait是可以設定error的。
如果出現了panic,如何將panic的紀錄檔列印出來。
整個框架我們並不希望有任何的全域性變數,包括全域性的Log,所以我這裡做了一個相容邏輯。
如果只是傳遞一個context,我們就使用官方的log包進行列印。
如果傳遞的是一個即實現了context,又實現了container介面的結構,我們就從container中獲取紀錄檔服務,來進行紀錄檔列印。這樣框架的所有紀錄檔就能統一在紀錄檔列印裡面。
if logger != nil { logger.Error(ctx, "safe go handler panic", map[string]interface{}{ "stack": string(buf), "err": e, }) } else { log.Printf("panict%vt%s", e, buf) }
由於我們修改了gin的context,讓它支援了我們的container容器結構,所以我們可以直接將gin.Context傳遞進來。具體使用起來就像這樣了:
// DemoGoroutine goroutine 的使用範例 func (api *DemoApi) DemoGoroutine(c *gin.Context) { logger := c.MustMakeLog() logger.Info(c, "request start", nil) // 初始化一個orm.DB gormService := c.MustMake(contract.ORMKey).(contract.ORMService) db, err := gormService.GetDB(orm.WithConfigPath("database.default")) if err != nil { logger.Error(c, err.Error(), nil) c.AbortWithError(50001, err) return } db.WithContext(c) err = goroutine.SafeGoAndWait(c, func() error { // 查詢一條資料 queryUser := &User{ID: 1} err = db.First(queryUser).Error logger.Info(c, "query user1", map[string]interface{}{ "err": err, "name": queryUser.Name, }) return err }, func() error { // 查詢一條資料 queryUser := &User{ID: 2} err = db.First(queryUser).Error logger.Info(c, "query user2", map[string]interface{}{ "err": err, "name": queryUser.Name, }) return err }) if err != nil { c.AbortWithError(50001, err) return } c.JSON(200, "ok") }
官方的panic其實列印的是所有goroutine的堆疊資訊。但是這裡我們希望列印的是出panic的那個堆疊資訊。所以我們會使用
debug.Stack()
來列印出問題的goroutine的堆疊資訊。
為了列印美觀,這裡將換行符統一替換為n 來進行展示。
具體的實現程式碼可以參考github地址:https://github.com/gohade/hade/blob/main/framework/util/goroutine/goroutine.go
說明檔案:https://github.com/gohade/hade/blob/main/docs/guide/util.md
為hade封裝了兩個SafeGo方法。特別是第二個SafeGoAndWait,在實際工作中確實是非常有用的。
到此這篇關於如何封裝安全的go的文章就介紹到這了,更多相關封裝安全的go內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45