欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

golang中channel+error來做異步錯(cuò)誤處理有多香

 更新時(shí)間:2023年01月19日 10:22:49   作者:文大俠666  
官方推薦golang中錯(cuò)誤處理當(dāng)做值處理, 既然是值那就可以在channel中傳輸,這篇文章主要介紹了golang 錯(cuò)誤處理channel+error真的香,需要的朋友可以參考下

官方推薦golang中錯(cuò)誤處理當(dāng)做值處理, 既然是值那就可以在channel中傳輸,本文帶你看看golang中channel+error來做異步錯(cuò)誤處理有多香,看完本文還會(huì)覺得golang的錯(cuò)誤處理相比java try catch一點(diǎn)優(yōu)勢(shì)都沒有嗎?

場(chǎng)景

如下,一次任務(wù)起多個(gè)協(xié)程異步處理任務(wù),比如同時(shí)做服務(wù)/redis/mysql/kafka初始化,當(dāng)某一個(gè)協(xié)程出現(xiàn)錯(cuò)誤(初始化失敗)時(shí),程序是停止還是繼續(xù)呢?如何記錄錯(cuò)誤?如何控制優(yōu)雅的退出全部工作協(xié)程呢?

image.png

為了解決類似的問題,常見如下三種解決方案:

1.中斷退出并記錄日志

如下,最簡單粗暴的方式就是,一旦協(xié)程中發(fā)生錯(cuò)誤,記錄日志立即退出,外層如果想取到錯(cuò)誤可以通過共享全局變量,單個(gè)協(xié)程無法控制所有協(xié)程動(dòng)作。

// 出錯(cuò)中斷協(xié)程,打印日志,退出

func TestSimpleExit(t *testing.T) {
	wg := sync.WaitGroup{}

	wg.Add(1)
	go func() {
		defer wg.Done()

		//do something
		if err := doSomething(); err != nil {
			t.Logf("Error when call doSomething:%v\n", err)
			return
		}
	}()

	wg.Wait()
}

2.監(jiān)控error,可選記錄日志或退出

前面講過,既然error是值,那就可以在channel中傳輸,可以單獨(dú)開一個(gè)channel,所有協(xié)程錯(cuò)誤都發(fā)送到這個(gè)通道。

	// 數(shù)據(jù)處理流程
	dataFunc := func(ctx context.Context, dataChan chan int, errChan chan error) {
		defer wg.Done()

		for {
			select {
			case v, ok := <-dataChan:
				if !ok {
					log.Println("Receive data channel close msg!")
					return
				}

				if err := doSomething2(v); err != nil {
					errChan <- err
					continue
				}

				// do ...

			case <-ctx.Done():
				log.Println("Receive exit msg!")
				return
			}
		}
	}

	wg.Add(1)
	go dataFunc(ctx, dataChan, errChan)

	wg.Add(1)
	go dataFunc(ctx, dataChan, errChan)

監(jiān)控錯(cuò)誤error通道,統(tǒng)一記錄和退出,一旦檢測(cè)到錯(cuò)誤可以通過ctx通知所有協(xié)程退出,這里可以靈活控制監(jiān)控到錯(cuò)誤時(shí)的錯(cuò)誤處理策略(是否記錄日志/是否退出等),error通道可以同步或異步處理。
整體流程如下

image.png

異步監(jiān)控error

	// 錯(cuò)誤處理流程,error處理通道異步等待
	wg.Add(1)
	go func(errChan chan error) {
		defer wg.Done()

		for {
			select {
			case v, ok := <-errChan:
				if !ok {
					log.Println("Receice err channel close msg!")
					return
				}

				// 收到錯(cuò)誤時(shí),可選擇記錄日志或退出
				if v != nil {
					t.Logf("Error when call doSomething:%v\n", v)
					cancel() // 通知全部退出
					return
				}

			case <-ctx.Done():
				log.Println("Receive exit msg!")
				return
			}
		}

	}(errChan)

	dataChan <- 1
	wg.Wait()

同步監(jiān)控error

	// 錯(cuò)誤處理流程,error處理通道同步等待
	for {
		select {
		case v, ok := <-errChan:
			if !ok {
				log.Println("Receice err channel close msg!")
				goto EXIT
			}

			// 收到錯(cuò)誤時(shí),可選擇記錄日志或退出
			if v != nil {
				t.Logf("Error when call doSomething:%v\n", v)
				cancel()
				goto EXIT
			}

		case <-ctx.Done():
			log.Println("Receive exit msg!")
			goto EXIT
		}
	}

EXIT:
	wg.Wait()

3.官方庫errgroup

