Go 多重错误管理

概述

年度调查中,关于开发人员在使用 Go 时面临的最大挑战,Go 中的错误管理总是容易引起争论,并且是一个反复出现的话题。然而,当涉及到在并发环境中处理错误或为同一个 goroutine 合并多个错误时,Go 提供了很棒的包,使管理多个错误变得容易。让我们看看如何合并由单个 goroutine 生成的多个错误。

单个 goroutine, 多个错误

例如,当您处理具有重试策略的代码时,将多个错误合并为一个错误会非常有用。这是我们需要收集生成的错误的基本示例:
multiple errors

这个程序读取和解析一个 CSV 文本,并显示发现的错误。将错误分组以获得完整的报告可能更方便。要将错误合并为一个,我们可以在两个很棒的包中进行选择:

multiple errors

输出结果如下:
multiple errors

这里的实现类似,下面是输出:
multiple errors

错误通过分号连接起来,没有任何其他格式。

对于每个包的性能,下面是一个基准测试结论:

1
2
3
name                    time/op         alloc/op        allocs/op
HashiCorpMultiErrors-4 6.01µs ± 1% 6.78kB ± 0% 77.0 ± 0%
UberMultiErrors-4 9.26µs ± 1% 10.3kB ± 0% 126 ± 0%

Uber 的实现稍微慢一些,消耗更多的内存。但是,这个包的设计目的是将收集到的错误分组在一起,而不是每次都附加它们。当对错误进行分组时,结果很接近,但是代码不够优雅,因为它需要额外的步骤。以下是最新的测试结果:

1
2
3
name                    time/op         alloc/op        allocs/op
HashiCorpMultiErrors-4 6.01µs ± 1% 6.78kB ± 0% 77.0 ± 0%
UberMultiErrors-4 6.02µs ± 1% 7.06kB ± 0% 77.0 ± 0%

这两个包都利用了 Go error 接口,并在其自定义实现中实现了 Error() string 函数。

单个错误, 多个 goroutines

当处理多个 goroutines 来执行一个任务时,有必要正确管理结果和将错误聚合,以确保程序的正确性。

让我们从一个使用多个 goroutines 执行一系列操作的程序开始; 每个操作持续一秒钟:
multiple errors

为了说明错误传播,第三个 goroutine 的第一个操作将失败。事情是这样的:
multiple errors

正如预期的那样,这个程序大约需要 3 秒,因为大多数 goroutines 需要经历三个动作,每个动作需要 1 秒:

1
go run .  0.30s user 0.19s system 14% cpu 3.274 total

但是,我们可能希望使 goroutines 相互依赖,并在其中一个失败时取消它们。避免不必要工作的解决方案是添加上下文,一旦 goroutine 失败,它就会取消它:
multiple errors

这正是 errgroup 所提供的;处理一组 goroutines 时的错误和上下文传播。下面是使用包 errgroup 的新代码:
multiple errors

程序现在运行得更快,因为它通过错误传播取消的上下文:

1
go run .  0.30s user 0.19s system 38% cpu 1.269 total

该包提供的另一个好处是,我们不需要再担心等待组添加和标记 goroutines 完成。包为我们管理这些,我们只需要说我们准备好等待过程的结束。

微信订阅号

译自:https://medium.com/a-journey-with-go/go-multiple-errors-management-a67477628cf1

-------------本文结束感谢您的阅读-------------