最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

一文教你如何封裝安全的go

 更新時(shí)間:2022年02月23日 09:15:24   作者:葉劍峰  
這篇文章主要給大家介紹了關(guān)于如何封裝安全go的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下

前言

在業(yè)務(wù)代碼開(kāi)發(fā)過(guò)程中,我們會(huì)有很大概率使用go語(yǔ)言的goroutine來(lái)開(kāi)啟一個(gè)新的goroutine執(zhí)行另外一段業(yè)務(wù),或者開(kāi)啟多個(gè)goroutine來(lái)并行執(zhí)行多個(gè)業(yè)務(wù)邏輯。所以我為hade框架增加了兩個(gè)方法goroutine.SafeGo 和 goroutine.SafeGoAndWait。

封裝

SafeGo

SafeGo 這個(gè)函數(shù),提供了一種goroutine安全的函數(shù)調(diào)用方式。主要適用于業(yè)務(wù)中需要進(jìn)行開(kāi)啟異步goroutine業(yè)務(wù)邏輯調(diào)用的場(chǎng)景。

// SafeGo 進(jìn)行安全的goroutine調(diào)用
// 第一個(gè)參數(shù)是context接口,如果還實(shí)現(xiàn)了Container接口,且綁定了日志服務(wù),則使用日志服務(wù)
// 第二個(gè)參數(shù)是匿名函數(shù)handler, 進(jìn)行最終的業(yè)務(wù)邏輯
// SafeGo 函數(shù)并不會(huì)返回error,panic都會(huì)進(jìn)入hade的日志服務(wù)
func SafeGo(ctx context.Context, handler func())

調(diào)用方式參照如下的單元測(cè)試用例:

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 這個(gè)函數(shù),提供安全的多并發(fā)調(diào)用方式。該函數(shù)等待所有函數(shù)都結(jié)束后才返回。

// SafeGoAndWait 進(jìn)行并發(fā)安全并行調(diào)用
// 第一個(gè)參數(shù)是context接口,如果還實(shí)現(xiàn)了Container接口,且綁定了日志服務(wù),則使用日志服務(wù)
// 第二個(gè)參數(shù)是匿名函數(shù)handlers數(shù)組, 進(jìn)行最終的業(yè)務(wù)邏輯
// 返回handlers中任何一個(gè)錯(cuò)誤(如果handlers中有業(yè)務(wù)邏輯返回錯(cuò)誤)
func SafeGoAndWait(ctx context.Context, handlers ...func() error) error

調(diào)用方式參照如下的單元測(cè)試用例:

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")
    }
}

實(shí)現(xiàn)說(shuō)明

實(shí)現(xiàn)方面,有幾個(gè)難點(diǎn)記錄下。

首先是接口設(shè)計(jì)方面

可以看到handler函數(shù)在兩個(gè)接口中是不一樣的。在SafeGo接口中,handler定義為func() 而在SafeGoAndWait中,定義為func() error

兩者的區(qū)別就在于SafeGo這個(gè)接口是沒(méi)有能力處理error的,因?yàn)樗黦o出去一個(gè)goroutine就直接進(jìn)行接下來(lái)的操作了。而SafeGoAndWait是必須等到所有的請(qǐng)求結(jié)束,所以它是有能力接收到error的。

所以SafeGo的handler沒(méi)有必要設(shè)置error返回值,而SafeGoAndWait是可以設(shè)置error的。

其次是日志兼容hade

如果出現(xiàn)了panic,如何將panic的日志打印出來(lái)。

整個(gè)框架我們并不希望有任何的全局變量,包括全局的Log,所以我這里做了一個(gè)兼容邏輯。

如果只是傳遞一個(gè)context,我們就使用官方的log包進(jìn)行打印。

如果傳遞的是一個(gè)即實(shí)現(xiàn)了context,又實(shí)現(xiàn)了container接口的結(jié)構(gòu),我們就從container中獲取日志服務(wù),來(lái)進(jìn)行日志打印。這樣框架的所有日志就能統(tǒng)一在日志打印里面。

				if logger != nil {
						logger.Error(ctx, "safe go handler panic", map[string]interface{}{
							"stack": string(buf),
							"err":   e,
						})
				} else {
						log.Printf("panic\t%v\t%s", e, buf)
				}

由于我們修改了gin的context,讓它支持了我們的container容器結(jié)構(gòu),所以我們可以直接將gin.Context傳遞進(jìn)來(lái)。具體使用起來(lái)就像這樣了:

