網站首頁 編程語言 正文
串行調用
在用go編寫web/rpc服務器的時候,經常會出現需要對下游多 個/組 服務調用rpc(或者其他比較耗時的操作)的情況。
按照自然的寫法,比如對下游有ABC三個調用,串行順著寫,就總共要花費TimeA+TimeB+TimeC的時間:
func Handler(ctx context.Context) { var a, b, c respType a = A(ctx) b = B(ctx) c = C(ctx) }
基于sync.WaitGroup實現簡單的并發調用
但經常地,幾個rpc相互之間沒有依賴關系的情況,這時,我們稍加思考就會想到使用并發的方式,同時發出請求,阻塞等到所有請求返回,這樣,總體耗時就變成了Max(TimeA, TimeB, TimeC),我們可以通過常用的sync.WaitGroup輕松實現這事:
func Handler(ctx context.Context) { var a, b, c respType wg := sync.WaitGroup{} wg.Add(3) go func() { defer wg.Done() a = A(ctx) }() go func() { defer wg.Done() b = B(ctx) }() go func() { defer wg.Done() c = C(ctx) }() wg.Wait() }
但是現實事件是不完美的,尤其是在加入了網絡這一因素后,我們經常會需要處理調用失敗的情況,很多情況下,并發的幾個操作只要任一失敗,整個處理就算失敗了,但是由于WaitGroup要等所有調用都done才能返回,因此調用時間是由耗時最長的那個(不一定是失敗的)決定的,如果不是失敗的那個,其實就產生了資源浪費,如下圖,B最先失敗了,此時邏輯上已經可以返回,但是實際卻等到了最長的調用-A返回了整個函數才返回:
func Handler(ctx context.Context) { var a, b, c respType var errA, errB, errC error wg := sync.WaitGroup{} wg.Add(3) go func() { defer wg.Done() a, errA = A(ctx) }() go func() { defer wg.Done() b, errB = B(ctx) }() go func() { defer wg.Done() c, errC = C(ctx) }() wg.Wait() if errA != nil { // ... } if errB != nil { // ... } if errC != nil { // ... } }
基于errgroup.Group實現并發調用
這對于追求極致的我們來說顯然是不能接受的,我們希望達到,如果有任意一個調用報錯,立刻讓所有調用返回的效果:
好在,我們有現成的工具可以用,通過引入"golang.org/x/sync/errgroup",可以輕松實現上面的目的。
為了使用errgroup,先使用WithContext方法創建一個Group
wg, groupCtx := errgroup.WithContext(ctx)
返回的第一個參數是*errgroup.Group,第二個則是在子調用中應該使用的context。
然后,使用Go方法調用所有的并發方法
wg.Go(func() error { var err error a, err = A(groupCtx) return err })
最后, 使用Wait方法等待并發結束,返回值是所有子調用中第一個非nil的error,全成功的話就是nil。
if err := wg.Wait(); err != nil { // ... }
因此整體,我們的代碼差不多就長這個樣子
func handler(ctx context.Context) { var a, b, c respType wg, groupCtx := errgroup.WithContext(ctx) wg.Go(func() error { var err error a, err = A(groupCtx) return err }) wg.Go(func() error { var err error b, err = B(groupCtx) return err }) wg.Go(func() error { var err error c, err = C(groupCtx) return err }) if err := wg.Wait(); err != nil { // ... 錯誤處理 } // 全部成功 }
errgroup內部通過封裝了waitGroup和sync.Once實現了這個語法糖。
使用時特別要注意的是,errgroup的提前取消調用rpc是通過cancel那個返回的context(即上面的groupCtx)實現的,因此在所有子調用中都要實現監聽groupCtx的Done事件。而在正常的rpc框架中都已經幫我們實現了這件事,因此我們只要保證傳進去的是groupCtx即可。
總結
errgroup幫我們封裝了并發調用下游時快速失敗的邏輯,我們能很方便地使用它進行業務代碼的編寫。使用的關鍵是一定要記得在子調用中傳遞WithContext中返回的Context。
原文鏈接:https://blog.csdn.net/lin_strong/article/details/126681571
相關推薦
- 2022-11-22 Redis?BloomFilter布隆過濾器原理與實現_Golang
- 2022-06-25 C++文件讀寫操作詳解_C 語言
- 2022-06-18 C語言?從根本上理解指針_C 語言
- 2022-03-22 Linux系統中.bash_profile文件詳解_Linux
- 2022-05-13 Python Anaconda安裝sasl,thrift,thrift-sasl 并連接PyHive
- 2022-05-08 C++中如何修改const變量你知道嗎_C 語言
- 2022-10-10 redis緩存延時雙刪的原因分析_Redis
- 2022-05-23 C#中using語句的用法_C#教程
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支