考慮到error輸出到通道后統(tǒng)一處理是golang常用手段,官方也針對(duì)封裝了一個(gè)error處理包,errgroup顧名思義,多個(gè)協(xié)程的error被當(dāng)做一個(gè)組,一旦某個(gè)協(xié)程出錯(cuò)所有協(xié)程都退出,只輸出第一個(gè)error

func TestSimpleChannel5(t *testing.T) {
	eg, ctx := errgroup.WithContext(context.Background())

	dataChan := make(chan int)
	defer close(dataChan)

	// 數(shù)據(jù)處理流程
	dataFunc := func() error {
		for {
			select {
			case v, ok := <-dataChan:
				if !ok {
					log.Println("Receive data channel close msg!")
					return nil
				}

				if err := doSomething2(v); err != nil {
					return err
				}

				// do ...

			// 增加ctx通知完成
			case <-ctx.Done():
				log.Println("Receive exit msg!")
				return nil
			}
		}
	}

	eg.Go(dataFunc)
	eg.Go(dataFunc)
	eg.Go(dataFunc)

	dataChan <- 1

	// 錯(cuò)誤處理流程,任何一個(gè)協(xié)程出現(xiàn)error,則會(huì)調(diào)用ctx對(duì)應(yīng)cancel函數(shù),所有相關(guān)協(xié)程都會(huì)退出
	if err := eg.Wait(); err != nil {
		fmt.Printf("Something is wrong->%v\n", err)
	}
}

類似上一小節(jié),可以看到errgroup就是結(jié)合waitgroup cancel和channel通道封裝的。

4.監(jiān)控error,全部日志合并后輸出

同樣是上述場(chǎng)景,有時(shí)候我們的需求是返回所有的錯(cuò)誤(不是第一個(gè)錯(cuò)誤)。

  • 比如在服務(wù)啟動(dòng)時(shí),對(duì) redis、kafka、mysql 等各種資源初始化場(chǎng)景,可以把所有相關(guān)資源初始化的錯(cuò)誤都返回,展示給用戶統(tǒng)一排查。
  • 另一種場(chǎng)景就是在 web 請(qǐng)求中,校驗(yàn)請(qǐng)求參數(shù)時(shí),返回所有參數(shù)的校驗(yàn)錯(cuò)誤給客戶端的場(chǎng)景。

這種需求,一般考慮使用多錯(cuò)誤管理(hashicorp/go-multierror庫),如下一個(gè)簡答同步任務(wù)演示多錯(cuò)誤管理,所有返回的錯(cuò)誤可以通過Append歸并成一個(gè)錯(cuò)誤,實(shí)際上是通過error wrap的方式合并起來的,因此也可以使用Is/As判斷嵌套error。

// 多路協(xié)程error合并,用于多路check場(chǎng)景
func TestSimpleChannel3(t *testing.T) {

	// 同步執(zhí)行多個(gè)任務(wù),返回error合并
	var err = func() error {
		var result error

		if err := doSomething(); err != nil {
			result = multierror.Append(result, err)
		}

		if err := doSomething2(nil); err != nil {
			result = multierror.Append(result, err)
		}

		return result
	}()

	// 打印輸出
	if err != nil {
		fmt.Printf("%v\n", err)
	}

	// 獲取錯(cuò)誤列表
	if err != nil {
		if merr, ok := err.(*multierror.Error); ok {
			fmt.Printf("%v\n", merr.Errors)
		}
	}

	// 判斷是否為某種類型
	if err != nil && errors.Is(err, Error1) {
		fmt.Println("Errors contain error 1")
	}

	// 判斷是否其中一個(gè)error能夠轉(zhuǎn)換成指定error
	var e MyError
	if err != nil && errors.As(err, &e) {
		fmt.Println("One Error can be convert to nyerror")
	}
}

那么,在起多個(gè)異步任務(wù)時(shí),就可以如下處理,返回的多個(gè)error通過channel消費(fèi)合并展示。

func TestSimpleChannel4(t *testing.T) {
	wg := sync.WaitGroup{}

	taskNum := 10
	errChan := make(chan error, taskNum)

	// 異步執(zhí)行多個(gè)任務(wù)
	step := func(stepNum int, errChan chan error) {
		defer wg.Done()
		errChan <- fmt.Errorf("step %d error", stepNum)
	}

	for i := 0; i < taskNum; i++ {
		wg.Add(1)
		go step(i, errChan)
	}

	// 等待任務(wù)完成
	go func() {
		wg.Wait()
		close(errChan)
	}()

	// err通道阻塞等待,可能的所有錯(cuò)誤合并
	var result *multierror.Error
	for err := range errChan {
		result = multierror.Append(result, err)
	}

	// 出現(xiàn)一個(gè)錯(cuò)誤時(shí),選擇記錄日志或退出
	if len(result.Errors) != 0 {
		log.Println(result.Errors)
	}
}

