golang基于errgroup实现并发调用

2023-11-02

串行调用

在用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。

好用的工具千千万,让我们一个个来掌握!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

golang基于errgroup实现并发调用 的相关文章

随机推荐

  • 台式计算机怎么看有没有开独显,怎么看是集显还是独显?台式电脑应该怎么选择...

    怎么看是集显还是独显 台式电脑应该怎么选择 在装机的时候我们经常会听到集显和独显 很多小白也并不知道集显和独显的区别 甚至都不知道怎么看自己的电脑是独显还是集显 下面电脑配置网就来为大家科普一下关于集显和独显的相关问题 显卡是电脑主机不可缺
  • sklearn进行归一化

    利用神经网络预测数据时 突然就被一个以前从来没想过的问题困扰了 训练集与测试集应该分别进行归一化还是合并为一个大的矩阵统一进行归一化 如果放在一起 测试集会参与到模型的训练当中 感觉不对 如果分开 怎么对测试集进行归一化呢 咨询老师 得到了
  • rk3368 Android9.0 升级固件后,一直在recovery界面

    升级固件后 一直在recovery界面 Platform RK3368 OS Android 9 0 Kernel 4 4 194 文章目录 升级固件后 一直在recovery界面 1 串口日志 2 解决方法 1 串口日志 E Failed
  • 欧拉操作系统和linux区别

    centos和Linux区别 linux与centos的区别与联系 1 centos是基于linux建立的操作系统 2 linux属于内核系统 只有终端命令界面 无图形界面 3 centos同时拥有终端命令界面和图形界面 4 linux和c
  • Warning:(23, 8) java: lombok.javac.apt.LombokProcessor could not be initialized.

    项目背景 开发工具idea springboot项目 在使用 Data时 启动项目后控制台报以下信息 Warning 23 8 java lombok javac apt LombokProcessor could not be initi
  • 闭关之 C++ 并发编程笔记(二):同步、内存模型和原子操作

    目录 第4章 并发操作的同步 4 1 等待事件或等待其他条件 4 1 1 凭借条件变量等待条件成立 4 1 2 利用条件变量构建线程安全的队列 4 2 使用future等待一次性事件发生 4 2 1 从后台任务返回值 4 2 2 关联fut
  • 定时删除某目录下几天前的文件

    系统每天生成日志 为了保证系统正常运行 需要不断清理系统空间 就把这种事情交给批处理文件来吧 这种方法简单实用 你只要通过任务计划和批处理文件就能实现 1 任务计划 进入 控制面板 任务计划 然后根据提示选择要运行的程序 运行时间即可 2
  • 基于STM32F103 实现按键状态机

    文章目录 开发板 开发环境 前言 按键消抖 按键硬件原理图 软件延时实现思路 实验目的 代码 按键状态 按键信息 按键相关定义 按键底层配置及状态获取 总结 开发板 正点原子STM32F103ZET6战舰 开发环境 stm32cubeMX
  • 单片机论文参考:1、基于单片机的电子琴

    摘要 随着社会的发展进步 音乐逐渐成为我们生活中很重要的一部分 有人曾说喜欢音乐的人不会向恶 我们都会抽空欣赏世界名曲 作为对精神的洗礼 本论文设计一个基于单片机的简易电子琴 电子琴是现代电子科技与音乐结合的产物 是一种新型的键盘乐器 它在
  • 遇到python调用selenium库使用chrome时候报错 selenium.common.exceptions.WebDriverException问题

    遇到selenium common exceptions WebDriverException问题 selenium common exceptions WebDriverException Message chromedriver exe
  • CTF中,命令中空格被过滤的解决方法

    转载于 作者 HyyMbb 链接 https blog csdn net a3320315 article details 99773192 来源 CSDN博客 1 linux cat flag txt cat IFS flag txt c
  • 【Matlab代码实现】电动过滤器:LPF和HPF、模拟调制:调幅和调频、WiFi、蓝牙和蜂窝网络的容量分析.....

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Matlab代码实现 1 概述 为了更好 更深入地了解工程通信原理 需要获
  • 矢量图形绘制Illustrator 2022简体中文

    Illustrator 2022简称AI 是简体中文的版本 在矢量图形创作的行业是首选的软件 作为一个行业标准的AI 用途越来越多 比如产品的包装 书籍的插图插画设计 大街上随处可见的广告牌设计 社交媒体等的插图 越来越多的人使用AI进行创
  • 学习笔记(118):R语言入门基础-最年长和最年轻富豪

    立即学习 https edu csdn net course play 24913 285865 utm source blogtoedu na omit 显示非NA的数据
  • latex常见编译错误和细节

    1 nu sqrt mu等一些常用希腊字母和字符不能放在 text 里面 text sqrt text mu 是错误用法 2 使用左右尖括号 left langle和 right rangle 持续更新
  • 性能测试工具 Locust 分布式进行性能测试

    目录 前言 Options 介绍 前言 在进行性能测试时 我们需要确保应用程序在分布式环境下仍能正常运行 使用 Locust 进行性能测试时 当一台单机不足以模拟所需的用户数量的时候 可以在多台机器上分布式的执行性能测试 首先 需要在主机下
  • 认识kubenetes的核心组件之一kubelet

    kubelet是在每个 Node 节点 包括master节点 上运行的主要 节点代理 默认监听10250端口 kubelet主要完成如下一些任务 接收并执行master发送来的指令 管理pod以及pod中的容器 管理节点和更新节点状态信息
  • Detr源码解读(mmdetection)

    Detr源码解读 mmdetection 1 原理简要介绍 整体流程 在给定一张输入图像后 1 特征向量提取 首先经过ResNet提取图像的最后一层特征图F 注意此处仅仅用了一层特征图 是因为后续计算复杂度原因 另外 由于仅用最后一层特征图
  • sql转nosql的第一件事:能不能实现复杂计算逻辑——MongoDB

    公司开始要把部分业务转到NoSQL上 并且决定开始使用mongodb作为技术栈 于是花了两天时间搭建服务 学习语法 以下给大家分享一些关系型数据库sqlserver在非关系型数据库mongodb的一些复杂语句的实现 首先 假设我们有一张表
  • golang基于errgroup实现并发调用

    文章目录 串行调用 基于sync WaitGroup实现简单的并发调用 基于errgroup Group实现并发调用 总结 串行调用 在用go编写web rpc服务器的时候 经常会出现需要对下游多 个 组 服务调用rpc 或者其他比较耗时的