日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
技巧分享:多Goroutine如何優(yōu)雅處理錯(cuò)誤?

本文轉(zhuǎn)載自微信公眾號「腦子進(jìn)煎魚了」,作者陳煎魚。轉(zhuǎn)載本文請聯(lián)系腦子進(jìn)煎魚了公眾號。

成都創(chuàng)新互聯(lián)主打移動網(wǎng)站、網(wǎng)站建設(shè)、成都做網(wǎng)站、網(wǎng)站改版、網(wǎng)絡(luò)推廣、網(wǎng)站維護(hù)、域名注冊、等互聯(lián)網(wǎng)信息服務(wù),為各行業(yè)提供服務(wù)。在技術(shù)實(shí)力的保障下,我們?yōu)榭蛻舫兄Z穩(wěn)定,放心的服務(wù),根據(jù)網(wǎng)站的內(nèi)容與功能再決定采用什么樣的設(shè)計(jì)。最后,要實(shí)現(xiàn)符合網(wǎng)站需求的內(nèi)容、功能與設(shè)計(jì),我們還會規(guī)劃穩(wěn)定安全的技術(shù)方案做保障。

大家好,我是煎魚。

在 Go 語言中,goroutine 的使用是非常頻繁的,因此在日常編碼的時(shí)候我們會遇到一個(gè)問題,那就是 goroutine 里面的錯(cuò)誤處理,怎么做比較好?

這是來自我讀者群的問題。作為一個(gè)寵粉煎魚,我默默記下了這個(gè)技術(shù)話題。今天煎魚就大家來看看多 goroutine 的錯(cuò)誤處理機(jī)制也有哪些!