// DemoGoroutine goroutine 的使用示例
func (api *DemoApi) DemoGoroutine(c *gin.Context) {
    logger := c.MustMakeLog()
    logger.Info(c, "request start", nil)

    // 初始化一個(gè)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 {
        // 查詢(xún)一條數(shù)據(jù)
        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 {
        // 查詢(xún)一條數(shù)據(jù)
        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的trace記錄

官方的panic其實(shí)打印的是所有g(shù)oroutine的堆棧信息。但是這里我們希望打印的是出panic的那個(gè)堆棧信息。所以我們會(huì)使用

debug.Stack()

來(lái)打印出問(wèn)題的goroutine的堆棧信息。

為了打印美觀,這里將換行符統(tǒng)一替換為\n 來(lái)進(jìn)行展示。

具體的實(shí)現(xiàn)代碼可以參考github地址:https://github.com/gohade/hade/blob/main/framework/util/goroutine/goroutine.go

說(shuō)明文檔:https://github.com/gohade/hade/blob/main/docs/guide/util.md

總結(jié)

為hade封裝了兩個(gè)SafeGo方法。特別是第二個(gè)SafeGoAndWait,在實(shí)際工作中確實(shí)是非常有用的。

到此這篇關(guān)于如何封裝安全的go的文章就介紹到這了,更多相關(guān)封裝安全的go內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang如何調(diào)用Python代碼詳解

    Golang如何調(diào)用Python代碼詳解

    這篇文章主要給大家介紹了關(guān)于Golang如何調(diào)用Python代碼的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2018-10-10
  • Go語(yǔ)言處理超大字符串型整數(shù)加減經(jīng)典面試詳解

    Go語(yǔ)言處理超大字符串型整數(shù)加減經(jīng)典面試詳解

    這篇文章主要為大家介紹了Go語(yǔ)言處理超大字符串型整數(shù)加減經(jīng)典面試示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-10-10
  • Go語(yǔ)言標(biāo)準(zhǔn)庫(kù)sync.Once使用場(chǎng)景及性能優(yōu)化詳解

    Go語(yǔ)言標(biāo)準(zhǔn)庫(kù)sync.Once使用場(chǎng)景及性能優(yōu)化詳解

    這篇文章主要為大家介紹了Go語(yǔ)言標(biāo)準(zhǔn)庫(kù)sync.Once使用場(chǎng)景及性能優(yōu)化詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • 淺析Go語(yǔ)言中的方法集合與選擇receiver類(lèi)型

    淺析Go語(yǔ)言中的方法集合與選擇receiver類(lèi)型

    這篇文章主要為大家詳細(xì)介紹了Go語(yǔ)言中的方法集合與選擇receiver類(lèi)型的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),對(duì)我們深入學(xué)習(xí)go語(yǔ)言有一定的幫助,需要的可以參考下
    2023-11-11
  • Golang中interface{}轉(zhuǎn)為數(shù)組的操作

    Golang中interface{}轉(zhuǎn)為數(shù)組的操作

    這篇文章主要介紹了Golang中interface{}轉(zhuǎn)為數(shù)組的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • Go語(yǔ)言學(xué)習(xí)之接口使用的示例詳解

    Go語(yǔ)言學(xué)習(xí)之接口使用的示例詳解

    Go語(yǔ)言并沒(méi)有類(lèi)的定義,接口可以說(shuō)Go語(yǔ)言最接近于類(lèi)的實(shí)現(xiàn)方式,但是更輕量。本文將通過(guò)一些簡(jiǎn)單的示例和大家介紹下Go語(yǔ)言中接口的使用,感興趣的可以學(xué)習(xí)一下
    2022-11-11
  • 淺析Golang中調(diào)度器的關(guān)鍵機(jī)制與性能

    淺析Golang中調(diào)度器的關(guān)鍵機(jī)制與性能

    Golang的調(diào)度器是其并發(fā)模型的核心組件,負(fù)責(zé)管理Goroutine的調(diào)度和執(zhí)行,本文將從理論和代碼層面分析Golang調(diào)度器的關(guān)鍵機(jī)制,感興趣的可以了解下
    2025-03-03
  • 在golang xorm中使用postgresql的json,array類(lèi)型的操作

    在golang xorm中使用postgresql的json,array類(lèi)型的操作

    這篇文章主要介紹了在golang xorm中使用postgresql的json,array類(lèi)型的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-04-04
  • Golang中Json的序列化和反序列化的使用

    Golang中Json的序列化和反序列化的使用

    本文主要介紹了Golang中Json的序列化和反序列化的使用,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-04-04
  • Golang try catch與錯(cuò)誤處理的實(shí)現(xiàn)

    Golang try catch與錯(cuò)誤處理的實(shí)現(xiàn)

    社區(qū)不少人在談?wù)?nbsp;golang 為毛不用try/catch模式,而采用苛刻的recovery、panic、defer組合,本文就來(lái)詳細(xì)的介紹一下,感興趣的可以了解一下
    2021-07-07

最新評(píng)論

宁夏| 葫芦岛市| 鞍山市| 故城县| 托克逊县| 保山市| 宁蒗| 兰西县| 西藏| 六枝特区| 巴林右旗| 徐州市| 岚皋县| 诸城市| 刚察县| 天柱县| 江安县| 大余县| 城口县| 陇西县| 砚山县| 遵义县| 西乌| 朔州市| 汶上县| 盘锦市| 平罗县| 荥经县| 隆子县| 洞口县| 平乐县| 辽阳县| 龙南县| 德江县| 海口市| 黔西县| 宁化县| 弥勒县| 武宣县| 砀山县| 英超|