參考文獻(xiàn)

演示代碼 https://gitee.com/wenzhou1219/go-in-prod/tree/master/error_group

errgroup源碼解析 https://zhuanlan.zhihu.com/p/416054707
errgroup使用參考 https://zhuanlan.zhihu.com/p/338999914

go-multierror使用參考 https://zhuanlan.zhihu.com/p/581030231

到此這篇關(guān)于golang 錯(cuò)誤處理channel+error真的香的文章就介紹到這了,更多相關(guān)golang 錯(cuò)誤處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • IdeaGo啟動(dòng)報(bào)錯(cuò)Failed to create JVM的問題解析

    IdeaGo啟動(dòng)報(bào)錯(cuò)Failed to create JVM的問題解析

    這篇文章主要介紹了IdeaGo啟動(dòng)報(bào)錯(cuò)Failed to create JVM的問題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-11-11
  • Golang設(shè)計(jì)模式之適配器模式介紹和代碼示例

    Golang設(shè)計(jì)模式之適配器模式介紹和代碼示例

    適配器是一種結(jié)構(gòu)型設(shè)計(jì)模式, 它能使不兼容的對(duì)象能夠相互合作,可擔(dān)任兩個(gè)對(duì)象間的封裝器, 它會(huì)接收對(duì)于一個(gè)對(duì)象的調(diào)用, 并將其轉(zhuǎn)換為另一個(gè)對(duì)象可識(shí)別的格式和接口,本文將通過代碼示例詳細(xì)給大家介紹Golang的適配器模式
    2023-06-06
  • 淺談Go數(shù)組比切片好在哪

    淺談Go數(shù)組比切片好在哪

    Go1.17 會(huì)正式支持切片轉(zhuǎn)換到數(shù)據(jù),不再需要用以前那種騷辦法了,本文就談?wù)凣o數(shù)組比切片好在哪,感興趣的可以了解一下
    2021-09-09
  • golang如何獲得一個(gè)變量的類型

    golang如何獲得一個(gè)變量的類型

    這篇文章主要介紹了golang獲得一個(gè)變量類型的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05
  • 解決Golang并發(fā)工具Singleflight的問題

    解決Golang并發(fā)工具Singleflight的問題

    前段時(shí)間在一個(gè)項(xiàng)目里使用到了分布式鎖進(jìn)行共享資源的訪問限制,后來了解到Golang里還能夠使用singleflight對(duì)共享資源的訪問做限制,于是利用空余時(shí)間了解,將知識(shí)沉淀下來,并做分享
    2022-05-05
  • Go語言操作etcd的示例詳解

    Go語言操作etcd的示例詳解

    etcd是使用Go語言開發(fā)的一個(gè)開源的、高可用的分布式key—value存儲(chǔ)系統(tǒng),可以用于配置共享和服務(wù)的注冊(cè)和發(fā)現(xiàn),下面我們就來看看Go語言是如何操作etcd的吧
    2024-03-03
  • 安裝GoLang環(huán)境和開發(fā)工具的圖文教程

    安裝GoLang環(huán)境和開發(fā)工具的圖文教程

    Go是一門由Google開發(fā)的編程語言,GoLand的安裝非常簡單,本文主要介紹了安裝GoLang環(huán)境和開發(fā)工具的圖文教程,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-09-09
  • GoFrame框架Scan類型轉(zhuǎn)換實(shí)例

    GoFrame框架Scan類型轉(zhuǎn)換實(shí)例

    這篇文章主要為大家介紹了GoFrame框架Scan類型轉(zhuǎn)換的實(shí)例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • Go語言使用templ實(shí)現(xiàn)編寫HTML用戶界面

    Go語言使用templ實(shí)現(xiàn)編寫HTML用戶界面

    templ是一個(gè)在 Go 中編寫 HTML 用戶界面的語言,使用 templ,我們可以創(chuàng)建可呈現(xiàn) HTML 片段的組件,下面就跟隨小編一起了解一下具體的實(shí)現(xiàn)方法吧
    2023-12-12
  • Go map排序的實(shí)現(xiàn)示例

    Go map排序的實(shí)現(xiàn)示例

    map默認(rèn)是無序的,不管是按照key還是按照value默認(rèn)都不排序,本文主要介紹了Go map排序的實(shí)現(xiàn)示例,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-12-12

最新評(píng)論