一般來講,我們的業(yè)務(wù)代碼會是:

 
 
 
 
  1. func main() { 
  2.  var wg sync.WaitGroup 
  3.  wg.Add(2) 
  4.  go func() { 
  5.   log.Println("腦子進(jìn)煎魚了") 
  6.   wg.Done() 
  7.  }() 
  8.  go func() { 
  9.   log.Println("煎魚想報(bào)錯(cuò)...") 
  10.   wg.Done() 
  11.  }() 
  12.  
  13.  time.Sleep(time.Second) 

在上述代碼中,我們運(yùn)行了多個(gè) goroutine。但我想拋出 error 的錯(cuò)誤信息出來,似乎沒什么好辦法...

通過錯(cuò)誤日志記錄

為此,業(yè)務(wù)代碼中常見的第一種方法:通過把錯(cuò)誤記錄寫入日志文件中,再結(jié)合相關(guān)的 logtail 進(jìn)行采集和梳理。

但這又會引入新的問題,那就是調(diào)用錯(cuò)誤日志的方法寫的到處都是。代碼結(jié)構(gòu)也比較亂,不直觀。

最重要的是無法針對 error 做特定的邏輯處理和流轉(zhuǎn)。

利用 channel 傳輸

這時(shí)候大家可能會想到 Go 的經(jīng)典哲學(xué):不要通過共享內(nèi)存來通信,而是通過通信來實(shí)現(xiàn)內(nèi)存共享(Do not communicate by sharing memory; instead, share memory by communicating)。

第二種的方法:利用 channel 來傳輸多個(gè) goroutine 中的 errors:

 
 
 
 
  1. func main() { 
  2.  gerrors := make(chan error) 
  3.  wgDone := make(chan bool) 
  4.  
  5.  var wg sync.WaitGroup 
  6.  wg.Add(2) 
  7.  
  8.  go func() { 
  9.   wg.Done() 
  10.  }() 
  11.  go func() { 
  12.   err := returnError() 
  13.   if err != nil { 
  14.    gerrors <- err 
  15.   } 
  16.   wg.Done() 
  17.  }() 
  18.  
  19.  go func() { 
  20.   wg.Wait() 
  21.   close(wgDone) 
  22.  }() 
  23.  
  24.  select { 
  25.  case <-wgDone: 
  26.   break 
  27.  case err := <-gerrors: 
  28.   close(gerrors) 
  29.   fmt.Println(err) 
  30.  } 
  31.  
  32.  time.Sleep(time.Second) 
  33.  
  34. func returnError() error { 
  35.  return errors.New("煎魚報(bào)錯(cuò)了...") 

輸出結(jié)果:

 
 
 
 
  1. 煎魚報(bào)錯(cuò)了... 

雖然使用 channel 后已經(jīng)方便了不少。但自己編寫 channel 總是需要關(guān)心一些非業(yè)務(wù)向的邏輯。

借助 sync/errgroup

因此第三種方法,就是使用官方提供的 sync/errgroup 標(biāo)準(zhǔn)庫:

 
 
 
 
  1. type Group 
  2.     func WithContext(ctx context.Context) (*Group, context.Context) 
  3.     func (g *Group) Go(f func() error) 
  4.     func (g *Group) Wait() error 
  • Go:啟動一個(gè)協(xié)程,在新的 goroutine 中調(diào)用給定的函數(shù)。
  • Wait:等待協(xié)程結(jié)束,直到來自 Go 方法的所有函數(shù)調(diào)用都返回,然后返回其中的第一個(gè)非零錯(cuò)誤(如果有的話)。

結(jié)合其特性能夠非常便捷的針對多 goroutine 進(jìn)行錯(cuò)誤處理:

 
 
 
 
  1. func main() { 
  2.  g := new(errgroup.Group) 
  3.  var urls = []string{ 
  4.   "http://www.golang.org/", 
  5.   "https://golang2.eddycjy.com/", 
  6.   "https://eddycjy.com/", 
  7.  } 
  8.  for _, url := range urls { 
  9.   url := url 
  10.   g.Go(func() error { 
  11.    resp, err := http.Get(url) 
  12.    if err == nil { 
  13.     resp.Body.Close() 
  14.    } 
  15.    return err 
  16.   }) 
  17.  } 
  18.  if err := g.Wait(); err == nil { 
  19.   fmt.Println("Successfully fetched all URLs.") 
  20.  } else { 
  21.   fmt.Printf("Errors: %+v", err) 
  22.  } 

在上述代碼中,其表現(xiàn)的是爬蟲的案例。每一個(gè)計(jì)劃新起的 goroutine 都直接使用 Group.Go 方法。在等待和錯(cuò)誤上,直接調(diào)用 Group.Wait 方法就可以了。

使用標(biāo)準(zhǔn)庫 sync/errgroup 這種方法的好處就是不需要關(guān)注非業(yè)務(wù)邏輯的控制代碼,比較省心省力。

進(jìn)階使用

在真實(shí)的工程代碼中,我們還可以基于 sync/errgroup 實(shí)現(xiàn)一個(gè) http server 的啟動和關(guān)閉 ,以及 linux signal 信號的注冊和處理。以此保證能夠?qū)崿F(xiàn)一個(gè) http server 退出,全部注銷退出。

參考代碼(@via 毛老師)如下:

 
 
 
 
  1. func main() { 
  2.  g, ctx := errgroup.WithContext(context.Background()) 
  3.  svr := http.NewServer() 
  4.  // http server 
  5.  g.Go(func() error { 
  6.   fmt.Println("http") 
  7.   go func() { 
  8.    <-ctx.Done() 
  9.    fmt.Println("http ctx done") 
  10.    svr.Shutdown(context.TODO()) 
  11.   }() 
  12.   return svr.Start() 
  13.  }) 
  14.  
  15.  // signal 
  16.  g.Go(func() error { 
  17.   exitSignals := []os.Signal{os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT} // SIGTERM is POSIX specific 
  18.   sig := make(chan os.Signal, len(exitSignals)) 
  19.   signal.Notify(sig, exitSignals...) 
  20.   for { 
  21.    fmt.Println("signal") 
  22.    select { 
  23.    case <-ctx.Done(): 
  24.     fmt.Println("signal ctx done") 
  25.     return ctx.Err() 
  26.    case <-sig: 
  27.     // do something 
  28.     return nil 
  29.    } 
  30.   } 
  31.  }) 
  32.  
  33.  // inject error 
  34.  g.Go(func() error { 
  35.   fmt.Println("inject") 
  36.   time.Sleep(time.Second) 
  37.   fmt.Println("inject finish") 
  38.   return errors.New("inject error") 
  39.  }) 
  40.  
  41.  err := g.Wait() // first error return 
  42.  fmt.Println(err) 

內(nèi)部基礎(chǔ)框架有非常有這種代碼,有興趣的可以自己模仿著寫一遍,收貨會很多。

總結(jié)

在 Go 語言中 goroutine 是非常常用的一種方法,為此我們需要更了解 goroutine 配套的上下游(像是 context、error 處理等),應(yīng)該如何用什么來保證。

再在團(tuán)隊(duì)中形成一定的共識和規(guī)范,這么工程代碼閱讀起來就會比較的舒適,一些很坑的隱藏 BUG 也會少很多 :)


網(wǎng)站名稱:技巧分享:多Goroutine如何優(yōu)雅處理錯(cuò)誤?
網(wǎng)頁URL:http://m.5511xx.com/article/ccdijjd.html