diff --git a/2023/w03-Go-Application-Security-and-Appsec-Automation-Made-Easy.md b/2023/w03-Go-Application-Security-and-Appsec-Automation-Made-Easy.md index 00ceda7..450553b 100644 --- a/2023/w03-Go-Application-Security-and-Appsec-Automation-Made-Easy.md +++ b/2023/w03-Go-Application-Security-and-Appsec-Automation-Made-Easy.md @@ -1,18 +1,18 @@ -# 自动化检查Go代码中的漏洞 +# 自动化检查 Go 代码中的漏洞 - [原文链接](https://awkwardferny.medium.com/go-application-security-and-appsec-automation-made-easy-36bd2f3d520b) - 原文作者:Fernando Diaz - [本文永久链接](https://github.com/gocn/translator/blob/master/static/images/2023/w03-Go-Application-Security-and-Appsec-Automation-Made-Easy/w03-Go-Application-Security-and-Appsec-Automation-Made-Easy.md) -- 译者:[司镜233](https://github.com/sijing233) +- 译者:[司镜 233](https://github.com/sijing233) - 校对:[刘思家](https://github.com/lsj1342) -在云应用程序领域,Go是最流行的语言之一了,Kubernetes大部分的内容都是Go构建的。 +在云应用程序领域,Go 是最流行的语言之一了,Kubernetes 大部分的内容都是 Go 构建的。 -但即便如此,根据[Nautilus2022云原生威胁报告](https://info.aquasec.com/cloud-native-threat-report-2022)表明:具有恶意的个人或组织,也增加了更多目标和方式,包括CI/CD的环境、容易收到攻击的Kubernets部署和应用程序。 +但即便如此,根据[Nautilus2022 云原生威胁报告](https://info.aquasec.com/cloud-native-threat-report-2022)表明:具有恶意的个人或组织,也增加了更多目标和方式,包括 CI/CD 的环境、容易收到攻击的 Kubernets 部署和应用程序。 -随着时间的推移,针对Kubernets的攻击次数、攻击手段不断增加。根据[AquaSec](https://www.aquasec.com/)的观察显示:以Kubernets为目标的恶意攻击数量,从2020年的9%到2021年的19%,增加了10%。这也说明,保护我们Golang应用程序的安全,越来越重要。 +随着时间的推移,针对 Kubernets 的攻击次数、攻击手段不断增加。根据[AquaSec](https://www.aquasec.com/)的观察显示:以 Kubernets 为目标的恶意攻击数量,从 2020 年的 9%到 2021 年的 19%,增加了 10%。这也说明,保护我们 Golang 应用程序的安全,越来越重要。 -在这篇文章中,我将展示用扫描应用程序源代码漏洞的各种方法,以及如何将安全扫描器集成到GitLab等CI/CD平台中。我将提供一份我创建的,不安全的微服务的真实示例。 +在这篇文章中,我将展示用扫描应用程序源代码漏洞的各种方法,以及如何将安全扫描器集成到 GitLab 等 CI/CD 平台中。我将提供一份我创建的,不安全的微服务的真实示例。 @@ -20,13 +20,13 @@ ## 先决条件 -- 基本了解Go编程语言 -- Git基础知识 +- 基本了解 Go 编程语言 +- Git 基础知识 - 基本了解应用程序的安全性 -- Gitlab账户(免费) +- Gitlab 账户(免费) - Go 1.19+ -``` +```plain $ go versiongo version go1.19.1 darwin/amd64 ``` @@ -36,11 +36,11 @@ $ go versiongo version go1.19.1 darwin/amd64 -在推送代码之前,或是将代码部署到生产级环境之前,运行安全扫描器,检测并修复漏洞。我将介绍如何用Go,使用各种不同的安全扫描器:[GoSec](](https://github.com/securego/gosec))、[GoVulnCheck](https://go.dev/blog/vuln)、[Fuzz](](https://go.dev/security/fuzz/)) +在推送代码之前,或是将代码部署到生产级环境之前,运行安全扫描器,检测并修复漏洞。我将介绍如何用 Go,使用各种不同的安全扫描器:[GoSec](](https://github.com/securego/gosec))、[GoVulnCheck](https://go.dev/blog/vuln)、[Fuzz](](https://go.dev/security/fuzz/)) -首先,我们可以开始设置一个适当的GOPATH,添加GOPATH/bin到我们的PATH,并且git clone [不安全](https://gitlab.com/awkwardferny/insecure-microservice)的微服务代码,可以在[此处](https://go.dev/doc/tutorial/compile-install)找到有关路径的详细信息。 +首先,我们可以开始设置一个适当的 GOPATH,添加 GOPATH/bin 到我们的 PATH,并且 git clone [不安全](https://gitlab.com/awkwardferny/insecure-microservice)的微服务代码,可以在[此处](https://go.dev/doc/tutorial/compile-install)找到有关路径的详细信息。 ```shell # 设置合适的 GOPATH @@ -62,13 +62,13 @@ $ git clone git@gitlab.com:awkwardferny/insecure-microservice.git src/gitlab.com $ cd src/gitlab.com/awkwardferny/insecure-microservice ``` -现在,我们已经正确设置了路径,并且已经clone了应用程序,我们可以开始运行我们的安全扫描器了。 +现在,我们已经正确设置了路径,并且已经 clone 了应用程序,我们可以开始运行我们的安全扫描器了。 # GoSec(源代码分析) -我们将介绍的第一个安全扫描器是[GoSec](https://github.com/securego/gosec)。它是一种流行的Go安全扫描器,可以扫描应用程序的源代码和依赖项,检查到漏洞。它通过将您的源代码与一组规则进行模式匹配来工作。 +我们将介绍的第一个安全扫描器是[GoSec](https://github.com/securego/gosec)。它是一种流行的 Go 安全扫描器,可以扫描应用程序的源代码和依赖项,检查到漏洞。它通过将您的源代码与一组规则进行模式匹配来工作。 -如果Go模块打开(e.g.`GO111MODULE=on`) ,或者明确下载依赖项(`go get -d`),GoSec还可以自动扫描您的应用程序依赖项,来检查漏洞。现在,让我们在不安全的微服务上运行GoSec: +如果 Go 模块打开(e.g.`GO111MODULE=on`) ,或者明确下载依赖项(`go get -d`),GoSec 还可以自动扫描您的应用程序依赖项,来检查漏洞。现在,让我们在不安全的微服务上运行 GoSec: ```shell # 安装GoSec @@ -93,15 +93,15 @@ G104 (CWE-703): Errors unhandled. (Confidence: HIGH, Severity: LOW) 这些漏洞表明我们的应用程序,有很多未捕获的异常:没有设置超时、使用了弱随机生成数。扫描返回规则出发、常见弱点枚举(CWE)、置信度、严重性和受影响的代码行。 -在典型的开发人员工作流中,发现漏洞后,开发人员可以检查CWE,获取改进提示,对受影响的代码进行代码更改,然后重新运行扫描程序,以检查解决方案。应该运行回归测试,以确保我们的应用程序逻辑仍然健全。 +在典型的开发人员工作流中,发现漏洞后,开发人员可以检查 CWE,获取改进提示,对受影响的代码进行代码更改,然后重新运行扫描程序,以检查解决方案。应该运行回归测试,以确保我们的应用程序逻辑仍然健全。 # Govulncheck(源代码分析) -接下来是Govulncheck!Govulncheck是一个针对源代码,和应用程序依赖项的安全扫描器。Go安全团队正在积极开发它,并且在几个方面,与GoSec不同: +接下来是 Govulncheck!Govulncheck 是一个针对源代码,和应用程序依赖项的安全扫描器。Go 安全团队正在积极开发它,并且在几个方面,与 GoSec 不同: -首先,它由[Go漏洞数据库]((https://vuln.go.dev/))支持。 +首先,它由[Go 漏洞数据库]((https://vuln.go.dev/))支持。 其次,它只显示您的代码,实际调用的漏洞。这会减少“噪声”,并且让您知道哪些漏洞实际影响了您的应用程序。 @@ -113,7 +113,7 @@ G104 (CWE-703): Errors unhandled. (Confidence: HIGH, Severity: LOW) 现在,让我们试一试! -``` +```plain # 安装 govulncheck $ go install golang.org/x/vuln/cmd/govulncheck@latest @@ -139,7 +139,7 @@ More info: https://pkg.go.dev/vuln/GO-2020-0016 -您可以看到扫描器,向我们提供了漏洞规则参考、说明、受影响的代码行、漏洞依赖项、解决方案以及附加信息的链接。因为我在我的应用程序中使用***github.com/ulikunitz/xz@v0.5.7作为*依赖*项并调用***xz.Reader.Read,所以我的应用程序容易受到[DDoS](https://www.cloudflare.com/learning/ddos/what-is-a-ddos-attack/)攻击。这个漏洞是由Go 漏洞数据库中的[GO-2020-016规则检测到的。](https://github.com/golang/vulndb/blob/master/data/reports/GO-2020-0016.yaml) +您可以看到扫描器,向我们提供了漏洞规则参考、说明、受影响的代码行、漏洞依赖项、解决方案以及附加信息的链接。因为我在我的应用程序中使用***github.com/ulikunitz/xz@v0.5.7 作为*依赖*项并调用***xz.Reader.Read,所以我的应用程序容易受到[DDoS](https://www.cloudflare.com/learning/ddos/what-is-a-ddos-attack/)攻击。这个漏洞是由 Go 漏洞数据库中的[GO-2020-016 规则检测到的。](https://github.com/golang/vulndb/blob/master/data/reports/GO-2020-0016.yaml) 在典型的工作流程中,开发人员会更新依赖版本,然后重新运行扫描器以及*单元*和*功能*测试,以确保应用程序不会中断。 @@ -179,13 +179,13 @@ func add(a string, b string) (c int, e error) { -我们可以看到 `FuzzAdd() `的编写类似于单元测试。我们通过添加`f.Fuzz(func(t \*testing.T, a string, b string)`来启用模糊测试,它调用`add( a string, b string )`函数,为变量`a`和`b`提供随机数据。然后,将它和预期值结果,进行比较。 +我们可以看到 `FuzzAdd()`的编写类似于单元测试。我们通过添加`f.Fuzz(func(t \*testing.T, a string, b string)`来启用模糊测试,它调用`add( a string, b string )`函数,为变量`a`和`b`提供随机数据。然后,将它和预期值结果,进行比较。 -`add()`函数,简单地将2 个字符串转换为整数,然后将它们相加并返回结果。 +`add()`函数,简单地将 2 个字符串转换为整数,然后将它们相加并返回结果。 `FuzzAdd ()`测试可以使用[种子数据](https://go.dev/security/fuzz/#glos-seed-corpus)`f.Add("1", “2”),`正确运行,但是当存在格式错误或随机数据时会发生什么情况?让我们运行模糊测试并找出: -``` +```plain # 运行 fuzz 测试 $ go test ./internal/logic -fuzz FuzzAdd ``` @@ -203,11 +203,11 @@ $ go test ./internal/logic -fuzz FuzzAdd FAIL ``` -导致这个错误,是因为传递了字母A,而不是可以转换为整数的字符串。Fuzz还在testdata目录下,生成了一个种子语料库,可以用来再次测试这个特定的故障。 +导致这个错误,是因为传递了字母 A,而不是可以转换为整数的字符串。Fuzz 还在 testdata 目录下,生成了一个种子语料库,可以用来再次测试这个特定的故障。 -解决这个问题的一个方式,是在add()函数中,简单地返回err,而不是nil。并期望在FuzzAdd()中,返回非整数可转换字符串的错误。 +解决这个问题的一个方式,是在 add()函数中,简单地返回 err,而不是 nil。并期望在 FuzzAdd()中,返回非整数可转换字符串的错误。 -我们还可以考虑,仅将整数值设置为0,并记录错误。如下所示,这仅仅取决于,我们要实现的目标。 +我们还可以考虑,仅将整数值设置为 0,并记录错误。如下所示,这仅仅取决于,我们要实现的目标。 ```go func add(a string, b string) (c int, e error) { @@ -225,25 +225,25 @@ func add(a string, b string) (c int, e error) { } ``` -有关模糊测试的更多高级用法,请查看 [Go模糊测试教程](https://go.dev/doc/tutorial/fuzz). +有关模糊测试的更多高级用法,请查看 [Go 模糊测试教程](https://go.dev/doc/tutorial/fuzz). -# 使用GitLab实现自动化扫描 +# 使用 GitLab 实现自动化扫描 -如果可以自动运行安全扫描器来搜索Go应用程序中的漏洞,这样我们就可以在每次推送代码时,在功能分支上运行扫描器。 +如果可以自动运行安全扫描器来搜索 Go 应用程序中的漏洞,这样我们就可以在每次推送代码时,在功能分支上运行扫描器。 这会在代码投入生产之前,解决安全问题,并且不必在每次更改代码时,都手动运行扫描程序,从而节省了我们的时间。 -这些扫描器,可以通过在GitLab中,创建CI/CD管道来实现自动化。管道可以在每次将代码推送到分支时,自动运行这些扫描。我们将查看[GitLab CI yaml](https://gitlab.com/awkwardferny/insecure-microservice/-/blob/master/.gitlab-ci.yml),它在下面生成了一个CI/CD管道。 +这些扫描器,可以通过在 GitLab 中,创建 CI/CD 管道来实现自动化。管道可以在每次将代码推送到分支时,自动运行这些扫描。我们将查看[GitLab CI yaml](https://gitlab.com/awkwardferny/insecure-microservice/-/blob/master/.gitlab-ci.yml),它在下面生成了一个 CI/CD 管道。 首先,我们看到的是,将按照提供的顺序,在管道中运行的阶段: -``` +```plain stages: - build - test @@ -253,7 +253,7 @@ The **build** stage makes sure the application even builds before proceeding. If 构建阶段,确保是在构建应用程序之前。如果您已经容器化了您的应用程序,那么在这个阶段,您最好也测试一下,是否可以构建容器镜像: -``` +```plain build: image: golang:alpine stage: build @@ -268,7 +268,7 @@ build: -``` +```plain unit: image: golang:alpine stage: test @@ -322,7 +322,7 @@ fuzz: -这就是将单元测试、模糊测试和安全扫描器,集成到CI/CD管道中的方法。这让生活变的更轻松,并且无需每次都手动运行所有内容。 +这就是将单元测试、模糊测试和安全扫描器,集成到 CI/CD 管道中的方法。这让生活变的更轻松,并且无需每次都手动运行所有内容。 # 代码审查和安全编码实践 @@ -362,5 +362,5 @@ fuzz: 仪表板类型的视图将是理想的,这样您就可以有效地分类和管理漏洞,引导您找到应该首先解决的问题。 -好了,自动化检查Go代码中的漏洞!感谢阅读,希望您喜欢这篇文章。 +好了,自动化检查 Go 代码中的漏洞!感谢阅读,希望您喜欢这篇文章。 diff --git a/2023/w04_Implementing_clean_architecture_in_Go.md b/2023/w04_Implementing_clean_architecture_in_Go.md index d9e4126..c550bda 100644 --- a/2023/w04_Implementing_clean_architecture_in_Go.md +++ b/2023/w04_Implementing_clean_architecture_in_Go.md @@ -6,13 +6,13 @@ - 译者:[zxmfke](https://github.com/zxmfke) - 校对: -已经有很多关于 [简洁架构](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)的文章了。它的主要价值在于能够维护无副作用的领域层,使我们能够不需要利用沉重的mock来测试核心业务逻辑。 +已经有很多关于 [简洁架构](https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html)的文章了。它的主要价值在于能够维护无副作用的领域层,使我们能够不需要利用沉重的 mock 来测试核心业务逻辑。 通过写一个无需依赖的核心领域逻辑,以及外部适配器(成为它的数据库存储或者 API 层)来实现的。这些适配器依赖于领域,而不是领域依赖适配器。 在这篇文章,我们会看一下简洁架构是如何实现一个简单的 Go 项目。我们会提及一些额外的主题,例如容器化以及用 Swagger 实现 OpenAPI 规范。 -虽然我将在文章中高亮了感兴趣的点,但你可以在 [我的Github](https://github.com/Wkalmar/toggl-deck-management-api) 上看看整个项目。 +虽然我将在文章中高亮了感兴趣的点,但你可以在 [我的 Github](https://github.com/Wkalmar/toggl-deck-management-api) 上看看整个项目。 ## 项目需求 @@ -268,7 +268,7 @@ func main() { -一些读者可能对根据上述要求,创建牌组这个路由将参数作为URL请求的一部分感到困惑,可能会考虑让这个路由用 GET 请求而不是 POST。 然而,GET 请求的一个重要前提是,它们表现出[一致性](https://www.restapitutorial.com/lessons/idempotency.html),即每次请求的结果是一致的,而这个路由不是这样的。这就是我们坚持使用 POST 的原因。 +一些读者可能对根据上述要求,创建牌组这个路由将参数作为 URL 请求的一部分感到困惑,可能会考虑让这个路由用 GET 请求而不是 POST。 然而,GET 请求的一个重要前提是,它们表现出[一致性](https://www.restapitutorial.com/lessons/idempotency.html),即每次请求的结果是一致的,而这个路由不是这样的。这就是我们坚持使用 POST 的原因。 路由对应的 Handler 遵循相同的模式。我们解析查询参数,根据这些参数创建一个领域实体,对其进行操作,更新存储并返回专属的 DTO。让我们来看看更多的细节。 @@ -455,7 +455,7 @@ r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerFiles.Handler, url)) 这些都完成之后,那我们现在可以运行我们的应用程序,看看通过 Swagger 生成的文档。 -## API容器化 +## API 容器化 最后但并非最不重要的是我们将如何部署我们的应用程序。传统的方法是在一个专门的服务器上安装,并在安装的服务器上运行应用程序。 @@ -502,4 +502,4 @@ docker run -it --rm -p 8080:8080 ## 总结 -在这篇文章中,我们已经介绍了在 Go 中编写简洁架构 API 的整体过程。从经过测试的领域开始,为其提供一个API 层,使用 OpenAPI 标准对其进行记录,并将我们的 runtime 与应用程序打包在一起,从而简化了部署过程。 \ No newline at end of file +在这篇文章中,我们已经介绍了在 Go 中编写简洁架构 API 的整体过程。从经过测试的领域开始,为其提供一个 API 层,使用 OpenAPI 标准对其进行记录,并将我们的 runtime 与应用程序打包在一起,从而简化了部署过程。 \ No newline at end of file diff --git a/2023/w05_Introducing Ristretto A High-Performance Go Cache - Dgraph Blog.md b/2023/w05_Introducing Ristretto A High-Performance Go Cache - Dgraph Blog.md index 4d81d2c..c6da1fc 100644 --- a/2023/w05_Introducing Ristretto A High-Performance Go Cache - Dgraph Blog.md +++ b/2023/w05_Introducing Ristretto A High-Performance Go Cache - Dgraph Blog.md @@ -1,12 +1,12 @@ -# Ristretto 简介: 一个高性能GO缓存 +# Ristretto 简介: 一个高性能 GO 缓存 - 原文地址:[Introducing Ristretto: A High-Performance Go Cache](https://dgraph.io/blog/post/introducing-ristretto-high-perf-go-cache/) - 原文作者:[Dmitry Filimonov](https://github.com/petethepig) -- 本文永久链接:[Ristretto 简介: 一个高性能GO缓存](https://github.com/gocn/translator/blob/master/2023/w05_Introducing%20Ristretto%20A%20High-Performance%20Go%20Cache%20-%20Dgraph%20Blog.md) +- 本文永久链接:[Ristretto 简介: 一个高性能 GO 缓存](https://github.com/gocn/translator/blob/master/2023/w05_Introducing%20Ristretto%20A%20High-Performance%20Go%20Cache%20-%20Dgraph%20Blog.md) - 译者:[784909593](https://github.com/784909593) - 校对:[b8kings0ga](https://github.com/b8kings0ga) -**这个博客登上了 Golang [subreddit](https://www.reddit.com/r/golang/comments/d6taoq/introducing_ristretto_a_highperformance_go_cache/) 的顶部,并且在 [Hacker News](https://news.ycombinator.com/item?id=21023949) 的trending上排在前十位。 一定要在那里参与讨论,并通过给我们一个 [star](https://github.com/dgraph-io/dgraph),表达对我们的喜欢。** +**这个博客登上了 Golang [subreddit](https://www.reddit.com/r/golang/comments/d6taoq/introducing_ristretto_a_highperformance_go_cache/) 的顶部,并且在 [Hacker News](https://news.ycombinator.com/item?id=21023949) 的 trending 上排在前十位。 一定要在那里参与讨论,并通过给我们一个 [star](https://github.com/dgraph-io/dgraph),表达对我们的喜欢。** 经过六个月的研发,我们自豪的宣布**缓存 [Ristretto](https://github.com/dgraph-io/ristretto):一个高性能、并发、可设置内存上限的 Go 缓存**的初始版本。他是抗争用、扩展性好、提供稳定的高命中率。 @@ -21,7 +21,7 @@ 2. 缓存命中率高; 3. Memory-bounded (限制为可配置的最大内存使用量); 4. 随着核数和协程数量的增加而扩展; -5. 在非随机key访问(例如 Zipf)分布下很好的扩展; +5. 在非随机 key 访问(例如 Zipf)分布下很好的扩展; 发布了[博客文章](https://blog.dgraph.io/post/caching-in-go/)之后,我们组建了一个团队来解决其中提到的挑战,并创建了一个值得与非 Go 语言缓存实现进行比较的 Go 缓存库。特别的,[Caffeine](https://github.com/ben-manes/caffeine) 这是一个基于 Java 8 的高性能、近乎最优的缓存库。许多基于 Java 8 的数据库都在使用它, 比如 Cassandra,HBase,和 Neo4j。[这里](http://highscalability.com/blog/2016/1/25/design-of-a-modern-cache.html)有一篇关于 Caffeine 设计的文章。 @@ -31,7 +31,7 @@ 在我们开始讲解 [Ristretto](https://github.com/dgraph-io/ristretto) 的设计之前, 这有一个代码片段展示了如何使用它: -``` +```plain func main() { cache, err := ristretto.NewCache(&ristretto.Config{ NumCounters: 1e7, // key 跟踪频率为(10M) @@ -71,13 +71,13 @@ cache.Del("key") 缓存的核心是一个 hash map 和关于进入和出去的规则。如果 hash map 表现不佳,那么整个缓存将受到影响。 与 Java 不同, Go 没有无锁的并发 hashmap。相反,Go 的线程安全是必须通过显式获取互斥锁来达到。 -我们尝试了多种实现方式(使用 Ristretto 中的`store`接口),发现`sync.Map`在读取密集型工作负载方面表现良好,但在写入工作负载方面表现不佳。考虑没有 thread-local 存储,**我们发现使用分片的互斥锁包装的 Go map具有最佳的整体性能。** 特别是,我们选择使用 256 个分片,以确保即使在 64 核服务器上也能表现良好。 +我们尝试了多种实现方式(使用 Ristretto 中的`store`接口),发现`sync.Map`在读取密集型工作负载方面表现良好,但在写入工作负载方面表现不佳。考虑没有 thread-local 存储,**我们发现使用分片的互斥锁包装的 Go map 具有最佳的整体性能。** 特别是,我们选择使用 256 个分片,以确保即使在 64 核服务器上也能表现良好。 使用基于分片的方法,我们还需要找到一种快速方法来计算 key 应该进入哪个分片。这个要求和对 key 太长消耗太多内存的担忧,导致我们对 key 使用 `uint64`,而不是存储整个 key。理由是我们需要在多个地方使用 key 的哈希值,并且在入口处执行一次允许我们重用该哈希值,避免任何更多的计算。 -**为了生成快速 hash,我们从 Go Runtime 借用了 [runtime.memhash](https://github.com/dgraph-io/ristretto/blob/master/z/rtutil.go#L42-L44)** 该函数使用汇编代码快速生成哈希。 请注意,这个 hash 有一个随机化器,每当进程启动时都会初始化,这意味着相同的key不会在下一次进程运行时生成相同的哈希。 但是,这对于非持久缓存来说没问题。 在我们的[实验](https://github.com/dgraph-io/ristretto/blob/master/z/rtutil_test.go#L11-L44), 我们发现它可以在 10ns 内散列 64 字节的 key。 +**为了生成快速 hash,我们从 Go Runtime 借用了 [runtime.memhash](https://github.com/dgraph-io/ristretto/blob/master/z/rtutil.go#L42-L44)** 该函数使用汇编代码快速生成哈希。 请注意,这个 hash 有一个随机化器,每当进程启动时都会初始化,这意味着相同的 key 不会在下一次进程运行时生成相同的哈希。 但是,这对于非持久缓存来说没问题。 在我们的[实验](https://github.com/dgraph-io/ristretto/blob/master/z/rtutil_test.go#L11-L44), 我们发现它可以在 10ns 内散列 64 字节的 key。 -``` +```plain BenchmarkMemHash-32 200000000 8.88 ns/op BenchmarkFarm-32 100000000 17.9 ns/op BenchmarkSip-32 30000000 41.1 ns/op @@ -103,7 +103,7 @@ BenchmarkFnv-32 20000000 70.6 ns/op 存储在 pool 中的任何 item 都可能随时自动删除,[不另行](https://golang.org/pkg/sync/#Pool)通知。_这引入了一个级别的有损行为。_ Pool 中的每个 item 实际上都是一批 key。当批次填满时,它会被推送到一个 chan。chan 大小故意保持较小,以避免消耗太多 CPU 周期来处理它。 如果 chan 已满,则丢弃该批次。_这引入了二级有损行为。_ 一个 goroutine 从内部 chan 中获取这批数据并处理这些 key,更新它们的命中计数器。 -``` +```plain AddToLossyBuffer(key): stripe := b.pool.Get().(*ringStripe) stripe.Push(key) @@ -135,7 +135,7 @@ Set buffer 的要求与 Get 略有不同。在 Gets 中,我们缓冲 key,只 与 Gets 一样,此方法旨在优化抗争用性。 但是,有一些注意事项,如下所述。 -``` +```plain select { case c.setBuf <- &item{key: hash, val: val, cost: cost}: return true @@ -170,7 +170,7 @@ default: _无限大的缓存实际上是不可能的。_ 缓存的大小必须有界。许多缓存库会将缓存大小视为元素的数量。我们发现这种方法 _很幼稚_。当然,它适用于值大小相同的工作负载。然而,大多数工作负载具有可变大小的值。一个值可能需要几个字节,另一个可能需要几千字节,还有一个需要几兆字节。将它们视为具有相同的内存成本是不现实的。 -**在 [Ristretto](https://github.com/dgraph-io/ristretto) 中, 我们将成本附加到每个 key-value** 用户可以在调用 Set 时指定该成本是多少。我们将此成本计入缓存的 MaxCost。 当缓存满负荷运行时,一个 _重_ 的 item 可能会取代许多 _轻_ 的item。 这种机制很好,因为它适用于所有不同的工作负载,包括每个 key-value 成本为 1 的朴素方法。 +**在 [Ristretto](https://github.com/dgraph-io/ristretto) 中, 我们将成本附加到每个 key-value** 用户可以在调用 Set 时指定该成本是多少。我们将此成本计入缓存的 MaxCost。 当缓存满负荷运行时,一个 _重_ 的 item 可能会取代许多 _轻_ 的 item。 这种机制很好,因为它适用于所有不同的工作负载,包括每个 key-value 成本为 1 的朴素方法。 #### 通过 TinyLFU 的准入策略 @@ -194,13 +194,13 @@ TinyLFU 还通过`重置`功能维护 key 访问的新近度。每 N 个 key 递 #### 通过采样 LFU 的驱逐策略 -当缓存达到容量时,每个传入的 key 都应替换缓存中存在的一个或多个 key。 不仅如此,**传入 key 的 ε 应该高于被驱逐 key 的 ε。**为了找到一个 ε 低的 key,我们使用 Go map 迭代提供的自然[随机性](https://blog.golang.org/go-maps-in-action)来选择key样本并循环,在它们之上找到具有最低ε的key。 +当缓存达到容量时,每个传入的 key 都应替换缓存中存在的一个或多个 key。 不仅如此,**传入 key 的 ε 应该高于被驱逐 key 的 ε。**为了找到一个 ε 低的 key,我们使用 Go map 迭代提供的自然[随机性](https://blog.golang.org/go-maps-in-action)来选择 key 样本并循环,在它们之上找到具有最低ε的 key。 然后我们将这个 key 的 ɛ 与传入的 key 进行比较。如果传入的 key 具有更高的 ε,则该 key 将被逐出(_驱逐策略_)。否则,传入的 key 将被拒绝(_准入策略_)。重复此机制,直到传入 key 的成本可以放入缓存中。因此,一个传入的 key 可能取代一个以上的 key。_注意,传入 key 的成本在选择驱逐 key 时不起作用。_ **使用这种方法,在各种工作负载下,命中率与精确 LFU 策略的误差在 1% 以内。** 这意味着我们在同一个包中获得了准入策略、保守内存使用和较低争用的好处。 -``` +```plain // 准入和驱逐算法的片段 incHits := p.admit.Estimate(key) for ; room < 0; room = p.evict.roomLeft(cost) { @@ -238,7 +238,7 @@ for ; room < 0; room = p.evict.roomLeft(cost) { **为了实现可扩展性,我们确保每个原子计数器完全占据一个完整的 cache line。**所以,每个核在不同的 cache line 上工作。Ristretto 通过为每个 metric 分配 256 个 uint64 来使用它,在每个活动的 uint64 之间留下 9 个未使用的 uint 64。为了避免额外的计算,重用 key hash 值去决定要增加哪个 uint64。 -``` +```plain Add: valp := p.all[t] // 通过在两个将递增的原子计数器之间填充至少 64 字节的空间来避免 false sharing。 @@ -274,7 +274,7 @@ return total ##### 数据库 -这个 trace 被描述为“在一个商业数据库上,一个商业网站正运行一个ERP应用,一个数据库服务运行在上面“ +这个 trace 被描述为“在一个商业数据库上,一个商业网站正运行一个 ERP 应用,一个数据库服务运行在上面“ ![命中率: 商业数据库](https://dgraph.io/blog/images/rt-hit-db.svg?sanitize=true) @@ -322,5 +322,5 @@ return total ## 结论 -我们的目标是让缓存库与 Caffeine 竞争。虽然没有完全实现, 但我们确实通过使用其他人可以学习的一些新技术,创造了比目前Go世界中大多数其他人[**更好**](https://en.wikipedia.org/wiki/Ristretto)的东西。 +我们的目标是让缓存库与 Caffeine 竞争。虽然没有完全实现, 但我们确实通过使用其他人可以学习的一些新技术,创造了比目前 Go 世界中大多数其他人[**更好**](https://en.wikipedia.org/wiki/Ristretto)的东西。 在 Dgraph 中使用此缓存的一些初步实验看起来很有希望。并且我们希望将 [Ristretto](https://github.com/dgraph-io/ristretto) 整合到 [Dgraph](https://github.com/dgraph-io/dgraph) 和 [Badger](https://github.com/dgraph-io/badger) 在接下来的几个月里. 一定要[查看它](https://github.com/dgraph-io/ristretto),也许可以使用 Ristretto 来加快您的工作负载。 diff --git a/2023/w06_Go's_garbage_collector.md b/2023/w06_Go's_garbage_collector.md index 1e6fc6d..07740dc 100644 --- a/2023/w06_Go's_garbage_collector.md +++ b/2023/w06_Go's_garbage_collector.md @@ -8,17 +8,17 @@ > Go \[>= v1.5\]的新垃圾回收器是一种并发的三色标记清除回收器,这个想法最早是由 [Dijkstra 在 1978](https://github.com/rubinius/rubinius-website-archive/blob/cf54187d421275eec7d2db0abd5d4c059755b577/_posts/2013-06-22-concurrent-garbage-collection.markdown) 年提出的。 -Go 团队一直在密切关注并改进 Go 语言的垃圾回收器。从每50毫秒一次的10毫秒 STW 暂停到每次GC有两个 500μs 的 STW 暂停,整个改进过程可以在[这里](https://blog.golang.org/ismmkeynote)找到。 +Go 团队一直在密切关注并改进 Go 语言的垃圾回收器。从每 50 毫秒一次的 10 毫秒 STW 暂停到每次 GC 有两个 500μs 的 STW 暂停,整个改进过程可以在[这里](https://blog.golang.org/ismmkeynote)找到。 -长期从事 Go 开发,我一直对其性能感到畏惧,因此我决定深入了解其中的机制,比如 Go 语言为什么如此高效和充满前途,了解它使用的是什么样的垃圾回收器,goroutine 如何在 OS 线程上多路复用,如何对 Go 程序进行性能分析,Go 运行时是如何工作的等等。在这篇文章中,我们将着重探讨Go的垃圾回收器是如何工作的。 +长期从事 Go 开发,我一直对其性能感到畏惧,因此我决定深入了解其中的机制,比如 Go 语言为什么如此高效和充满前途,了解它使用的是什么样的垃圾回收器,goroutine 如何在 OS 线程上多路复用,如何对 Go 程序进行性能分析,Go 运行时是如何工作的等等。在这篇文章中,我们将着重探讨 Go 的垃圾回收器是如何工作的。 -在浏览互联网时,我发现了很多关于Go语言垃圾回收器的赞誉,而我对垃圾回收器的概念和工作原理只有一个抽象的理解,于是我开始阅读和学习,并在[这里](https://agrim123.github.io/posts/garbage-collection.html)记录了一些关于垃圾回收的笔记。 +在浏览互联网时,我发现了很多关于 Go 语言垃圾回收器的赞誉,而我对垃圾回收器的概念和工作原理只有一个抽象的理解,于是我开始阅读和学习,并在[这里](https://agrim123.github.io/posts/garbage-collection.html)记录了一些关于垃圾回收的笔记。 这篇博客仅仅是我在阅读一些关于 Go 的垃圾回收器及其演变历程的博客后整理出的一些想法和结论的随笔。 所以,让我们开始吧。 -``` +```plain 紧紧抓住,伙计,这将是一场精彩的旅程。 ``` @@ -38,7 +38,7 @@ Go 是一种内存管理语言,这意味着大多数时候你不必担心手 简单的 STW 标记/清除的问题在随着你增加核心和扩大你的堆或分配率时会变得非常糟糕。 -## Go的并发收集器 +## Go 的并发收集器 Go 现在的 GC 不是 **“分代”回收** (一种垃圾回收算法)的。它只在后台运行一个普通的标记/清除。这有一些缺点: @@ -76,7 +76,7 @@ func stubbornGoroutine(numbers []int32) int { 这种情况可能会导致垃圾回收无法开始。因为当收集器等待时,其他处理器不能服务任何其他协程。因此,协程必须在合理的时间内进行函数调用。 -> 如果一个goroutine没有调用函数,它不会被抢占,并且在任务结束之前它的 P 不会释放。这将迫使 “Stop the World” 等待它。 +> 如果一个 goroutine 没有调用函数,它不会被抢占,并且在任务结束之前它的 P 不会释放。这将迫使 “Stop the World” 等待它。 ### 标记阶段 (并发) @@ -90,7 +90,7 @@ func stubbornGoroutine(numbers []int32) int { 如果收集器确定它需要减缓分配,它将会招募应用程序的 Goroutine 协助 Marking 工作,这称为 Mark Assist。任何应用程序 Goroutine 在 Mark Assist 中的时间量与它对堆内存的数据添加量成比例。 -> Mark Assist 可以帮助更快地完成收集。 +> Mark Assist 可以帮助更快地完成收集。 收集器的一个目标是消除对 Mark Assist 的需求。如果任意一次收集最终需要大量的 Mark Assist,收集器可以更早开始下一次垃圾收集,以减少下一次收集所需的 Mark Assist 数量。 @@ -98,7 +98,7 @@ func stubbornGoroutine(numbers []int32) int { 一旦标记工作完成,下一阶段是标记终止。这个阶段将关闭写屏障,执行各种清理任务以及计算下一个回收目标的时刻。在标记阶段处于紧密循环的协程也可能导致标记终止 STW 延迟延长。 -回收完成后,应用程序协程可以再次使用每个P,应用程序将回到全速。 +回收完成后,应用程序协程可以再次使用每个 P,应用程序将回到全速。 ### 并发清除 @@ -108,13 +108,13 @@ func stubbornGoroutine(numbers []int32) int { ## 如何让运行时知道什么时候开始回收垃圾? -收集器有一个步伐算法,用于确定何时开始收集。节奏的建模类似于一个控制问题,它试图找到启动 GC 周期的正确时间,以达到目标堆大小目标。Go 的默认步伐控制器将尝试在堆大小加倍时触发 GC 周期。它通过在当前 GC 周期的标记终止阶段设置下一个堆触发大小来实现这一点。因此,在标记所有活动内存后,它可以决定在当前活动集的总堆大小是目前活动集的2倍时触发下一个 GC。2倍的值来自运行时使用的变量`GOGC`,用于设置触发比率。 +收集器有一个步伐算法,用于确定何时开始收集。节奏的建模类似于一个控制问题,它试图找到启动 GC 周期的正确时间,以达到目标堆大小目标。Go 的默认步伐控制器将尝试在堆大小加倍时触发 GC 周期。它通过在当前 GC 周期的标记终止阶段设置下一个堆触发大小来实现这一点。因此,在标记所有活动内存后,它可以决定在当前活动集的总堆大小是目前活动集的 2 倍时触发下一个 GC。2 倍的值来自运行时使用的变量`GOGC`,用于设置触发比率。 一种错误的观念是认为减缓收集器的速度是提高性能的方法。这个想法是,如果你可以延迟下一次收集的开始,那么你就是在延迟它造成的延迟。对收集器的同情并不是减缓节奏。 ___ -Go 1.5 在2015年8月发布,带有新的低暂停并发垃圾收集器,包括实现了[节奏算法](https://docs.google.com/document/d/1wmjrocXIWTr1JxU-3EQBI6BK6KgtiFArkG47XK73xIQ/edit#heading=h.4801yvqy4taz)。 +Go 1.5 在 2015 年 8 月发布,带有新的低暂停并发垃圾收集器,包括实现了[节奏算法](https://docs.google.com/document/d/1wmjrocXIWTr1JxU-3EQBI6BK6KgtiFArkG47XK73xIQ/edit#heading=h.4801yvqy4taz)。 ___ @@ -138,7 +138,7 @@ ___ - 找到最佳的一致节奏。 - 最小化每次收集的持续时间、STW 和 Mark Assist。 -## 有两个控制垃圾回收的开关。 +## 有两个控制垃圾回收的开关 正如 Rick Hudson 在[该文](https://blog.golang.org/ismmkeynote)中谈到。 diff --git a/2023/w07_Go_1_20_Experiment_Memory_Arenas_vs_Traditional_Memory_Management.md b/2023/w07_Go_1_20_Experiment_Memory_Arenas_vs_Traditional_Memory_Management.md index 3a666dc..92fd99d 100644 --- a/2023/w07_Go_1_20_Experiment_Memory_Arenas_vs_Traditional_Memory_Management.md +++ b/2023/w07_Go_1_20_Experiment_Memory_Arenas_vs_Traditional_Memory_Management.md @@ -16,16 +16,16 @@ ## 简介 -Go 1.20 引入了一个实验性的内存管理概念 "arenas",可以用来提高Go程序的性能。在本博客文章中,我们将探讨: +Go 1.20 引入了一个实验性的内存管理概念 "arenas",可以用来提高 Go 程序的性能。在本博客文章中,我们将探讨: - 什么是 arenas - 它们是如何工作的 - 如何确定你的程序是否可以从使用 arenas 中受益 -- 我们如何使用arenas优化我们的一项服务 +- 我们如何使用 arenas 优化我们的一项服务 ## 什么是内存 Arenas -Go语言是一种利用垃圾回收机制的编程语言,这意味着运行时会自动帮助程序员管理内存的分配和释放。这消除了手动内存管理的需求,但也带来了代价: +Go 语言是一种利用垃圾回收机制的编程语言,这意味着运行时会自动帮助程序员管理内存的分配和释放。这消除了手动内存管理的需求,但也带来了代价: **Go 运行时必须跟踪 \*每个\* 分配的对象,导致性能开销增加。** @@ -45,17 +45,17 @@ Arenas 提供了一种解决这个问题的方法,通过减少与许多小分 ![2](https://github.com/gocn/translator/blob/master/static/images/2023/w07-Go-1-20-Experiment-Memory-Arenas-vs-Traditional-Memory-Management/2.png) -你可以看到内存分配(`533.30 M`)的大部分来自代码的一个区域 - 这是在底部调用函数`InsertStackA`的紫色节点。鉴于它代表65%的分配,这是使用 arenas 的好候选者。但是,通过减少这些分配是否可以获得足够的性能收益?让我们看看同一服务的CPU分析(`cpu`): +你可以看到内存分配(`533.30 M`)的大部分来自代码的一个区域 - 这是在底部调用函数`InsertStackA`的紫色节点。鉴于它代表 65%的分配,这是使用 arenas 的好候选者。但是,通过减少这些分配是否可以获得足够的性能收益?让我们看看同一服务的 CPU 分析(`cpu`): ![3](https://github.com/gocn/translator/blob/master/static/images/2023/w07-Go-1-20-Experiment-Memory-Arenas-vs-Traditional-Memory-Management/3.png) 几件事情很突出: -- 程序在相同的`InsertStackA`函数中花费了很多CPU时间,因此显然有潜在的重要性能改进潜力。 -- 如果搜索`runtime.mallocgc`(底部的多个粉色节点),你会发现该函数在各种不同的地方频繁调用,它占用了我们总执行时间的约14%。 -- 大约5%的CPU时间花费在`runtime.gcBgMarkWorker`(位于火焰图右侧的粉色节点)上。 +- 程序在相同的`InsertStackA`函数中花费了很多 CPU 时间,因此显然有潜在的重要性能改进潜力。 +- 如果搜索`runtime.mallocgc`(底部的多个粉色节点),你会发现该函数在各种不同的地方频繁调用,它占用了我们总执行时间的约 14%。 +- 大约 5%的 CPU 时间花费在`runtime.gcBgMarkWorker`(位于火焰图右侧的粉色节点)上。 -因此,理论上,如果我们优化了这个程序中的所有分配,我们可以减少14%+5%= 19%的CPU时间。这将转化为我们所有客户的19%成本节约和延迟改进。在实践中,不太可能真正使这些数字降到零,但这仍然是应用程序执行的重要工作,可能值得优化。 +因此,理论上,如果我们优化了这个程序中的所有分配,我们可以减少 14%+5%= 19%的 CPU 时间。这将转化为我们所有客户的 19%成本节约和延迟改进。在实践中,不太可能真正使这些数字降到零,但这仍然是应用程序执行的重要工作,可能值得优化。 ## 我们做出的优化 @@ -63,14 +63,14 @@ Arenas 提供了一种解决这个问题的方法,通过减少与许多小分 - 首先,我们创建了[一个包装组件](https://github.com/pyroscope-io/pyroscope/pull/1804/files#diff-70ab4bbe796a97ad1a47d7970504296eff36b5307527ae2806d2b50f94f83a45),负责处理切片或结构的分配。如果启用了 arenas ,此组件使用 arenas 分配切片,否则使用标准“make”函数。我们通过使用构建标记(`//go:build goexperiment.arenas`)实现此目的。这允许在构建时轻松地在 arenas 分配和标准分配之间切换 - 然后,我们在解析器代码周围添加了[初始化](https://github.com/pyroscope-io/pyroscope/pull/1804/files#diff-32bf8c53a15c8a5f7eb424b21c8502dc4905ec3caa28fac50f64277361ae746fR417)和[清理](https://github.com/pyroscope-io/pyroscope/pull/1804/files#diff-34edf37e55842273380ee6cb31c9245f31ed25aa6d7898b0f2c25145f17d8ea0R170)调用 arenas -- 接下来,我们[用我们的包装组件中的make调用替换了常规的`make`调用](https://github.com/pyroscope-io/pyroscope/pull/1804/files#diff-abe15b6d3634170650f86bb7283aa15265de2197cffa969deda2dd5b26fcecd9R89-R92) +- 接下来,我们[用我们的包装组件中的 make 调用替换了常规的`make`调用](https://github.com/pyroscope-io/pyroscope/pull/1804/files#diff-abe15b6d3634170650f86bb7283aa15265de2197cffa969deda2dd5b26fcecd9R89-R92) - 最后,我们在启用了 arenas 的情况下构建了 pyroscope,并逐渐部署到了我们的 [Pyroscope Cloud](https://pyroscope.io/pricing) 生产环境中。 ## 我们 Arenas 实验的结论 ![4](https://github.com/gocn/translator/blob/master/static/images/2023/w07-Go-1-20-Experiment-Memory-Arenas-vs-Traditional-Memory-Management/4.png) -上面的火焰图表示我们实施更改后的配置文件。您可以看到,许多`runtime.mallocgc`调用现在已经消失,但被 arenas 特定的等效项(`runtime.(*userArena).alloc`)替代,您也可以看到垃圾回收开销减少了一半。仅从火焰图上看准确的节省量很难看出,但是当我们查看结合了火焰图和AWS指标的CPU使用情况的 Grafana 仪表板时,我们发现CPU使用率大约减少了8%。这直接转化为该特定服务的云账单上的8%费用节省,使其成为一项有价值的改进。 +上面的火焰图表示我们实施更改后的配置文件。您可以看到,许多`runtime.mallocgc`调用现在已经消失,但被 arenas 特定的等效项(`runtime.(*userArena).alloc`)替代,您也可以看到垃圾回收开销减少了一半。仅从火焰图上看准确的节省量很难看出,但是当我们查看结合了火焰图和 AWS 指标的 CPU 使用情况的 Grafana 仪表板时,我们发现 CPU 使用率大约减少了 8%。这直接转化为该特定服务的云账单上的 8%费用节省,使其成为一项有价值的改进。 ![5](https://github.com/gocn/translator/blob/master/static/images/2023/w07-Go-1-20-Experiment-Memory-Arenas-vs-Traditional-Memory-Management/5.png) @@ -100,11 +100,11 @@ Go arenas 目前的一个主要缺点是它是一项实验性特性。 API 和 ## 解决社区关注的问题 -Go团队已经收到了关于 arenas 的大量反馈,我们想要回应社区中我们所看到的一些担忧。有关 arenas 最常见的问题是它们添加了一种隐式且不立即显现问题的程序崩溃方式,使语言变得更加复杂。 +Go 团队已经收到了关于 arenas 的大量反馈,我们想要回应社区中我们所看到的一些担忧。有关 arenas 最常见的问题是它们添加了一种隐式且不立即显现问题的程序崩溃方式,使语言变得更加复杂。 大部分的批评是明确的但误导性的。我们不预期 arenas 会变得普遍。我们认为 arenas 是一个强大的工具,但只适用于特定情况。在我们看来, arenas 应该包含在标准库中,但它们的使用应该受到警惕,就像使用`unsafe`,`reflect`或`cgo`一样。 -我们对 arenas 的经验非常充分,我们能够证明 arenas 可以显着减少垃圾回收和内存分配的时间。本文描述的实验关注的是一个单独的、已经高度优化的服务,我们仍然能够通过使用 arenas 获得8%的额外性能。我们认为许多用户可以从在代码库中使用 arenas 中获益更多。 +我们对 arenas 的经验非常充分,我们能够证明 arenas 可以显着减少垃圾回收和内存分配的时间。本文描述的实验关注的是一个单独的、已经高度优化的服务,我们仍然能够通过使用 arenas 获得 8%的额外性能。我们认为许多用户可以从在代码库中使用 arenas 中获益更多。 此外,我们还发现,相比我们过去尝试的其他优化(如使用缓冲池或编写自定义无分配 protobuf 解析器), arenas 的实现更容易。与其他类型的优化相比,它们具有相同的缺点,但提供了更多的好处 - 因此,在我们看来, arenas 是一个净赢。我们希望未来能看到 arenas 成为标准库的一部分(并且是常用包如 protobuf 或 JSON 解析器的一部分)。 @@ -112,4 +112,4 @@ Go团队已经收到了关于 arenas 的大量反馈,我们想要回应社区 Arenas 对于优化 Go 程序是一个强大的工具,特别适用于处理大量 protobuf 或 JSON 块的情况。它们有可能带来显著的性能改进,但是需要注意的是它们是一个实验性的功能,不保证兼容性或在未来版本中的存在。 -我们建议您对应用程序进行分析,并在代码库的有限部分尝试使用arenas,并将您的结果[报告给 Go 团队](https://github.com/golang/go/issues/51317)。 +我们建议您对应用程序进行分析,并在代码库的有限部分尝试使用 arenas,并将您的结果[报告给 Go 团队](https://github.com/golang/go/issues/51317)。 diff --git a/2023/w08_Profile_guided_optimization_prview.md b/2023/w08_Profile_guided_optimization_prview.md index 097bc27..94c6185 100644 --- a/2023/w08_Profile_guided_optimization_prview.md +++ b/2023/w08_Profile_guided_optimization_prview.md @@ -25,14 +25,14 @@ Go 1.20 包含用于预览的 PGO 的初始支持。 完整文档请参阅[按 ### 建立开发环境 -``` +```plain $ go mod init example.com/markdown $ go get gitlab.com/golang-commonmark/markdown@bf3e522c626a ``` 在 `main.go`: -``` +```plain package main import ( @@ -89,7 +89,7 @@ func main() { 构建并运行服务器: -``` +```plain $ go build -o markdown.nopgo.exe $ ./markdown.nopgo.exe 2023/01/19 14:26:24 Serving on port 8080... @@ -97,7 +97,7 @@ $ ./markdown.nopgo.exe 让我们尝试从另一个终端发送一些 Markdown。我们可以将 Go 项目中的 README 文件作为示例文档: -``` +```plain $ curl -o README.md -L "https://raw.githubusercontent.com/golang/go/c16c2c49e2fa98ae551fc6335215fadd62d33542/README.md" $ curl --data-binary @README.md http://localhost:8080/render

The Go Programming Language

@@ -114,13 +114,13 @@ reliable, and efficient software.

通常,您希望从生产环境中收集性能分析文件,以便编译器获得生产环境中代表性行为。 由于此示例没有“生产”环境,因此我们将创建一个简单的程序来生成负载,以便收集性能分析文件。 复制此[程序](https://go.dev/play/p/yYH0kfsZcpL)的源代码到 `load/main.go` 中并启动负载生成器(确保服务器仍在运行!)。 -``` +```plain $ go run example.com/markdown/load ``` 在负载生成器还在运行中,从服务器上下载一个性能分析文件: -``` +```plain $ curl -o cpu.pprof "http://localhost:8080/debug/pprof/profile?seconds=30" ``` @@ -134,7 +134,7 @@ $ curl -o cpu.pprof "http://localhost:8080/debug/pprof/profile?seconds=30" 让我们构建: -``` +```plain $ mv cpu.pprof default.pgo $ go build -pgo=auto -o markdown.withpgo.exe ``` @@ -145,31 +145,31 @@ $ go build -pgo=auto -o markdown.withpgo.exe 首先,我们将对没有 PGO 的服务器进行基准测试。启动该服务器: -``` +```plain $ ./markdown.nopgo.exe ``` 在服务器运行时,执行一些基准测试迭代: -``` +```plain $ go test example.com/markdown/load -bench=. -count=20 -source ../README.md > nopgo.txt ``` 完成后,终止原服务器并启动 PGO 版本的服务器: -``` +```plain $ ./markdown.withpgo.exe ``` 在服务器运行时,执行一些基准测试迭代: -``` +```plain $ go test example.com/markdown/load -bench=. -count=20 -source ../README.md > withpgo.txt ``` 完成后,我们对比结果: -``` +```plain $ go install golang.org/x/perf/cmd/benchstat@latest $ benchstat nopgo.txt withpgo.txt goos: linux diff --git a/2023/w09_Introducing_Service_Weaver_A_Framework_for_Writing_Distributed_Applications.md b/2023/w09_Introducing_Service_Weaver_A_Framework_for_Writing_Distributed_Applications.md index 32de78f..f78301b 100644 --- a/2023/w09_Introducing_Service_Weaver_A_Framework_for_Writing_Distributed_Applications.md +++ b/2023/w09_Introducing_Service_Weaver_A_Framework_for_Writing_Distributed_Applications.md @@ -54,7 +54,7 @@ func main() { 要对上述应用程序进行更改,例如向 Add 方法添加无限数量的参数,您只需要更改 Add 的签名,更改其调用位置,然后重新部署应用程序。Service Weaver 确保新版本的 main() 仅与新版本的 Adder 通信,而不管它们是否共存。这种行为,结合使用语言原生的数据结构和方法调用,使您可以专注于编写应用程序逻辑,而不必担心部署拓扑和服务间通信(例如,在代码中没有 protos、stubs 或 RPC 通道)。 -当运行应用程序时,Service Weaver 允许您在任何地方运行它——在您的本地桌面环境、本地机架或云上——而不需要更改应用程序代码。这种可移植性是通过Service Weaver 框架内置的明确关注点分离实现的。在一端,我们有编程框架,用于应用程序开发。在另一端,我们有各种“**部署器**”实现,每个实现针对一种部署环境。 +当运行应用程序时,Service Weaver 允许您在任何地方运行它——在您的本地桌面环境、本地机架或云上——而不需要更改应用程序代码。这种可移植性是通过 Service Weaver 框架内置的明确关注点分离实现的。在一端,我们有编程框架,用于应用程序开发。在另一端,我们有各种“**部署器**”实现,每个实现针对一种部署环境。 ![Flow chart depicting Service Weaver Libraries deployer implementations across three separate platforms in one single iteration](C:\Users\zhengxm\Documents\notes\翻译\Introducing Service Weaver A Framework for Writing Distributed Applications\2.png) @@ -62,8 +62,8 @@ func main() { ## Service Weaver v0.1 包括什么? -- 用于编写应用程序的[Go核心库](https://github.com/ServiceWeaver/weaver)。 -- 用于在本地或 GKE 上运行应用程序的一些部署器,如[本地部署器](https://github.com/ServiceWeaver/weaver/tree/main/cmd/weaver)或[ GKE 部署器](https://github.com/ServiceWeaver/weaver-gke)。 +- 用于编写应用程序的[Go 核心库](https://github.com/ServiceWeaver/weaver)。 +- 用于在本地或 GKE 上运行应用程序的一些部署器,如[本地部署器](https://github.com/ServiceWeaver/weaver/tree/main/cmd/weaver)或[GKE 部署器](https://github.com/ServiceWeaver/weaver-gke)。 - 一组 API,允许您为任何其他平台编写自己的部署器。 所有库都在 Apache 2.0 许可下发布。请注意,在发布 v1.0 版本之前,**我们可能会引入破坏性更改**。 @@ -79,6 +79,6 @@ func main() { ## 更多资源 - 访问我们的网站[serviceweaver.dev](https://serviceweaver.dev/),获取有关该项目的最新信息,例如入门指南、教程和博客文章。 -- 访问我们在GitHub上的其中一个Service Weaver [代码库](https://github.com/orgs/ServiceWeaver/repositories)。 +- 访问我们在 GitHub 上的其中一个 Service Weaver [代码库](https://github.com/orgs/ServiceWeaver/repositories)。 *By Srdjan Petrovic and Garv Sawhney, 仅代表 Service Weaver team* diff --git a/2023/w12_Go_Performance_Boosters_The_Top_5_Tips_and_Tricks_You_Need_to_Know.md b/2023/w12_Go_Performance_Boosters_The_Top_5_Tips_and_Tricks_You_Need_to_Know.md index 624a12e..4db7cd7 100644 --- a/2023/w12_Go_Performance_Boosters_The_Top_5_Tips_and_Tricks_You_Need_to_Know.md +++ b/2023/w12_Go_Performance_Boosters_The_Top_5_Tips_and_Tricks_You_Need_to_Know.md @@ -1,4 +1,4 @@ -# Go性能加速器:你需要知道的5个诀窍和技巧 +# Go 性能加速器:你需要知道的 5 个诀窍和技巧 - 原文地址: - 原文作者:Aiden (@func25) @@ -6,16 +6,16 @@ - 译者:[小超人](https://github.com/focozz) - 校对:[784909593](https://github.com/784909593) -> 通过这 5个诀窍和技巧来将那些运行缓慢,低效的 go 代码变成精简,高效,快速的机器代码。 +> 通过这 5 个诀窍和技巧来将那些运行缓慢,低效的 go 代码变成精简,高效,快速的机器代码。 ![Go性能加速器:你需要知道的5个诀窍和技巧](../static/images/2023/w12_Go_Performance_Boosters_The_Top_5_Tips_and_Tricks_You_Need_to_Know/1_7pKdwB_c5boKS_235L9DRQ.png) -Go性能加速器:你需要知道的5个诀窍和技巧 +Go 性能加速器:你需要知道的 5 个诀窍和技巧 各位 Go 大师和初学者们,你们是否已经厌倦了那些慢得让你想要抓狂的 Go 应用程序?别担心,我们有解决方案。 -在这篇文章中,我将分享将 Go 应用程序变成精简、高效的前5个诀窍和技巧。 +在这篇文章中,我将分享将 Go 应用程序变成精简、高效的前 5 个诀窍和技巧。 所以拿杯咖啡,放松一下,准备把你的 Go 技能提升到更高的水平。 -## 1. 避免使用反射。 +## 1. 避免使用反射 反射是 Go 中一个强大的特性,它允许程序在运行时自我检查和修改自身的结构和行为。 你可以使用反射来确定一个值的类型,访问其字段,并调用其方法。 @@ -39,10 +39,10 @@ func main() { ``` **但是!** 反射是在运行时进行值的自我检查和操作,而不是编译时。 -Go运行时必须执行额外的工作来确定反射值的类型和结构,这可能会增加开销并减慢程序速度。 +Go 运行时必须执行额外的工作来确定反射值的类型和结构,这可能会增加开销并减慢程序速度。 反射还会使代码更难以阅读和理解而使影响生产力受到影响。 -## 2. 避免使用字符串拼接。 +## 2. 避免使用字符串拼接 通常使用 `bytes.Buffer` 类型来构建字符串比使用 `+` 操作符连接字符串更有效率。 @@ -94,19 +94,19 @@ fmt.Println(s) 我已经比较了这两种解决方案,结果如下: - 使用 `bytes.Buffer` 比使用字符串拼接快得多,在某些情况下性能提升超过 250 倍。 -- 使用 `strings.Builder` 大约比 `bytes.Buffer` 快1.5倍。 +- 使用 `strings.Builder` 大约比 `bytes.Buffer` 快 1.5 倍。 需要注意的是,实际的性能提升可能因为特定的 CPU 和代码运行环境等因素而有所差异。 -> strings.Builder比bytes.Buffer更快的原因有几个。 +> strings.Builder 比 bytes.Buffer 更快的原因有几个。 这是因为 `strings.Builder` 专门针对字符串的构建进行了优化。相比之下,`bytes.Buffer` 是一个更通用的缓冲区,可以用于构建任何类型的数据,但它可能没有像 `strings.Builder` 那样专门优化字符串构建的性能。 -## 3. 预分配切片和 map 的空间。 +## 3. 预分配切片和 map 的空间 -在Go中,为预期容纳的元素数量适当分配切片的容量可以提高性能。 +在 Go 中,为预期容纳的元素数量适当分配切片的容量可以提高性能。 这是因为分配具有更大容量的切片可以减少在添加元素时需要调整切片大小的次数。 @@ -133,11 +133,11 @@ func main() { ``` -是的,通过预分配,我们能够将速度提升3倍。 +是的,通过预分配,我们能够将速度提升 3 倍。 我已经在一篇关于切片的文章中对于[为什么预分配更快](https://medium.com/@func25/go-secret-slice-a-deep-dive-into-slice-6bd7b0b70ec4)写了一个详细的解释,你可以直接点击链接查看。 -## 4. 避免使用只有一个具体类型的接口。 +## 4. 避免使用只有一个具体类型的接口 如果你知道一个接口只会有一个具体类型,你可以直接使用该具体类型,以避免接口的开销。 @@ -166,7 +166,7 @@ func main() { ``` -使用接口的耗时为358微秒,而使用具体类型的耗时为342微秒。 +使用接口的耗时为 358 微秒,而使用具体类型的耗时为 342 微秒。 需要注意的是,只有当你确信一个接口只会有一个具体类型时,才应该使用这种技术。 diff --git a/2023/w15_Building_Basic_Event_Scheduler_in_Go.md b/2023/w15_Building_Basic_Event_Scheduler_in_Go.md index 365b94f..159d951 100644 --- a/2023/w15_Building_Basic_Event_Scheduler_in_Go.md +++ b/2023/w15_Building_Basic_Event_Scheduler_in_Go.md @@ -4,7 +4,7 @@ - 译者:[lsj1342](https://github.com/lsj1342) - 校对:[cvley](https://github.com/cvley) -## Go构建基础的事件调度器 +## Go 构建基础的事件调度器 ![](https://github.com/gocn/translator/raw/master/static/images/2023/w15_Building_Basic_Event_Scheduler_in_Go//1_gBs7tyig8N5eeHNMOwIG8w.webp) 当我们需要在一段时间后的特定时间或间隔运行任务时,我们需要使用任务调度系统来运行任务:例如发送电子邮件、推送通知、午夜关闭账户、清空表格等 @@ -66,7 +66,7 @@ func NewScheduler(db *sql.DB, listeners Listeners) Scheduler { } ``` -在第 8 行到第 13 行中,我们通过将sql.DB实例和初始侦听器传递给调度程序来创建新的调度程序。 +在第 8 行到第 13 行中,我们通过将 sql.DB 实例和初始侦听器传递给调度程序来创建新的调度程序。 现在,我们实现调度函数,并将我们的事件插入到 `jobs` 表中; ```go @@ -200,12 +200,12 @@ func main() { 在第 13 行到第 16 行中,我们将侦听函数绑定到事件 `SendEmail` 和 `PayBills`上,以便在发生新事件时调用这些函数。 -在 22行 和 32 到 37 行中,我们添加了中断信号(os.Interrupt)通道,当程序中发生中断时,我们执行 19 行中的上下文取消函数。 +在 22 行 和 32 到 37 行中,我们添加了中断信号(os.Interrupt)通道,当程序中发生中断时,我们执行 19 行中的上下文取消函数。 从第 26 行到第 30 行,我们定义事件调度程序、运行轮询函数并将在一分钟后运行 `SendEmail` ,两分钟后运行 `PayBills`。 程序的输出将如下所示; -``` +```plain 2021/01/16 11:58:49 💾 Seeding database with table... 2021/01/16 11:58:49 🚀 Scheduling event SendEmail to run at 2021-01-16 11:59:49.344904505 +0545 +0545 m=+60.004623549 diff --git a/2023/w17_Dont_write_clean_code_write_CRISP_code.md b/2023/w17_Dont_write_clean_code_write_CRISP_code.md index 877bbb5..ab32902 100644 --- a/2023/w17_Dont_write_clean_code_write_CRISP_code.md +++ b/2023/w17_Dont_write_clean_code_write_CRISP_code.md @@ -20,7 +20,7 @@ 你的代码可能有很多其他优点,但如果它不是*正确的*,那么这些优点实际上并不重要。而且,如果我们愿意放弃正确性,我们可以相对容易地获得任何其他属性。在编写代码时,正确性是最重要的因素之一,因为一个程序只有在它能够正确地完成它的任务时才有意义。 ->*正确性是首要的品质。如果一个系统不能够完成它应该完成的任务,那么它的其他方面——无论它有多快,有多好的用户界面——都不重要。但这说起来容易做起来难* +> *正确性是首要的品质。如果一个系统不能够完成它应该完成的任务,那么它的其他方面——无论它有多快,有多好的用户界面——都不重要。但这说起来容易做起来难* >*——Bertrand Meyer,《面向对象软件构造》* 虽然这个观点似乎很显然,不值得一提,但如果存在不正确的代码,而我认为确实存在,那么这个观点肯定值得强调,因为显然有人没有注意到这个重要性。 diff --git a/2023/w19_Go_Developer_Survey_2023_Q1_Results.md b/2023/w19_Go_Developer_Survey_2023_Q1_Results.md index 4d61e90..0d89ffc 100644 --- a/2023/w19_Go_Developer_Survey_2023_Q1_Results.md +++ b/2023/w19_Go_Developer_Survey_2023_Q1_Results.md @@ -1,4 +1,4 @@ -# 2023年第一季度 Go 开发者调查结果 +# 2023 年第一季度 Go 开发者调查结果 - 原文地址:https://go.dev/blog/survey2023-q1-results - 原文作者:***Alice Merrick*** @@ -8,41 +8,41 @@ ## 感谢调查参与者给我们带来了这些洞察! -我们非常高兴与您分享2023年1月份 Go 开发者调查的结果。感谢 5844 位回应者与我们分享他们如何使用 Go,他们在使用 Go 时面临的最大挑战,以及他们对未来改进的首要课题。这些结果帮助 Go 团队将我们的工作重点放在社区最关心的领域,我们希望这些洞察也能帮助其他为 Go 生态系统做出贡献和支持的人了解相关信息。 +我们非常高兴与您分享 2023 年 1 月份 Go 开发者调查的结果。感谢 5844 位回应者与我们分享他们如何使用 Go,他们在使用 Go 时面临的最大挑战,以及他们对未来改进的首要课题。这些结果帮助 Go 团队将我们的工作重点放在社区最关心的领域,我们希望这些洞察也能帮助其他为 Go 生态系统做出贡献和支持的人了解相关信息。 ### 主要发现 - **新手 Go 开发者对 Web 开发感兴趣。**我们今年新引入了一个基于自我识别的经验水平的划分。新手表达出了一些与其他经验级别相比的有趣差异。最值得注意的是他们对使用 Go 进行 web 开发的兴趣更大。 -- **错误处理和学习是参与者面临的最大挑战。**历史上,缺乏泛型是使用Go的最大挑战,但自从引入泛型后,我们看到关于泛型的评论有所下降。现在最常被报告的挑战是关于错误处理(与可读性和冗长性有关)和学习最佳实践的困难。 +- **错误处理和学习是参与者面临的最大挑战。**历史上,缺乏泛型是使用 Go 的最大挑战,但自从引入泛型后,我们看到关于泛型的评论有所下降。现在最常被报告的挑战是关于错误处理(与可读性和冗长性有关)和学习最佳实践的困难。 - **优化指南被认为是最有价值的提升 Go 性能的方式。**当被问及如何在 Go 的编译和运行时的各种改进上投入资源时,参与者最多的选择是优化指南,而不是具体的性能改进,这证明了在这一领域,文档的价值有多大。 - **管理依赖和版本是开源 Go 模块维护者面临的最大挑战。**开源模块维护者在保持他们的依赖关系更新和避免因版本和突发改变引起的中断方面面临挑战。这是我们将进一步探索的领域,以帮助维护者提供稳定和健康的生态系统。 ### 如何阅读调查结果 -在这篇文章中,我们使用调查问卷的图表来为我们的发现提供证据支持。所有这些图表都使用类似的格式。标题是调查参与者看到的确切问题。除非另有说明,问题都是选择题,参与者只能选择一个答案;每个图表的子标题将告诉你问题是否允许多个答案,或者是开放式文本框而不是选择题。对于开放式文本回答的图表,Go 团队的成员阅读并手动分类了所有的回答。许多开放式问题引发了各种各样的回答;为了保持图表的合理大小,我们将它们压缩到了前10-15个主题,其他的主题都归类为“其他”。在适用的地方,我们还包含了一个“无”的类别。 +在这篇文章中,我们使用调查问卷的图表来为我们的发现提供证据支持。所有这些图表都使用类似的格式。标题是调查参与者看到的确切问题。除非另有说明,问题都是选择题,参与者只能选择一个答案;每个图表的子标题将告诉你问题是否允许多个答案,或者是开放式文本框而不是选择题。对于开放式文本回答的图表,Go 团队的成员阅读并手动分类了所有的回答。许多开放式问题引发了各种各样的回答;为了保持图表的合理大小,我们将它们压缩到了前 10-15 个主题,其他的主题都归类为“其他”。在适用的地方,我们还包含了一个“无”的类别。 为了帮助读者理解每项发现背后的证据的权重,我们在图表中包含了显示回答的 95% 置信区间的误差条;条状越窄,置信度越大。有时,两个或更多的回答的误差条可能会重叠,这意味着这些回答的相对顺序在统计上没有意义(也就是说,这些回答实际上是平手的)。每个图表的右下角显示了在图表中包含的人数,以“n = [回答者人数]”的形式。 ### 关于方法论的说明 -大多数调查回答者通过在 [Go 博客](https://go.dev/blog)、[@golang推特](https://twitter.com/golang)或其他 Go 社交渠道上的链接“自我选择”参加了这次调查。那些不关注这些渠道的人可能会与那些密切关注这些渠道的人有不同的回答。大约四分之一的回答者是随机抽样的,也就是说他们在 VS Code 中看到调查提示后回答了调查(在2023年1月18日至2月8日期间使用VS Code Go插件的每个人都有10%的机会收到这个随机提示)。这个随机抽样的群体帮助我们将这些发现推广到更大的 Go 开发者社区。大多数调查问题在这两个群体之间没有显示出有意义的差异,但在少数有重要差异的情况下,读者会看到将回答分为“随机样本”和“自选样本”群体的图表。 +大多数调查回答者通过在 [Go 博客](https://go.dev/blog)、[@golang 推特](https://twitter.com/golang)或其他 Go 社交渠道上的链接“自我选择”参加了这次调查。那些不关注这些渠道的人可能会与那些密切关注这些渠道的人有不同的回答。大约四分之一的回答者是随机抽样的,也就是说他们在 VS Code 中看到调查提示后回答了调查(在 2023 年 1 月 18 日至 2 月 8 日期间使用 VS Code Go 插件的每个人都有 10%的机会收到这个随机提示)。这个随机抽样的群体帮助我们将这些发现推广到更大的 Go 开发者社区。大多数调查问题在这两个群体之间没有显示出有意义的差异,但在少数有重要差异的情况下,读者会看到将回答分为“随机样本”和“自选样本”群体的图表。 ## 深入研究不同群体的回答者 -我们的受访者人口统计数据与[上次调查](https://go.dev/blog/survey2022-q2-results)相比没有显著变化。与以往的周期一致,Go 主要用于技术行业,约80%的受访者表示他们在工作中使用 Go 编程。总体而言,调查受访者在过去一年中对 Go 感到满意,92%的人表示他们在某种程度上或非常满意。 +我们的受访者人口统计数据与[上次调查](https://go.dev/blog/survey2022-q2-results)相比没有显著变化。与以往的周期一致,Go 主要用于技术行业,约 80%的受访者表示他们在工作中使用 Go 编程。总体而言,调查受访者在过去一年中对 Go 感到满意,92%的人表示他们在某种程度上或非常满意。 ![Bar chart showing where respondents use Go](https://go.dev/blog/survey2023q1/where.svg) ![Bar chart showing proportion of satisfied respondents](https://go.dev/blog/survey2023q1/csat.svg) -相比其他语言,我们的受访者花费了很多时间在 Go 编程上。大约三分之一的受访者甚至维护着一个开源的 Go 模块。我们认识到我们的调查对象是那些成功采用Go、经常使用 Go 并且大多数人使用 Go 都感到满意的人。为了确定满足社区需求的潜在差距,我们查看不同的受访者子群体,看看他们可能如何不同地使用 Go 或有不同的优先事项。例如,今年我们查看了不同样本来源(即 Go 博客或通过VS Code插件)、不同的工作角色、组织规模和 Go 经验水平之间的差异。最有趣的差异在于经验水平之间。 +相比其他语言,我们的受访者花费了很多时间在 Go 编程上。大约三分之一的受访者甚至维护着一个开源的 Go 模块。我们认识到我们的调查对象是那些成功采用 Go、经常使用 Go 并且大多数人使用 Go 都感到满意的人。为了确定满足社区需求的潜在差距,我们查看不同的受访者子群体,看看他们可能如何不同地使用 Go 或有不同的优先事项。例如,今年我们查看了不同样本来源(即 Go 博客或通过 VS Code 插件)、不同的工作角色、组织规模和 Go 经验水平之间的差异。最有趣的差异在于经验水平之间。 ## 来自新手调查对象的洞察 ![Bar chart of years of experience using Go](https://go.dev/blog/survey2023q1/go_exp.svg) -过去,我们以回答者使用 Go 的时间(以月/年为单位)为根据,以洞察不同经验级别的结果如何变化。今年我们尝试了一个新的分段问题,“你的Go经验级别是什么?”,看看自我认定相比将各种时间间隔混合在一起,是否可能是一种检查 Go 经验更有用的方式。由于像“初学者”或“专家”这样的分类术语可能因人而异,我们提供了描述以帮助使这些分类更具客观性。选项包括: +过去,我们以回答者使用 Go 的时间(以月/年为单位)为根据,以洞察不同经验级别的结果如何变化。今年我们尝试了一个新的分段问题,“你的 Go 经验级别是什么?”,看看自我认定相比将各种时间间隔混合在一起,是否可能是一种检查 Go 经验更有用的方式。由于像“初学者”或“专家”这样的分类术语可能因人而异,我们提供了描述以帮助使这些分类更具客观性。选项包括: - 了解:我知道 Go,但是如果没有帮助,我不能写一个简单的 Go 程序 - 初学者:我可以在可能需要帮助的情况下完成 Go 的简单编程项目 @@ -55,45 +55,45 @@ using Go](https://go.dev/blog/survey2023q1/exp_level.svg) 我们发现回答者使用 Go 的时间长短与他们自我认定的经验等级之间存在中等程度的相关性(⍴ = .66)。这意味着,虽然经验等级尺度与时间尺度类似,但它可能会给我们提供一些关于不同经验等级的回答者之间的差异的新洞见。例如,一个回答者花在 Go 编程上的时间比例与他们花在其他语言编程上的时间比较,与他们自我认定的经验等级的相关性比他们使用 Go 的时间长短的相关性更强。 -在我们使用这种分段的分析中,我们通常会排除“了解”类别,因为他们被认为没有回答问题所必需的经验,并且只占回答者的约1%。 +在我们使用这种分段的分析中,我们通常会排除“了解”类别,因为他们被认为没有回答问题所必需的经验,并且只占回答者的约 1%。 -### 初学回答者更可能比更有经验的回答者更喜欢Windows +### 初学回答者更可能比更有经验的回答者更喜欢 Windows -我们随机抽样的群体中,初学者的比例比自选群体高,这表明我们不常听到的新 Gopher 更多。因为他们是通过 Go VS Code 插件进行抽样的,我们可能期望这个群体更喜欢使用 VS Code 或比其他经验等级更喜欢在 Windows 上开发。虽然这是真的,但无论他们是否通过VS Code 插件回答,初学者也更可能在 Windows 上开发。 +我们随机抽样的群体中,初学者的比例比自选群体高,这表明我们不常听到的新 Gopher 更多。因为他们是通过 Go VS Code 插件进行抽样的,我们可能期望这个群体更喜欢使用 VS Code 或比其他经验等级更喜欢在 Windows 上开发。虽然这是真的,但无论他们是否通过 VS Code 插件回答,初学者也更可能在 Windows 上开发。 ![Bar chart of levels of experience using Go for self-selected and random samples](https://go.dev/blog/survey2023q1/exp_level_s.svg) ![Bar chart of editor preference broken down by experience levels for self-selected group only](https://go.dev/blog/survey2023q1/editor_self_select_exp.svg) ![Bar chart of levels of experience using Go](https://go.dev/blog/survey2023q1/os_dev_exp_s.svg) -我们在更高的经验等级中看不到更多 Windows 用户的比例,可能有多种原因。例如,Windows用户可能更容易遇到困难并停止使用Go,或者可能存在与 Go 无关的操作系统使用的更广泛的趋势。无论如何,我们应该在未来关于开始使用 Go 的研究中包括更多的Windows 用户,以确保我们提供一个包容性的入门体验。 +我们在更高的经验等级中看不到更多 Windows 用户的比例,可能有多种原因。例如,Windows 用户可能更容易遇到困难并停止使用 Go,或者可能存在与 Go 无关的操作系统使用的更广泛的趋势。无论如何,我们应该在未来关于开始使用 Go 的研究中包括更多的 Windows 用户,以确保我们提供一个包容性的入门体验。 -### 不同经验等级的用户当前如何使用Go(以及他们希望使用的其他领域) +### 不同经验等级的用户当前如何使用 Go(以及他们希望使用的其他领域) ![Bar chart of use cases](https://go.dev/blog/survey2023q1/go_app.svg) ![Bar chart of use cases broken down by experience level](https://go.dev/blog/survey2023q1/go_app_exp.svg) -根据调查者目前如何使用Go,更有经验的 Gopher 倾向于使用 Go 进行更多类型的应用。例如,平均每个专家至少在四个领域使用Go,而平均每个初学者只在两个领域使用Go。这就是为什么在每个使用案例中,使用 Go 的初学者和专家的比例有很大的差异。然而,前两大应用,API / RPC服务和CLI,都是所有经验等级的主要用例。 +根据调查者目前如何使用 Go,更有经验的 Gopher 倾向于使用 Go 进行更多类型的应用。例如,平均每个专家至少在四个领域使用 Go,而平均每个初学者只在两个领域使用 Go。这就是为什么在每个使用案例中,使用 Go 的初学者和专家的比例有很大的差异。然而,前两大应用,API / RPC 服务和 CLI,都是所有经验等级的主要用例。 -我们在 GUI 和 Website / Web 服务(返回HTML)中看到更有趣的趋势。所有经验等级都以大约相同的比例使用 Go 进行桌面/ GUI 应用。这为我们提供了证据,GUI 的需求并不仅仅来自寻找有趣的入门项目的新 Gopher,而是来自整个经验谱。 +我们在 GUI 和 Website / Web 服务(返回 HTML)中看到更有趣的趋势。所有经验等级都以大约相同的比例使用 Go 进行桌面/ GUI 应用。这为我们提供了证据,GUI 的需求并不仅仅来自寻找有趣的入门项目的新 Gopher,而是来自整个经验谱。 -返回 HTML 的 Website / Web 服务显示出类似的趋势。一种解释可能是这是某人开始 Go 之旅早期的一个常见用例(因为它是初学者中最常用的前3个),或者初学者更可能在返回 HTML 的 Website / Web 服务上工作。在调查的后期,我们问回答者,“在哪个领域(如果有的话)你没有使用 Go,但最想使用?”虽然许多回答者(29%)表示他们已经在他们希望的地方使用Go,但扩大使用的前两个领域是GUI / 桌面和AI / ML应用。这在不同组织规模和工作角色的群体中保持一致,但在经验等级上并非如此。初学者最想更多使用 Go的领域是网站/返回HTML的 websites / web 服务 。 +返回 HTML 的 Website / Web 服务显示出类似的趋势。一种解释可能是这是某人开始 Go 之旅早期的一个常见用例(因为它是初学者中最常用的前 3 个),或者初学者更可能在返回 HTML 的 Website / Web 服务上工作。在调查的后期,我们问回答者,“在哪个领域(如果有的话)你没有使用 Go,但最想使用?”虽然许多回答者(29%)表示他们已经在他们希望的地方使用 Go,但扩大使用的前两个领域是 GUI / 桌面和 AI / ML 应用。这在不同组织规模和工作角色的群体中保持一致,但在经验等级上并非如此。初学者最想更多使用 Go 的领域是网站/返回 HTML 的 websites / web 服务 。 ![Bar chart of levels of experience using Go](https://go.dev/blog/survey2023q1/app_opportunities_exp.svg) -在一个开放式文本问题中,有29位回答者表示他们希望使用 Go 进行返回HTML的 websites / web 服务,其中12位表示他们被阻碍了,因为其他语言有更好支持这个用例的框架。更有经验的 Go 开发者可能在其他语言已经有满足这些需求的框架时,不会尝试或期望使用Go进行这个用例。正如一位回答者所说, +在一个开放式文本问题中,有 29 位回答者表示他们希望使用 Go 进行返回 HTML 的 websites / web 服务,其中 12 位表示他们被阻碍了,因为其他语言有更好支持这个用例的框架。更有经验的 Go 开发者可能在其他语言已经有满足这些需求的框架时,不会尝试或期望使用 Go 进行这个用例。正如一位回答者所说, > “通常在其他语言如 PHP 或 Ruby 中实现这个更容易。部分原因是这些语言中存在的优秀框架。” -初学者对 web 开发的兴趣可能与他们使用 JavaScript / TypeScript 的情况有关。初学者花费在 JavaScript / TypeScript 上的时间比更有经验的回答者多。对 web 的高度兴趣可能与初学者目前在其他语言上工作的内容有关,或者可能仅仅表明对 web 技术感兴趣。在未来,我们希望更多地了解这个用例,以及我们如何可以帮助新的 Gopher 开始在对他们最有用的领域使用Go。 +初学者对 web 开发的兴趣可能与他们使用 JavaScript / TypeScript 的情况有关。初学者花费在 JavaScript / TypeScript 上的时间比更有经验的回答者多。对 web 的高度兴趣可能与初学者目前在其他语言上工作的内容有关,或者可能仅仅表明对 web 技术感兴趣。在未来,我们希望更多地了解这个用例,以及我们如何可以帮助新的 Gopher 开始在对他们最有用的领域使用 Go。 ![Bar chart of levels of experience using Go](https://go.dev/blog/survey2023q1/language_time_exp.svg) ## 调查者面临一系列长期挑战 -在每个调查周期,我们都会询问回应者在使用 Go 时最大的挑战是什么。从历史来看,缺乏泛型是最常被引用的挑战——例如,这是2020年最常见的回应,约18%的回答者提到了这一点。自从引入了泛型以来,错误处理(12%)和学习/最佳实践/文档(11%)已经在一系列长期问题中脱颖而出,而不是任何单一问题变得更加频繁。 +在每个调查周期,我们都会询问回应者在使用 Go 时最大的挑战是什么。从历史来看,缺乏泛型是最常被引用的挑战——例如,这是 2020 年最常见的回应,约 18%的回答者提到了这一点。自从引入了泛型以来,错误处理(12%)和学习/最佳实践/文档(11%)已经在一系列长期问题中脱颖而出,而不是任何单一问题变得更加频繁。 ![Bar chart of biggest challenges](https://go.dev/blog/survey2023q1/text_biggest_challenge.svg) @@ -108,13 +108,13 @@ biggest challenges](https://go.dev/blog/survey2023q1/text_biggest_challenge.svg) ### 学习最佳实践的挑战 -> “有效地使用Go。容易学习,难以掌握。” +> “有效地使用 Go。容易学习,难以掌握。” -我们听到Go语言易于学习,而且[之前的一项调查显示](https://go.dev/blog/survey2020-results#TOC_6.2),超过70%的回应者在第一年内使用 Go 就感到了效率,但是学习 Go 的最佳实践被提出作为使用 Go 的最大挑战之一。今年的回应者告诉我们,关于**代码结构**和**推荐工具和库**的最佳实践文档不足,这给初学者和团队保持代码一致性带来了挑战。对于来自其他编程范式的人来说,学习写符合惯用方式的 Go 尤其具有挑战性。对 Go 经验更丰富的回应者证实,当开发者不遵循编写符合惯用方式的 Go 的最佳实践时,这会损害共享项目的一致性和质量。 +我们听到 Go 语言易于学习,而且[之前的一项调查显示](https://go.dev/blog/survey2020-results#TOC_6.2),超过 70%的回应者在第一年内使用 Go 就感到了效率,但是学习 Go 的最佳实践被提出作为使用 Go 的最大挑战之一。今年的回应者告诉我们,关于**代码结构**和**推荐工具和库**的最佳实践文档不足,这给初学者和团队保持代码一致性带来了挑战。对于来自其他编程范式的人来说,学习写符合惯用方式的 Go 尤其具有挑战性。对 Go 经验更丰富的回应者证实,当开发者不遵循编写符合惯用方式的 Go 的最佳实践时,这会损害共享项目的一致性和质量。 ### 模块维护者面临的最大挑战 -Go模块维护者是 Go 社区的关键成员,帮助发展并维持我们的包生态系统的健康。今年我们计划与模块维护者进行研究,以确定支持包生态系统的稳定性和增长,以及寻找在组织内推广 Go 的机会。为了为这项研究提供信息,我们在调查中引入了一个问题,以了解当前开源维护者面临的最大挑战。 +Go 模块维护者是 Go 社区的关键成员,帮助发展并维持我们的包生态系统的健康。今年我们计划与模块维护者进行研究,以确定支持包生态系统的稳定性和增长,以及寻找在组织内推广 Go 的机会。为了为这项研究提供信息,我们在调查中引入了一个问题,以了解当前开源维护者面临的最大挑战。 ![Bar chart of challenges for open source module maintainers](https://go.dev/blog/survey2023q1/text_maintainer_challenge.svg) @@ -130,7 +130,7 @@ for open source module maintainers](https://go.dev/blog/survey2023q1/text_deploy ## 社区优先级:受访者最关心的问题 -今年我们使用了一种在以往调查中使用过的基于 buy-a-feature 优先级问题。我们给予受访者10个“gopher币”,并让他们将这些币分配给他们希望看到改进的领域。受访者被随机分配三个可能的问题,来自于七个与工具、安全或编译器和运行时相关的项目。这种方法让我们能够在不让受访者承受三组认知要求高的优先级问题的负担的情况下,询问每个重点领域相关的项目。 +今年我们使用了一种在以往调查中使用过的基于 buy-a-feature 优先级问题。我们给予受访者 10 个“gopher 币”,并让他们将这些币分配给他们希望看到改进的领域。受访者被随机分配三个可能的问题,来自于七个与工具、安全或编译器和运行时相关的项目。这种方法让我们能够在不让受访者承受三组认知要求高的优先级问题的负担的情况下,询问每个重点领域相关的项目。 在这个练习的最后,我们给了受访者一个开放文本提示,让他们告诉我们他们认为在明年 Go 团队应该优先考虑的任何领域,无论他们在哪些项目上花费了他们的币。例如,如果一个受访者被展示了安全部分,但他们对安全并不是很关心,他们仍然有机会在开放文本区域中告诉我们这一点。 @@ -141,8 +141,8 @@ for open source module maintainers](https://go.dev/blog/survey2023q1/text_deploy * pkg.go.dev 识别出维护不良的包(例如,对问题无反应,未能更新其依赖关系,长期保持易受攻击) * pkg.go.dev 识别出进行了破坏性 API 更改的包(即,当升级这些包到新版本时,需要修复使用这些 API 的地方) * 支持在 govulncheck 中抑制漏洞 -* 跟踪敏感数据如何流经 Go 程序的工具(检测PII泄露) -* 安全最佳实践指南(例如,如何选择和更新依赖项;如何设置模糊测试、漏洞检查和线程消毒器;如何使用crypto) +* 跟踪敏感数据如何流经 Go 程序的工具(检测 PII 泄露) +* 安全最佳实践指南(例如,如何选择和更新依赖项;如何设置模糊测试、漏洞检查和线程消毒器;如何使用 crypto) * 默认安全的 Web 和 SQL 库,帮助用户避免在 web 服务器代码中引入漏洞 * 符合 FIPS-140 标准的密码学库 @@ -155,13 +155,13 @@ respondents spent the most on security issues](https://go.dev/blog/survey2023q1/ 我们在这个问题中包含的项目是受 VS Code 插件用户反馈的启发。我们想知道哪些工具和 IDE 改进对可能使用其他 IDE 或编辑器的更广泛的受众最有帮助。 -* 更好的重构工具(例如,支持自动代码转换:重命名,函数提取,API迁移等) -* 在你的代码编辑器/IDE 中更好地支持测试(例如,健壮且可扩展的Test Explorer UI,第三方测试框架,子测试支持,代码覆盖率) -* 在你的代码编辑器/IDE中更好地支持多模块工作(例如,编辑模块A和B,其中模块A依赖于模块B) -* 在pkg.go.dev中的依赖性洞察(例如,漏洞,破坏性变化,评分卡) -* 在你的代码编辑器/IDE中支持依赖性洞察(例如,漏洞,破坏性变化,评分卡) +* 更好的重构工具(例如,支持自动代码转换:重命名,函数提取,API 迁移等) +* 在你的代码编辑器/IDE 中更好地支持测试(例如,健壮且可扩展的 Test Explorer UI,第三方测试框架,子测试支持,代码覆盖率) +* 在你的代码编辑器/IDE 中更好地支持多模块工作(例如,编辑模块 A 和 B,其中模块 A 依赖于模块 B) +* 在 pkg.go.dev 中的依赖性洞察(例如,漏洞,破坏性变化,评分卡) +* 在你的代码编辑器/IDE 中支持依赖性洞察(例如,漏洞,破坏性变化,评分卡) * 支持使用新的模块路径发布模块(例如,仓库所有权移交) -* 在你的代码编辑器/IDE中支持找到实现了一个接口的类型以及由一个类型实现的接口 +* 在你的代码编辑器/IDE 中支持找到实现了一个接口的类型以及由一个类型实现的接口 ![Bar chart of where respondents spent the most on tooling](https://go.dev/blog/survey2023q1/prioritization_tooling.svg) @@ -170,29 +170,29 @@ respondents spent the most on tooling](https://go.dev/blog/survey2023q1/prioriti ### 编译器和运行时 -我们在这一部分的关键问题是,想要确定受访者是希望默认有更好的性能,更好的优化工具,还是只是希望更好地理解如何编写高性能的Go 代码。 +我们在这一部分的关键问题是,想要确定受访者是希望默认有更好的性能,更好的优化工具,还是只是希望更好地理解如何编写高性能的 Go 代码。 * 降低计算成本 * 减少内存使用 * 减少二进制文件大小 * 减少构建时间 * 更好的性能调试工具 -* 优化指南(如何提高性能和降低成本,包括Go的实现和性能调试工具) -* 在交叉编译时更好地支持使用cgo +* 优化指南(如何提高性能和降低成本,包括 Go 的实现和性能调试工具) +* 在交叉编译时更好地支持使用 cgo ![Bar chart of where respondents spent the most on compiler and runtime improvements](https://go.dev/blog/survey2023q1/prioritization_core.svg) -这个列表中得到最多资金支持的是优化指南。这在组织规模、工作角色和经验水平上都是一致的。我们还问了一个关于受访者是否对资源成本有所担忧的额外问题。大多数受访者(55%)表示他们没有任何成本担忧,但是那些对资源成本有所担忧的人在降低计算成本和内存成本上花费了更多的"gophercoins"(平均为2.0),比那些没有担忧的人多。然而,即使是那些对资源成本有所担忧的人,他们在优化指南上的花费也差不多(平均为1.9个"gophercoins")。这是一个强烈的信号,提供指导 Go 开发者理解和优化 Go 性能的指南,目前比额外的编译器和运行时性能改进更有价值。 +这个列表中得到最多资金支持的是优化指南。这在组织规模、工作角色和经验水平上都是一致的。我们还问了一个关于受访者是否对资源成本有所担忧的额外问题。大多数受访者(55%)表示他们没有任何成本担忧,但是那些对资源成本有所担忧的人在降低计算成本和内存成本上花费了更多的"gophercoins"(平均为 2.0),比那些没有担忧的人多。然而,即使是那些对资源成本有所担忧的人,他们在优化指南上的花费也差不多(平均为 1.9 个"gophercoins")。这是一个强烈的信号,提供指导 Go 开发者理解和优化 Go 性能的指南,目前比额外的编译器和运行时性能改进更有价值。 ## 总结 -感谢你加入我们一起回顾2023年第一次开发者调查的结果!理解开发者的经验和挑战有助于我们确定如何最好地服务 Go 社区。我们发现的一些特别有用的收获包括: +感谢你加入我们一起回顾 2023 年第一次开发者调查的结果!理解开发者的经验和挑战有助于我们确定如何最好地服务 Go 社区。我们发现的一些特别有用的收获包括: -* Go初学者对网页开发有更高的亲和力,比其他经验水平的受访者更为如此。这是我们希望进一步探索的一个领域,以确保我们满足新的Go开发者的需求。 -* 默认安全、安全和优化的最佳实践指导,以及在IDE中更多的重构帮助,将是对社区的有价值的补充。 -* 错误处理是社区的高优先级问题,它在冗长性和可调试性上带来了挑战。Go团队目前没有公开提案,但正在继续探索改进错误处理的选项。 +* Go 初学者对网页开发有更高的亲和力,比其他经验水平的受访者更为如此。这是我们希望进一步探索的一个领域,以确保我们满足新的 Go 开发者的需求。 +* 默认安全、安全和优化的最佳实践指导,以及在 IDE 中更多的重构帮助,将是对社区的有价值的补充。 +* 错误处理是社区的高优先级问题,它在冗长性和可调试性上带来了挑战。Go 团队目前没有公开提案,但正在继续探索改进错误处理的选项。 * 对受访者来说,入门和学习最佳实践是最大的挑战之一,将是未来研究的领域。 -* 对于Go模块维护者来说,保持依赖项更新、模块版本控制,以及识别或避免破坏性变化是最大的挑战。帮助维护者提供稳定和健康的生态系统是另一个感兴趣的进一步 UX 研究的话题。 +* 对于 Go 模块维护者来说,保持依赖项更新、模块版本控制,以及识别或避免破坏性变化是最大的挑战。帮助维护者提供稳定和健康的生态系统是另一个感兴趣的进一步 UX 研究的话题。 再次感谢所有参与并为这次调查做出贡献的人——没有你们我们无法完成这项工作。我们希望在今年晚些时候的下一次调查中再见到你。 \ No newline at end of file diff --git a/2023/w20_How_to_troubleshoot_memory_leaks_in_Go_with_Grafana_Pyroscope.md b/2023/w20_How_to_troubleshoot_memory_leaks_in_Go_with_Grafana_Pyroscope.md index 51d5033..3b70471 100644 --- a/2023/w20_How_to_troubleshoot_memory_leaks_in_Go_with_Grafana_Pyroscope.md +++ b/2023/w20_How_to_troubleshoot_memory_leaks_in_Go_with_Grafana_Pyroscope.md @@ -75,7 +75,7 @@ func longRunningTask(responses chan []byte) { } ``` -上面的这段简单的代码,在连接到并行数据库的HTT服务器过程中,造成了内存泄露。因为 HTTP 请求不等待响应 channel,所以 longRunningTask 永远处于阻塞状态,从而泄露了用它创建的协程和资源。 +上面的这段简单的代码,在连接到并行数据库的 HTT 服务器过程中,造成了内存泄露。因为 HTTP 请求不等待响应 channel,所以 longRunningTask 永远处于阻塞状态,从而泄露了用它创建的协程和资源。 为了防止这种情况的发生,我们需要正确终止协程。我们可以使用各种技术来完成,比如使用 channel 来通知什么时候应该退出协程,或是使用 `context.Cancel` 将取消信号传播到协程,或是使用 `sync.WaitGroup` 确保所有协程在退出程序之前都已完成。 @@ -99,7 +99,7 @@ Go 中另一个常见的协程和资源泄漏的情况是没有正确释放 Time (注:虽然这篇博文主要关注 Go 语言,但 Pyroscope 也支持其他语言的内存分析。) -### 步骤1:确定内存泄漏的来源 +### 步骤 1:确定内存泄漏的来源 假设您已经进行了监控,第一步是使用日志、度量或跟踪找出系统哪个部分有问题 @@ -111,7 +111,7 @@ Go 中另一个常见的协程和资源泄漏的情况是没有正确释放 Time 一旦您确定了系统的部分和时间,您就可以使用持续分析来确定有问题的功能 -### 步骤2:将 Pyroscope 与您的应用程序集成 +### 步骤 2:将 Pyroscope 与您的应用程序集成 要开始分析 Go 应用程序,你需要在应用程序中包含我们的 Go 模块: @@ -172,7 +172,7 @@ func main() { 要了解更多信息,请查看 [Pyroscope 文档](https://pyroscope.io/docs/golang/)。 -### 步骤3:下钻关联指标 +### 步骤 3:下钻关联指标 我建议你先看看 goroutines 随时间推移,是否有任何值得关注的问题,然后切换到内存调查。 @@ -217,7 +217,7 @@ func TestA(t *testing.T) { -### 步骤5:修复内存泄漏 +### 步骤 5:修复内存泄漏 既然您可以重现并理解您的问题,那么是时候迭代修复并部署它进行测试了。您可以再次利用持续分析来监控您的变更并确认符合您的预期。 diff --git a/2023/w22_Build_your_Golang_package_docs_locally.md b/2023/w22_Build_your_Golang_package_docs_locally.md index baae914..aaed1c4 100644 --- a/2023/w22_Build_your_Golang_package_docs_locally.md +++ b/2023/w22_Build_your_Golang_package_docs_locally.md @@ -26,7 +26,7 @@ [golang/pkgsite](https://github.com/golang/pkgsite) 托管了用于渲染文档的 `pkg.go.dev` 命令行界面。 `pkgsite` 命令的[注释](https://github.com/golang/pkgsite/blob/master/cmd/pkgsite/main.go#L5)虽然简洁,但可以让我们开始: -``` +```plain // Pkgsite 提取和生成 Go 程序的文档。 // 它作为 Web 服务器运行,将文档呈现为 Web 页面。 // @@ -35,13 +35,13 @@ 我们将使用它来设置一个本地服务器版本的 `pkg.go.dev`,其中包含 Golang 包的文档渲染功能。所以让我们安装它。我在其他地方看到的建议是本地克隆此存储库,构建它并将其安装在 `$PATH` 中的某个位置,但这个 `go install` 应该就可以工作: -``` +```plain $ go install golang.org/x/pkgsite/cmd/pkgsite@latest ``` 一旦安装完成,只需将其运行在我们的 Go 包仓库所在的位置即可: -``` +```plain $ cd /path/to/go/pkg $ pkgsite 2022/06/16 10:13:55 Info: Listening on addr http://localhost:8080 @@ -55,7 +55,7 @@ $ pkgsite 现在,类似于 pkg.go.dev,你可以通过将 `go.mod` 中的模块路径附加到 URL 上来检查本地模块的文档: -``` +```plain http://localhost:8080/my/local/module/path ``` @@ -75,7 +75,7 @@ http://localhost:8080/my/local/module/path 以下是我们将使用的关键命令。为每个进程打开一个新终端,或者只是将其中一个放在后台: -``` +```plain $ browser-sync start --proxy "localhost:8080" [Browsersync] Proxying: http://localhost:8080 [Browsersync] Access URLs: @@ -90,7 +90,7 @@ $ browser-sync start --proxy "localhost:8080" 我们需要设置这个代理的原因是,我们需要一种自动通知浏览器某些东西已经改变的方式。为此,`browser-sync` 在我们的请求中注入了一小段 JavaScript 代码,它会监听一个信号以确定何时重新加载。 -``` +```plain $ nodemon --signal SIGTERM --watch my-mod.go --exec "browser-sync reload && pkgsite ." [nodemon] 2.0.16 [nodemon] to restart at any time, enter `rs` diff --git a/2023/w23_Go_Toolchains.md b/2023/w23_Go_Toolchains.md index 6dd0ef6..459ec21 100644 --- a/2023/w23_Go_Toolchains.md +++ b/2023/w23_Go_Toolchains.md @@ -11,21 +11,21 @@ 当前使用的 Go 工具链的选择取决于 `GOTOOLCHAIN` 环境设置以及主模块的 `go.mod` 文件或当前工作区的 `go.work` 文件中的 `go` 和 `toolchain` 行。当您在不同的主模块和工作区之间移动时,当前使用的工具链版本可能会有所不同,就像模块依赖版本一样。 -在标准配置中,`go` 命令在其捆绑的工具链与主模块或工作区中的 `go` 或 `toolchain` 行中的版本至少一样新时使用自己的捆绑工具链。例如,在使用 Go 1.21.3 捆绑的 `go` 命令中,主模块指定 `go 1.21.0` 时,`go` 命令使用 Go 1.21.3。当 `go` 或 `toolchain` 行比捆绑的工具链更新时,`go` 命令将运行更新的工具链。例如,在使用Go 1.21.3捆绑的go命令中,主模块声明 `go 1.21.9` 时,go 命令会查找并运行 `Go 1.21.9`。它首先在PATH中查找名为 `go1.21.9` 的程序,否则会下载并缓存 Go 1.21.9 工具链。可以禁用此自动工具链切换,但在这种情况下,为了更精确的向前兼容性,`go` 命令将拒绝在 `go` 行要求更高版本的 Go 的主模块或工作区中运行。也就是说,`go` 行设置了使用模块或工作区所需的最低 Go 版本。 +在标准配置中,`go` 命令在其捆绑的工具链与主模块或工作区中的 `go` 或 `toolchain` 行中的版本至少一样新时使用自己的捆绑工具链。例如,在使用 Go 1.21.3 捆绑的 `go` 命令中,主模块指定 `go 1.21.0` 时,`go` 命令使用 Go 1.21.3。当 `go` 或 `toolchain` 行比捆绑的工具链更新时,`go` 命令将运行更新的工具链。例如,在使用 Go 1.21.3 捆绑的 go 命令中,主模块声明 `go 1.21.9` 时,go 命令会查找并运行 `Go 1.21.9`。它首先在 PATH 中查找名为 `go1.21.9` 的程序,否则会下载并缓存 Go 1.21.9 工具链。可以禁用此自动工具链切换,但在这种情况下,为了更精确的向前兼容性,`go` 命令将拒绝在 `go` 行要求更高版本的 Go 的主模块或工作区中运行。也就是说,`go` 行设置了使用模块或工作区所需的最低 Go 版本。 -作为其他模块的依赖项的模块可能需要将最低Go版本要求设置为低于在该模块中直接工作时要使用的首选工具链。在这种情况下,`go.mod` 或 `go.work` 中的 `toolchain` 行设置了一个首选工具链,这个工具链在 `go` 命令决定使用哪个工具链时优先于 `go` 行。 +作为其他模块的依赖项的模块可能需要将最低 Go 版本要求设置为低于在该模块中直接工作时要使用的首选工具链。在这种情况下,`go.mod` 或 `go.work` 中的 `toolchain` 行设置了一个首选工具链,这个工具链在 `go` 命令决定使用哪个工具链时优先于 `go` 行。 可以将 `go` 和 `toolchain` 行视为指定模块对 Go 工具链本身的依赖关系的版本要求,就像 `go.mod` 中的 `require` 行指定对其他模块的依赖关系的版本要求一样。`go get` 命令管理 Go 工具链依赖关系,就像管理对其他模块的依赖关系一样。例如,`go get go@latest` 会更新模块以要求最新发布的 Go 工具链。 `GOTOOLCHAIN` 环境设置可以强制使用特定的 Go 版本,覆盖 `go` 和 `toolchain` 行。例如,要使用 Go 1.21rc3 测试一个包: -``` +```plain GOTOOLCHAIN=go1.21rc3 go test ``` -默认的 `GOTOOLCHAIN` 设置为 `auto`,这启用了之前描述的工具链切换。替代形式 `+auto` 在决定是否进一步切换之前设置要使用的默认工具链。例如,`GOTOOLCHAIN=go1.21.3+auto` 指示 `go` 命令从默认使用 Go 1.21.3 开始做出决策,但如果由 `go` 和 `toolchain` 行指示,则仍然使用更新的工具链。由于默认的 `GOTOOLCHAIN` 设置可以通过 `go env -w` 更改,因此如果您安装了Go 1.21.0或更高版本,那么: +默认的 `GOTOOLCHAIN` 设置为 `auto`,这启用了之前描述的工具链切换。替代形式 `+auto` 在决定是否进一步切换之前设置要使用的默认工具链。例如,`GOTOOLCHAIN=go1.21.3+auto` 指示 `go` 命令从默认使用 Go 1.21.3 开始做出决策,但如果由 `go` 和 `toolchain` 行指示,则仍然使用更新的工具链。由于默认的 `GOTOOLCHAIN` 设置可以通过 `go env -w` 更改,因此如果您安装了 Go 1.21.0 或更高版本,那么: -``` +```plain go env -w GOTOOLCHAIN=go1.21.3+auto ``` @@ -37,13 +37,13 @@ go env -w GOTOOLCHAIN=go1.21.3+auto 发布的 Go 版本使用版本语法 “1.N.P”,表示 Go 1.N 的第 _P_ 次已发布版本。初始版本为 1.N.0,例如 “1.21.0”。后续版本如 1.N.9 通常被称为补丁版本。 -Go 1._N_的发布候选版本,在 1.N.0 之前发布,使用 ‘1.N_rc_R’ 的版本语法。Go 1.N 的第一个发布候选版本是1._N_rc1,如 `1.23rc1`。 +Go 1._N_的发布候选版本,在 1.N.0 之前发布,使用 ‘1.N_rc_R’ 的版本语法。Go 1.N 的第一个发布候选版本是 1._N_rc1,如 `1.23rc1`。 语法 “1.N” 被称为“语言版本”。它表示实现该版本 Go 语言和标准库的 Go 发布的整个系列。 -Go 版本的语言版本是将 N 之后的所有内容截断的结果:1.21、1.21rc2 和 1.21.3都实现了语言版本 1.21。 +Go 版本的语言版本是将 N 之后的所有内容截断的结果:1.21、1.21rc2 和 1.21.3 都实现了语言版本 1.21。 -已发布的 Go 工具链如 Go 1.21.0 和 Go 1.21rc1 报告特定版本(例如 `go1.21.0` 或 `go1.21rc1`)从 `go version` 和`[runtime.Version](/pkg/runtime/#Version)`中。未发布(仍在开发中)的Go工具链从Go开发存储库构建,只报告语言版本(例如 `go1.21`)。 +已发布的 Go 工具链如 Go 1.21.0 和 Go 1.21rc1 报告特定版本(例如 `go1.21.0` 或 `go1.21rc1`)从 `go version` 和`[runtime.Version](/pkg/runtime/#Version)`中。未发布(仍在开发中)的 Go 工具链从 Go 开发存储库构建,只报告语言版本(例如 `go1.21`)。 任何两个 Go 版本都可以进行比较,以确定一个是否小于、大于或等于另一个。如果语言版本不同,那么就决定了比较:1.21.9 < 1.22。在语言版本内,从最小到最大的排序是:语言版本本身,然后按_R_排序的发布候选版本,然后按_P_排序的发布版本。 @@ -71,7 +71,7 @@ Go 模块和工作区在其 `go. mod` 或 `go.work` 文件中指定与版本相 `go` 行声明了使用模块或工作区所需的最低 Go 版本。出于兼容性原因,如果 `go. mod` 文件中省略了 `go` 行,则该模块被认为具有隐式 `go 1.16` 行,如果 `go.work` 文件中省略了 go 行,则该工作区被认为具有隐式 `go 1.18` 行。 -`toolchain` 行声明了与模块或工作区一起使用的建议工具链。如下面的 “[Go工具链选择](https://go.dev/doc/toolchain#select])”中所述,如果默认工具链的版本小于建议工具链的版本,则 `go` 命令可以在该模块或工作区中运行此特定工具链。如果省略 `toolchain` 行,则模块或工作区被认为具有隐式 `toolchain go_V_` 行,其中 _V_ 是 `go` 行的 Go 版本。 +`toolchain` 行声明了与模块或工作区一起使用的建议工具链。如下面的 “[Go 工具链选择](https://go.dev/doc/toolchain#select])”中所述,如果默认工具链的版本小于建议工具链的版本,则 `go` 命令可以在该模块或工作区中运行此特定工具链。如果省略 `toolchain` 行,则模块或工作区被认为具有隐式 `toolchain go_V_` 行,其中 _V_ 是 `go` 行的 Go 版本。 例如,一个说 `go 1.21.0` 但没有 `toolchain` 行的 `go. mod` 被解释为它有一个 `toolchain go1.21.0` 行。 @@ -99,7 +99,7 @@ Go 工具链拒绝加载声明最低所需 Go 版本大于工具链自己版本 - 否则,如果在用户的环境默认文件(使用 [`go env -w` 和 `go env -u`](https://go.dev/cmd/go/#hdr-Print_Go_environment_information) 管理)中设置了 `GOTOOLCHAIN`,则 `go` 命令使用该值。 -- 否则,如果在捆绑的 Go 工具链的环境默认文件(`$GOROOT/go. env)中设置了 `GOTOOLCHAIN`,则 `go` 命令使用该值。 +- 否则,如果在捆绑的 Go 工具链的环境默认文件(`$GOROOT/go. env)中设置了`GOTOOLCHAIN`,则`go` 命令使用该值。 在标准 Go 工具链中,`$GOROOT/go.env` 文件设置默认的 `GOTOOLCHAIN=auto`,但重新打包的 Go 工具链可能会更改此值。 @@ -134,7 +134,7 @@ Go 工具链拒绝加载声明最低所需 Go 版本大于工具链自己版本 每当 `go` 命令在启动工具链选择后切换工具链时,它都会打印一条解释原因的消息。例如: -``` +```plain go: module example.com/widget@v1.2.3 requires go >= 1.24rc1; switching to go 1.27.9 ``` @@ -158,7 +158,7 @@ go: module example.com/widget@v1.2.3 requires go >= 1.24rc1; switching to go 1.2 ## 下载 toolchains -当使用 `GOTOOLCHAIN=auto` 或 `GOTOOLCHAIN=+auto` 时,Go 命令会根据需要下载更新的工具链。这些工具链被打包为特殊模块,模块路径为 `golang.org/toolchain`,版本为 `v0.0.1-goVERSION.GOOS-GOARCH`。工具链像其他模块一样被下载,这意味着可以通过设置 `GOPROXY` 来代理工具链下载,并通过Go校验和数据库检查它们的校验和。由于使用的特定工具链取决于系统自己的默认工具链以及本地操作系统和架构(GOOS 和 GOARCH),因此在 `go.sum` 中编写工具链模块的校验和不实际。相反,如果 `GOSUMDB=off`,则工具链下载失败缺乏验证。`GOPRIVATE` 和 `GONOSUMDB` 模式不适用于工具链下载。 +当使用 `GOTOOLCHAIN=auto` 或 `GOTOOLCHAIN=+auto` 时,Go 命令会根据需要下载更新的工具链。这些工具链被打包为特殊模块,模块路径为 `golang.org/toolchain`,版本为 `v0.0.1-goVERSION.GOOS-GOARCH`。工具链像其他模块一样被下载,这意味着可以通过设置 `GOPROXY` 来代理工具链下载,并通过 Go 校验和数据库检查它们的校验和。由于使用的特定工具链取决于系统自己的默认工具链以及本地操作系统和架构(GOOS 和 GOARCH),因此在 `go.sum` 中编写工具链模块的校验和不实际。相反,如果 `GOSUMDB=off`,则工具链下载失败缺乏验证。`GOPRIVATE` 和 `GONOSUMDB` 模式不适用于工具链下载。 ## 使用 `go get` 管理 Go 版本模块的需求 diff --git a/2023/w24_Top_3_Design_Patterns_for_a_Large_Go_Codebase.md b/2023/w24_Top_3_Design_Patterns_for_a_Large_Go_Codebase.md index f0f885e..17fc8fa 100644 --- a/2023/w24_Top_3_Design_Patterns_for_a_Large_Go_Codebase.md +++ b/2023/w24_Top_3_Design_Patterns_for_a_Large_Go_Codebase.md @@ -18,7 +18,7 @@ ## 设计模式的简史 -让我们回顾一下软件工程设计模式是如何产生的。 1994 年,四位作者——Erich Gamma、John Vlissides、Ralph Johnson 和 Richard Helm 出版了一本书“[设计模式:可复用面向对象软件的基础](https://www.oreilly.com/library/view/design-patterns-elements/0201633612/)”。 这本书赢得了“四人帮之书”的绰号,后来缩写为“GoF之书”。 +让我们回顾一下软件工程设计模式是如何产生的。 1994 年,四位作者——Erich Gamma、John Vlissides、Ralph Johnson 和 Richard Helm 出版了一本书“[设计模式:可复用面向对象软件的基础](https://www.oreilly.com/library/view/design-patterns-elements/0201633612/)”。 这本书赢得了“四人帮之书”的绰号,后来缩写为“GoF 之书”。 GoF 书中包含 23 种设计模式,分为三种类型:创建型设计模式、结构型设计模式和行为型设计模式。 本书的示例是用 C++ 和 Smalltalk 编写的。 不出意外的,这本书所描述的模式受到这些特定语言和当时软件工程需求的影响。 然而,设计模式的概念被证明是持久的。 diff --git a/2023/w25_Using_Bitmaps_to_Perform_Range_Queries.md b/2023/w25_Using_Bitmaps_to_Perform_Range_Queries.md index a4c76da..c49390f 100644 --- a/2023/w25_Using_Bitmaps_to_Perform_Range_Queries.md +++ b/2023/w25_Using_Bitmaps_to_Perform_Range_Queries.md @@ -43,7 +43,7 @@ _两个特征的逐位交集_ _Captivity 计数表示为单个位图_ -这种方法是可以的,但它有几个局限性。首先,它的效率不是很高。根据基数的不同,您可能需要创建大量位图来表示所有可能的值。其次,如果您想按 Captivity 值的范围筛选查询,则必须对范围内的每个可能值执行 OR 运算。为了知道样本里哪些动物的 Captivity 值少于100个,您的查询需要执行以下操作:(Captivity=99或Captivity=98或Captivity=97或…)。你明白了。 +这种方法是可以的,但它有几个局限性。首先,它的效率不是很高。根据基数的不同,您可能需要创建大量位图来表示所有可能的值。其次,如果您想按 Captivity 值的范围筛选查询,则必须对范围内的每个可能值执行 OR 运算。为了知道样本里哪些动物的 Captivity 值少于 100 个,您的查询需要执行以下操作:(Captivity=99 或 Captivity=98 或 Captivity=97 或……)。你明白了。 另一种方法是创建 Captivity 范围的存储桶,而不是将每个可能的值表示为唯一的位图。在这种情况下,您可能会遇到这样的情况: @@ -63,7 +63,7 @@ _Captivity 计数(以桶表示)_ _Captivity 计数用范围编码位图_ -用范围编码位图表示一个值与我们使用相等编码所做的类似,但我们不只是设置与特定值对应的位,而是为每个大于实际值的值设置一个位。例如,因为有14只考拉熊的圈养(captivity),我们在位图 14 以及位图 15、16、17 等中设置了 bit。位图现在表示的不是具有特定圈养数量的所有动物的位图,而是具有并包括该数量的圈养数量的全部动物的位图。 +用范围编码位图表示一个值与我们使用相等编码所做的类似,但我们不只是设置与特定值对应的位,而是为每个大于实际值的值设置一个位。例如,因为有 14 只考拉熊的圈养(captivity),我们在位图 14 以及位图 15、16、17 等中设置了 bit。位图现在表示的不是具有特定圈养数量的所有动物的位图,而是具有并包括该数量的圈养数量的全部动物的位图。 这种编码方法使我们能够执行以前所做的范围查询,但我们可以从一个或两个位图中获得所需的内容,而不是对许多不同的位图执行 OR 运算。 例如,如果我们想知道哪些动物圈养的标本少于 15 个,我们只需提取 14 位图就可以了。如果我们想知道哪些动物的圈养标本超过 15 个,这有点复杂,但并不多。为此,我们提取表示最大计数的位图(在我们的情况下是 956 位图),然后减去 15 位图。 @@ -92,7 +92,7 @@ _十进制,位切片索引_ _范围编码,十进制,位切片索引_ -请注意,每个组件中最重要的值(在基数为10的情况下为9)始终为 1。正因为如此,我们不需要存储最高值。因此,对于十进制的范围编码的位切片索引,我们只需要9个位图来表示一个组件。除此之外,我们还需要存储一个名为“Not Null”的位图,它指示是否为该列设置了值。下一个图表显示了生成的位图。 +请注意,每个组件中最重要的值(在基数为 10 的情况下为 9)始终为 1。正因为如此,我们不需要存储最高值。因此,对于十进制的范围编码的位切片索引,我们只需要 9 个位图来表示一个组件。除此之外,我们还需要存储一个名为“Not Null”的位图,它指示是否为该列设置了值。下一个图表显示了生成的位图。 ![](https://uploads-ssl.webflow.com/62fe6fb36d2e0b5c203ee7c3/6324dd62797efe87f0dfff68_captive-bsi-range-encoded-base10-not-null.png) @@ -108,7 +108,7 @@ _范围编码,十进制,位切片索引,不为空_ _范围编码,二进制,位切片索引_ -第一列比特表示以 2 为基数的值 000000011,这是圈养海牛的数量(以 10 为基数为 3)。由于 000000011 的组件 0 和组件 1 都是 1,我们在组件 0 的行 1 和组件 1 的行 1 中设置一个位。由于 000000011的 其余组件都是 0,我们在组件 2 到 9 的第 0 行设置了一个比特,也因为这些组件是范围编码的,我们在每个大于 0 的值中设置一个比特。在二进制的组件的情况下,这意味着我们还为组件 2 到 9设置了行 1 中的位。 +第一列比特表示以 2 为基数的值 000000011,这是圈养海牛的数量(以 10 为基数为 3)。由于 000000011 的组件 0 和组件 1 都是 1,我们在组件 0 的行 1 和组件 1 的行 1 中设置一个位。由于 000000011 的 其余组件都是 0,我们在组件 2 到 9 的第 0 行设置了一个比特,也因为这些组件是范围编码的,我们在每个大于 0 的值中设置一个比特。在二进制的组件的情况下,这意味着我们还为组件 2 到 9 设置了行 1 中的位。 但请记住,就像我们之前在十进制表示的位图 9 中看到的那样,位图 1 总是一个,所以我们不需要存储它。这就给我们留下了这样的方式: @@ -116,7 +116,7 @@ _范围编码,二进制,位切片索引_ _范围编码,二进制,不为空的位切片索引_ -通过这种编码,我们可以仅用 10 个位图来表示样本值的范围!此外,请注意,二进制、范围编码、位切片索引是整数值的二进制表示的反。这告诉我们,我们可以仅使用(n+1)位图(其中附加位图是“Not Null”位图)来表示基数为n的任何范围的值。这意味着我们可以对大整数值执行范围查询,而不需要存储不合理数量的位图。 +通过这种编码,我们可以仅用 10 个位图来表示样本值的范围!此外,请注意,二进制、范围编码、位切片索引是整数值的二进制表示的反。这告诉我们,我们可以仅使用(n+1)位图(其中附加位图是“Not Null”位图)来表示基数为 n 的任何范围的值。这意味着我们可以对大整数值执行范围查询,而不需要存储不合理数量的位图。 ## FeatureBase 中的范围编码位图 diff --git a/2023/w27_Understanding_Allocations_in_Go.md b/2023/w27_Understanding_Allocations_in_Go.md index 38545f4..adf459c 100644 --- a/2023/w27_Understanding_Allocations_in_Go.md +++ b/2023/w27_Understanding_Allocations_in_Go.md @@ -4,7 +4,7 @@ - 原文作者:James Kirk - 本文永久链接:https://github.com/gocn/translator/blob/master/2021/ - 译者:[lsj1342](https://github.com/lsj1342) -- 校对:[]() +- 校对:[](https://example.com) *** ## Introduction @@ -13,7 +13,7 @@ Thanks to efficient in-built memory management in the Go runtime, we’re genera Anyone who’s run a benchmark with the `-benchmem` flag will have seen the `allocs/op` stat in output like the below. In this post we’ll look at what counts as an alloc and what we can do to influence this number. -``` +```plain BenchmarkFunc-8 67836464 16.0 ns/op 8 B/op 1 allocs/op ``` @@ -65,7 +65,7 @@ Since compiler implementations change over time, **there’s no way of knowing w For escape analysis results, the `-m` option (`print optimization decisions`) can be used. Let’s test this with a simple program that creates two stack frames for functions `main1` and `stackIt`. -``` +```plain func main1() { _ = stackIt() } @@ -78,21 +78,21 @@ func stackIt() int { Since we can can’t discuss stack behaviour if the compiler removes our function calls, the `noinline` [pragma](https://dave.cheney.net/2018/01/08/gos-hidden-pragmas) is used to prevent inlining when compiling the code. Let’s take a look at what the compiler has to say about its optimization decisions. The `-l` option is used to omit inlining decisions. -``` +```plain $ go build -gcflags '-m -l' # github.com/Jimeux/go-samples/allocations ``` Here we see that no decisions were made regarding escape analysis. In other words, variable `y` remained on the stack, and didn’t trigger any heap allocations. We can verify this with a benchmark. -``` +```plain $ go test -bench . -benchmem BenchmarkStackIt-8 680439016 1.52 ns/op 0 B/op 0 allocs/op ``` As expected, the `allocs/op` stat is `0`. An important observation we can make from this result is that **copying variables can allow us to keep them on the stack** and avoid allocation to the heap. Let’s verify this by modifying the program to avoid copying with use of a pointer. -``` +```plain func main2() { _ = stackIt2() } @@ -106,7 +106,7 @@ func stackIt2() *int { Let’s see the compiler output. -``` +```plain go build -gcflags '-m -l' # github.com/Jimeux/go-samples/allocations ./main.go:10:2: moved to heap: res @@ -114,14 +114,14 @@ go build -gcflags '-m -l' The compiler tells us it moved the pointer `res` to the heap, which triggers a heap allocation as verified in the benchmark below -``` +```plain $ go test -bench . -benchmem BenchmarkStackIt2-8 70922517 16.0 ns/op 8 B/op 1 allocs/op ``` So does this mean pointers are guaranteed to create allocations? Let’s modify the program again to this time pass a pointer down the stack. -``` +```plain func main3() { y := 2 _ = stackIt3(&y) // pass y down the stack as a pointer @@ -136,14 +136,14 @@ func stackIt3(y *int) int { Yet running the benchmark shows nothing was allocated to the heap. -``` +```plain $ go test -bench . -benchmem BenchmarkStackIt3-8 705347884 1.62 ns/op 0 B/op 0 allocs/op ``` The compiler output tells us this explicitly. -``` +```plain $ go build -gcflags '-m -l' # github.com/Jimeux/go-samples/allocations ./main.go:10:14: y does not escape @@ -159,7 +159,7 @@ Why do we get this seeming inconsistency? `stackIt2` passes `res` _up the stack_ We’ve learnt a little about what the `alloc` in `allocs/op` means, and how to verify if an allocation to the heap is triggered, but why should we care if this stat is non-zero in the first place? The benchmarks we’ve already done can begin to answer this question. -``` +```plain BenchmarkStackIt-8 680439016 1.52 ns/op 0 B/op 0 allocs/op BenchmarkStackIt2-8 70922517 16.0 ns/op 8 B/op 1 allocs/op BenchmarkStackIt3-8 705347884 1.62 ns/op 0 B/op 0 allocs/op @@ -183,7 +183,7 @@ Many aspects of performance don’t become apparent without production condition We’re not going to recreate an entire application in this post, but we will take a look at some more detailed performance diagnostics using the [trace tool](https://golang.org/cmd/trace/). Let’s begin by defining a (somewhat) big struct with nine fields. -``` +```plain type BigStruct struct { A, B, C int D, E, F string @@ -193,7 +193,7 @@ type BigStruct struct { Now we’ll define two functions: `CreateCopy`, which copies `BigStruct` instances between stack frames, and `CreatePointer`, which shares `BigStruct` pointers up the stack, avoiding copying, but resulting in heap allocations. -``` +```plain //go:noinline func CreateCopy() BigStruct { return BigStruct{ @@ -214,7 +214,7 @@ func CreatePointer() *BigStruct { We can verify the explanation from above with the techniques used so far. -``` +```plain $ go build -gcflags '-m -l' ./main.go:67:9: &BigStruct literal escapes to heap $ go test -bench . -benchmem @@ -224,7 +224,7 @@ BenchmarkPointerIt-8 20393278 52.6 ns/op 80 B/op 1 allocs/op Here are the tests we’ll use for the `trace` tool. They each create 20,000,000 instances of `BigStruct` with their respective `Create` function. -``` +```plain const creations = 20_000_000 func TestCopyIt(t *testing.T) { @@ -242,7 +242,7 @@ func TestPointerIt(t *testing.T) { Next we’ll save the trace output for `CreateCopy` to file `copy_trace.out`, and open it with the trace tool in the browser. -``` +```plain $ go test -run TestCopyIt -trace=copy_trace.out PASS ok github.com/Jimeux/go-samples/allocations 0.281s @@ -260,7 +260,7 @@ Trace for 20,000,000 CreateCopy calls Let’s generate the trace data for the `CreatePointer` code. -``` +```plain $ go test -run TestPointerIt -trace=pointer_trace.out PASS ok github.com/Jimeux/go-samples/allocations 2.224s @@ -296,7 +296,7 @@ Top-level goroutine analysis for CreatePointer A look at some of the stats available elsewhere in the trace data further illustrates the cost of heap allocation, with a stark difference in the number of goroutines generated, and almost 400 STW (stop the world) events for the `CreatePointer` test. -``` +```plain +------------+------+---------+ | | Copy | Pointer | +------------+------+---------+ diff --git a/2023/w28_Go_Developer_Survey_2023_H2_Results.md b/2023/w28_Go_Developer_Survey_2023_H2_Results.md new file mode 100644 index 0000000..11dc477 --- /dev/null +++ b/2023/w28_Go_Developer_Survey_2023_H2_Results.md @@ -0,0 +1,318 @@ +# Go 开发者 2023 下半年调查问卷结果 + +- 原文地址:[Go Developer Survey 2023 H2 Results - The Go Programming Language](https://go.dev/blog/survey2023-h2-results) +- 原文作者:Go Team +- 本文永久链接: +- 译者:[zxmfk](https://github.com/zxmfke) + +Todd Kulesza + +2023 年 12 月 5 日 + +## 背景 + +2023 年 8 月,Google Go 团队进行了我们每半年一次的 Go 开发者调查。我们通过 Go 博客的公开文章和在 VS Code 中的随机提示招募了参与者,收到了 4,005 份回应。我们的调查主要集中在几个主题上:关于使用 Go 进行开发的整体感受和反馈,以及 Go 相关的技术栈,开发者如何启动新的 Go 项目,最近工具链错误消息的体验,以及了解开发者在 AI/ML 领域的兴趣。 + +感谢所有参与此次调查的人!本报告分享了我们从您的反馈中所学到的内容。 + +## 摘要 + +1. Go 开发者表示,他们对改善其编写代码的质量、可靠性和性能的人工智能/机器学习工具感兴趣,而不是编写代码的工具。一位时刻警醒、从不忙碌的专家“审阅者”可能是一种更有帮助的 AI 开发者辅助形式。 +2. 改进工具链警告和错误的最主要请求是让消息更易理解和可行动;这种观点不仅受到所有经验水平开发者的认同,而且在新手 Go 开发者中尤为突出。 +3. 我们对项目模板(gonew)的实验似乎为 Go 开发者(尤其是新手开发者)解决了关键问题,并且以与他们已有的启动新项目的工作流程相匹配的方式实现了这一点。基于这些发现,我们相信 gonew 能大幅降低新手 Go 开发者的入门障碍,并促进组织对 Go 的采用。 +4. 四分之三的受访者表示,他们所开发的 Go 软件也使用云服务;这表明开发者认为 Go 是一种现代、基于云的开发语言。 +5. 开发者对 Go 的态度依然非常积极,90% 的调查受访者表示在过去一年中使用 Go 时感到满意。 + +## 开发者感受 + +Go 开发者对 Go 生态系统依旧反馈了高满意度。绝大多数受访者表示,在过去一年中使用 Go 时感到满意(90% 满意,6% 不满意),其中大多数(52%)更进一步表示他们 “非常满意”,这是最高的评级。长期关注的读者可能已经注意到,这个数字在年复一年中变化不大。对于像 Go 这样庞大、稳定的项目来说,这是预期的;我们将这个指标视为[滞后指标](https://en.wikipedia.org/wiki/Economic_indicator#Lagging_indicators),可以帮助确认 Go 生态系统中普遍问题,但并不是我们来了解潜在问题的首要出处。 + +通常我们发现,一个人在使用 Go 的时间越长,他们对其满意度就越高。这种趋势在 2023 年仍在持续;在拥有不到一年 Go 经验的受访者中,有 82% 报告对 Go 开发体验感到满意,而在拥有五年或更多经验的 Go 开发者中,有 94% 表示满意。这可能涉及多种因素,比如一些受访者随着时间的推移对 Go 的设计选择有了更深的体会,或者认为 Go 并不适合他们的工作,因此在接下来的调查中不再回答(即[生存偏差](https://en.wikipedia.org/wiki/Survivorship_bias))。不过,这些数据有助于我们量化 Go 开发者目前的入门体验,而且很明显我们可以做更多来帮助新兴的 Gophers 找到立足点,并享受在 Go 中取得早期成功的过程。 + +关键点在于,过去一年中选择使用 Go 的绝大多数人对开发体验感到满意。此外,使用 Go 的人数不断增加;我们从像 [Stack Overflow 的开发者调查](https://survey.stackoverflow.co/2023/#most-popular-technologies-language-prof)(发现有 14% 的专业开发者在过去一年中使用 Go,同比增长约 15%)以及 [go.dev](https://go.dev/) 的分析数据(显示访问量同比增长 8%)等外部研究中看到了这一点。将这种增长与高满意度得分结合起来,表明 Go 仍然吸引着开发者,并且表明许多选择学习该语言的开发者在很长时间后依然对他们的决定感到满意。用他们自己的话来说: + +> “在 C、C++、Java 开发超过 30 年后,现在已经用 Go 开发 7 年了,它仍然是迄今为止最高效的语言。它并不完美(没有哪种语言是完美的),但在生产力、复杂性和性能方面取得了最佳平衡。” — 拥有 5 到 9 年经验的专业 Go 开发者 +> +> “目前这是我所了解的最好的语言,我尝试过很多。工具很棒,编译时间很快,我可以非常高效。我很高兴有 Go 这样的工具,不需要在服务器端使用 TypeScript。谢谢。” — 拥有 3 到 4 年经验的开源 Go 开发者 + +![image-20231217101830370](C:\Users\zhengxm\Documents\notes\Go开发者调查\1.png) + +## 开发环境 + +和往年一样,大多数调查受访者告诉我们,他们在 Linux(63%)和 macOS(58%)系统上使用 Go。这些数字在年复一年中有轻微变化,最可能取决于谁发现并回答了这份调查(特别是在 Go 博客上),因为我们在来自 VS Code 的随机样本中并未看到一致的年度变化趋势。 + +我们继续看到,Go 社区中较新的成员更有可能在 Windows 上进行工作,而不是经验丰富的 Go 开发者。我们将这解读为在新开发者加入 Go 生态系统时,基于 Windows 的开发至关重要,而这也是我们团队希望在 2024 年更加关注的一个话题。 + +![image-20231217101802712](C:\Users\zhengxm\Documents\notes\Go开发者调查\2.png) + +![image-20231217101849547](C:\Users\zhengxm\Documents\notes\Go开发者调查\3.png) + +受访者继续高度关注 Linux 部署。考虑到 Go 在云开发和容器化工作负载中的普及,这并不令人意外,但仍然是一个重要的确认。根据组织规模或经验水平等因素,我们发现了很少有意义的差异;事实上,尽管新手 Go 开发者似乎更有可能在 Windows 上开发,但仍有 92% 的人部署到 Linux 系统上。也许从这个分析中最有趣的发现是,经验丰富的 Go 开发者表示他们部署到更多类型的系统上(尤其是 WebAssembly 和 IoT),尽管目前尚不清楚这是因为这些部署对于新手 Go 开发者来说具有挑战性,还是因为经验丰富的 Go 开发者在更广泛的背景下使用 Go。我们还观察到,近年来 IoT 和 WebAssembly 的使用稳步增长,分别从 2021 年的 3% 上升到 2023 年的 6% 和 5%。 + +![image-20231217101920680](C:\Users\zhengxm\Documents\notes\Go开发者调查\4.png) + +在过去几年中,计算架构格局发生了变化,我们看到这种变化反映在当前 Go 开发者表示他们所使用的架构上。虽然兼容 x86 架构的系统仍然占据了大多数开发(89%),但 ARM64 现在也被多数受访者所采用(56%)。这种采用似乎部分受到了 Apple Silicon 的推动;现在,macOS 开发者更有可能表示他们为 ARM64 而非基于 x86 的架构进行开发(76% 对 71%)。然而,驱动 ARM64 采用的因素并不仅限于 Apple 硬件:在根本不会在 macOS 上进行开发的受访者中,仍有 29% 表示他们在为 ARM64 进行开发。 + +![image-20231217102049776](C:\Users\zhengxm\Documents\notes\Go开发者调查\5.png) + +在 Go 开发者调查受访者中,最常见的代码编辑器仍然是 [VS Code](https://code.visualstudio.com/)(44%)和 [GoLand](https://www.jetbrains.com/go/)(31%)。这两个比例与 2023 上半年略有下降(分别为 46%和 33%),但仍在本次调查的误差范围之内。在“其他”类别中,[Helix](https://helix-editor.com/) 占据了大部分。与上述操作系统的结果类似,我们认为这并不代表代码编辑器使用情况发生了实质性的变化,而是展示了我们在这类社区调查中预期看到的一些变化性。特别地,我们在这个问题上排除了来自 VS Code 的随机抽样受访者,因为我们知道该群体对 VS Code 有很大偏好。然而,这也导致了这些结果每年更容易出现变化。 + +我们还根据受访者所偏好使用的编辑器,观察了他们对 Go 满意度的水平。在控制经验长度后,我们并未发现任何差异:我们认为人们并不因为使用不同的代码编辑器而更多或更少地享受使用 Go。这并不一定意味着所有的 Go 编辑器都是相同的,而可能反映了人们找到最适合自己需求的编辑器。这表明了 Go 生态系统拥有多样化的不同编辑器,针对不同的使用情景和开发者偏好。 + +![image-20231217102238469](C:\Users\zhengxm\Documents\notes\Go开发者调查\6.png) + +## 技术栈 + +为了更好地了解 Go 开发者所交互的软件和服务网络,我们就技术栈询问了几个问题。我们将这些结果与社区分享,以展示今天常见使用的工具和平台,但我们认为每个人在选择技术栈时应考虑自己的需求和使用案例。更明确地说:我们既不打算让读者根据这些数据选择技术栈的组件,因为它们很受欢迎,也不打算因为它们不常用就不去使用它们。 + +首先,我们可以有信心地说,Go 是一种面向现代云端开发的语言。事实上,有 75%的受访者在使用与云服务集成的 Go 软件。近一半的受访者涉及到 AWS(48%),几乎三分之一的人在他们的 Go 开发和部署中使用了 GCP(29%)。对于 AWS 和 GCP,使用情况在大型企业和小型组织中均衡。微软 Azure 是唯一一个在大型组织(拥有超过 1000 名员工的公司)中使用可能性明显高于较小机构的云服务提供商;其他提供商在使用上并未因组织规模而有显著差异。 + +![image-20231217102455262](C:\Users\zhengxm\Documents\notes\Go开发者调查\7.png) + +数据库是软件系统中极为常见的组件,我们发现有 91%的受访者表示他们所开发的 Go 服务中至少使用了一个数据库。最常用的是 PostgreSQL(59%),但有双位数的受访者报告使用了其他六种数据库,因此可以肯定地说,并非只有几种标准的数据库供 Go 开发者考虑。根据组织规模的不同,我们再次看到差异,较小组织的受访者更有可能报告使用 PostgreSQL 和 Redis,而较大组织的开发者更有可能使用特定于其云服务提供商的数据库。 + +![image-20231217102851910](C:\Users\zhengxm\Documents\notes\Go开发者调查\8.png) + +另一个受访者报告常用的组件是缓存或键值存储;有 68%的受访者表示他们参与的 Go 软件至少使用了其中一种。Redis 显然是最常见的(57%),其次是 etcd(10%)和 memcached(7%)。 + +![image-20231217102912484](C:\Users\zhengxm\Documents\notes\Go开发者调查\9.png) + +与数据库类似,调查受访者告诉我们他们使用了一系列不同的可观测性系统。Prometheus 和 Grafana 是最常被提及的(都为 43%),但 Open Telemetry、Datadog 和 Sentry 的使用率也都达到了两位数。 + +![image-20231217102943491](C:\Users\zhengxm\Documents\notes\Go开发者调查\10.png) + +如果有人想知道“我们是不是将所有东西都转换成了 JSON?”…… 是的,确实是这样。几乎每位受访者(96%!)表示他们的 Go 软件使用了 JSON 数据格式;这几乎是自我报告数据中最接近普遍的情况了。YAML、CSV 和 Protocol Buffers 也大约被一半的受访者使用,还有两位数的比例使用 TOML 和 XML。 + +![image-20231217103018153](C:\Users\zhengxm\Documents\notes\Go开发者调查\11.png) + +在认证和授权服务方面,我们发现大多数受访者都是基于诸如[JWT](https://jwt.io/introduction)和[OAuth2](https://oauth.net/2/)等标准来构建的基础。这似乎也是一个领域,组织的云服务提供商解决方案与大多数即插即用的替代方案一样受欢迎。 + +![image-20231217103103082](C:\Users\zhengxm\Documents\notes\Go开发者调查\12.png) + +最后,我们有一些不太适合上述分类的其他服务。我们发现将近一半的受访者在他们的 Go 软件中使用了 gRPC(47%)。对于基础架构即代码的需求,约四分之一的受访者选择了 Terraform 作为工具。其他与 Go 一起常见使用的技术包括 Apache Kafka、ElasticSearch、GraphQL 和 RabbitMQ。 + +![image-20231217103208810](C:\Users\zhengxm\Documents\notes\Go开发者调查\13.png) + +我们还研究了哪些技术倾向于一起使用。虽然没有明显类似于经典的 LAMP 堆栈(Linux + Apache + MySQL + PHP)的模式出现在这项分析中,但我们确实确定了一些有趣的模式: + +- 全有或全无:每个类别(除了数据格式)都显示出强烈的相关性,如果受访者对某个类别回答“无”,他们很可能对其他所有类别也回答“无”。我们解释这一点是因为少数用例不需要这些技术堆栈组件中的任何一个,但一旦用例需要任何一个,很可能需要(或至少可以简化为)不止一个。 +- 对跨平台技术的偏好:特定于提供商的解决方案(即,仅适用于单个云平台的服务)并不常见。但是,如果受访者使用了一个特定于提供商的解决方案(例如,用于指标),他们很可能还会说他们在其他方面(例如,数据库、认证、缓存等)也使用了特定于云的解决方案。 +- 多云:三大主要云平台最有可能涉及多云设置。例如,如果一个组织使用任何非 AWS 的云提供商,他们很可能也在使用 AWS。这种模式在 Amazon Web Services 方面最为明显,但也在 Google Cloud Platform 和 Microsoft Azure 中(程度较小)出现。 + +## 开发者如何启动新的 Go 项目 + +作为我们在项目模板方面的实验的一部分,我们希望了解当下 Go 开发者如何开始新项目。受访者告诉我们,他们最大的挑战是选择适当的项目结构方式(54%),以及学习如何编写符合惯用法的 Go 代码(47%)。就像两位受访者所言: + +“为新项目找到适当的结构和正确的抽象层级可能相当繁琐;查看高知名度的社区和企业项目以获得灵感可能会令人困惑,因为每个人都以不同方式组织他们的项目” — 拥有 5 到 9 年 Go 经验的专业 Go 开发者 + +“如果[Go 有一个]工具链来创建 Web 或 CLI 的基本结构就太好了,就像 `go init <项目名>`” — 拥有 3 到 4 年经验的专业 Go 开发者 + +对于经验不到两年的 Go 开发者来说,更有可能遇到这些挑战:分别增加到了 59%和 53%。这正是我们希望通过 gonew 原型改进的两个方面:模板可以为新 Go 开发者提供经过充分测试的项目结构和设计模式,初始实现采用符合惯用法的 Go 编写。这些调查结果帮助我们的团队将 gonew 的目标集中在 Go 社区最困扰的任务上。 + +![image-20231217105326006](C:\Users\zhengxm\Documents\notes\Go开发者调查\14.png) + +大多数受访者告诉我们,他们在启动新的 Go 项目时要么使用模板,要么从现有项目中复制+粘贴代码(58%)。在经验不到五年的受访者中,这个比例增加到了将近三分之二(63%)。这是一个重要的确认,表明 gonew 中基于模板的方法似乎符合开发者目前的使用情况,将一种常见的非正式方法与 go 命令风格的工具对齐。这进一步支持了针对项目模板的常见功能请求:大多数受访者请求 + +- 预配置的目录结构来组织他们的项目 +- 项目领域常见任务的示例代码 + +这些结果与开发者在前面部分所面临的挑战非常一致。对这个问题的回答也有助于区分项目结构和设计模式之间的差异,几乎有两倍的受访者表示他们希望 Go 项目模板提供前者而不是后者。 + +![image-20231217105527148](C:\Users\zhengxm\Documents\notes\Go开发者调查\15.png) + +![image-20231217105816020](C:\Users\zhengxm\Documents\notes\Go开发者调查\16.png) + +大多数受访者告诉我们,能够对模板进行更改并将这些更改传播到基于该模板的项目是至关重要的。据我们所知,目前我们还没有与任何具有自制模板方法的开发者讨论过这种功能,但这表明这是未来发展的一个有趣方向。 + +![image-20231217105908061](C:\Users\zhengxm\Documents\notes\Go开发者调查\17.png) + +## 开发者对错误处理的目标 + +在 Go 开发者之间,一个经常讨论的话题是对错误处理的潜在改进。正如一位受访者总结的那样: + +> “错误处理增加了太多样板代码(我知道,你可能之前听过这个)” — 拥有 1 到 2 年经验的开源 Go 开发者 + +但是,我们也从许多开发者那里听到,他们赞赏 Go 的错误处理方式: + +> “Go 的错误处理简单而有效。由于我在 Java 和 C#中有后端,并且现在正在探索 Rust 和 Zig,我总是很高兴回到编写 Go 代码。而且其中一个原因是,信不信由你,就是错误处理。它真的很简单、朴素而有效。请保持这样。” — 拥有 5 到 9 年经验的开源 Go 开发者 + +与其询问对 Go 中错误处理的具体修改,我们更希望了解开发者更高层次的目标,以及 Go 当前的方法是否已被证明有用和可用。我们发现,大多数受访者赞赏 Go 的错误处理方法(55%),并表示它帮助他们知道何时检查错误(50%)。这两个结果对有更多 Go 经验的受访者更有影响,这表明开发者要么随着时间推移开始欣赏 Go 的错误处理方法,要么这是导致开发者最终离开 Go 生态系统的一个因素(或者至少停止回应与 Go 相关的调查)。许多调查受访者还认为,Go 需要大量乏味的样板代码来检查错误(43%);无论受访者之前有多少 Go 经验,这一点始终如此。有趣的是,当受访者表示他们赞赏 Go 的错误处理时,他们不太可能说它也会导致大量样板代码 — 我们的团队假设 Go 开发者既能欣赏语言的错误处理方式,又觉得它太啰嗦,但只有 14%的受访者同意这两种说法。 + +受访者提到的具体问题包括难以知道要检查哪种错误类型(28%),希望能够轻松地在错误消息中显示堆栈跟踪(28%),以及容易完全忽略错误(19%)。大约三分之一的受访者还对采用其他语言中的概念感兴趣,比如 Rust 的?操作符(31%)。 + +Go 团队没有计划在语言中添加 exceptions,但由于这在实践中是一个常见的请求,我们将其作为一个响应选择包括在内。只有十分之一的受访者表示他们希望能在 Go 中使用 exceptions,而这与经验成反比 — 更有经验的 Go 开发者对 exceptions 的兴趣不及较新加入 Go 社区的受访者。 + +![image-20231217110317767](C:\Users\zhengxm\Documents\notes\Go开发者调查\18.png) + +## 了解 ML/AI 用例 + +Go 团队正在考虑新的 ML/AI 技术发展如何影响软件开发的两个不同方面: + +- ML/AI 工具如何帮助工程师编写更好的软件 +- Go 如何帮助工程师将 ML/AI 支持引入其应用程序和服务 + +下面我们分别深入探讨这两个领域。 + +### 帮助工程师编写更好软件 + +毫无疑问,我们正处于围绕 [AI/ML 可能性的炒作周期](https://www.gartner.com/en/articles/what-s-new-in-artificial-intelligence-from-the-2023-gartner-hype-cycle)中。我们想退一步,关注开发者面临的更广泛挑战,以及他们认为 AI 可能在他们的常规工作中证明有用的地方。答案有些令人惊讶,特别是考虑到当前行业对编码助手的关注。 + +首先,我们看到大约一半的受访者认为以下几个 AI 用例可能会有帮助:生成测试(49%)、提出实地最佳实践建议(47%)以及在开发过程早期捕捉可能的错误(46%)。这些顶级用例的统一主题是每一个都可以帮助提高工程师所编写代码的质量和可靠性。第四个用例(帮助编写文档)引起了大约三分之一受访者的兴趣。其余的用例包括一系列潜在的有益想法,但这些比起前四个用例来说,普遍的兴趣要低得多。 + +当我们观察开发者在 Go 方面的经验时,我们发现初学者受访者对帮助解决编译器错误和解释 Go 代码的作用比经验丰富的 Go 开发者更感兴趣。这可能是 AI 可以帮助提高新 Gopher 入门体验的领域;例如,AI 助手可以帮助用自然语言解释未记录的代码块功能,或者针对特定错误消息建议常见的解决方案。相反地,我们发现在诸如“捕捉常见错误”之类的话题上,经验水平之间没有差异 — 初学者和经验丰富的 Go 开发者都表示他们会希望有工具来帮助处理这个问题。 + +人们可以从这些数据中看到三个广泛的趋势: + +- 受访者表达了对即时获得“专家审阅者”反馈的兴趣,而不仅仅是在审阅时期间。 +- 总体而言,受访者似乎最感兴趣的是那些可以节省他们可能不太喜欢的任务(例如,编写测试或文档化代码)的工具 +- 整体编写或翻译代码的兴趣相对较低,特别是对于有一两年以上经验的开发者来说。 + +综合起来,今天的开发者似乎对机器完成软件开发中有趣(例如,有创造性、愉快、适当具有挑战性的)部分的前景不太感兴趣,但确实认为另一组“眼睛”审阅他们的代码并可能为他们处理乏味或重复的任务有价值。就像一位受访者所表达的: + +> “我特别有兴趣利用 AI/ML 来提高我在 Go 中的生产力。拥有一个训练有素的 Go 最佳实践系统,可以捕捉反模式、错误,生成测试,并且出现错误率低,这将会非常有用。” — 拥有 5 到 9 年经验的专业 Go 开发者 + +然而,这项调查只是一个在快速发展的研究领域中的数据点,因此最好将这些结果放在适当的背景下进行考虑。 + +![image-20231217111047321](C:\Users\zhengxm\Documents\notes\Go开发者调查\19.png) + +### 将 AI 功能引入应用和服务 + +除了探讨 Go 开发者如何从 AI/ML 驱动的工具中受益外,我们还探讨了他们在使用 Go 构建 AI 驱动的应用程序和服务(或支持基础设施)方面的计划。我们发现我们在采用曲线的早期阶段:大多数受访者尚未尝试在这些领域使用 Go,尽管每个话题都引起了大约一半受访者的某种程度的兴趣。例如,大多数受访者表示有兴趣将他们所工作的 Go 服务与 LLM(低级机器学习模型)集成(49%),但只有 13%已经这样做或目前正在评估这种用例。在此次调查时,回答稍微表明开发者可能最感兴趣的是使用 Go 直接调用 LLM,构建驱动 ML/AI 系统所需的数据管道,并创建其他服务可以调用以与 ML/AI 模型进行交互的 API 端点。例如,这位受访者描述了他们希望通过在数据管道中使用 Go 获得的好处: + +> “我想使用 Go 集成 ETL [提取、转换和加载] 部分,以保持一致、稳健、可靠的代码库。” — 拥有 3 到 4 年经验的专业 Go 开发者 + +![image-20231217111115418](C:\Users\zhengxm\Documents\notes\Go开发者调查\20.png) + +## 工具链错误信息 + +开发者对看到错误消息、认为自己理解了消息含义和如何解决它,但经过数小时的无功的调试后发现消息实际意思完全不同的痛苦经历耿耿于怀,这是许多开发者共有的体验。一位受访者如此表达他们的沮丧: + +> “经常出现的异常最终与问题无关,但在我发现这一点之前可能要花一个小时。错误消息简洁得令人不安,并似乎并没有设法猜测用户可能正在尝试做什么或[解释他们的]错误。” — 拥有 10 年以上经验的专业 Go 开发者 + +我们认为开发工具发出的警告和错误应该简明扼要、易于理解和可操作:阅读它们的人应该能够准确理解出了什么问题以及他们可以做些什么来解决问题。这是一个极高的标准,通过这次调查,我们进行了一些测量,以了解开发者对 Go 当前警告和错误消息的感知。 + +在思考他们最近解决的 Go 错误消息时,受访者告诉我们有很大的改进空间。只有少数人单凭错误消息就能理解问题所在(54%),甚至更少的人知道下一步该怎么做以解决问题(41%)。似乎稍微增加一些额外信息就可以显著增加这些比例,因为有四分之一的受访者表示他们大多知道如何解决问题,但需要先看到一个例子。此外,有 11%的受访者表示他们无法理解错误消息,这为当前 Go 工具链错误消息的可理解性提供了一个基准。 + +改进 Go 工具链的错误消息将特别有益于经验较少的 Gopher。有最多两年经验的受访者不太可能像经验丰富的 Gopher 一样说他们理解了问题(47% vs. 61%)或知道如何解决它(29% vs. 52%),并且更有可能需要在线搜索来解决问题(21% vs. 9%),甚至理解错误消息的含义(15% vs. 7%)。 + +我们希望在 2024 年专注于改进工具链的错误消息。这些调查结果表明,这是所有经验水平的开发者都感到沮丧的地方,尤其有助于让新手开发者更好地开始使用 Go。 + +![image-20231217111400437](C:\Users\zhengxm\Documents\notes\Go开发者调查\21.png) + +![image-20231217111638468](C:\Users\zhengxm\Documents\notes\Go开发者调查\22.png) + +为了了解这些消息如何可以改进,我们向调查受访者提出了一个开放式问题:“如果您可以许一个愿望并改进 Go 工具链中的一个错误消息,您会改变什么?”。回应基本上符合我们的假设,即良好的错误消息既易于理解又可操作。最常见的回应是某种形式的“帮助我理解导致此错误的原因”(36%),21%的受访者明确要求提供解决问题的指导,还有 14%的受访者提到诸如 Rust 或 Elm 等语言,因为它们努力做到了这两点。一位受访者说道: + +> “对于编译错误,像 Elm 或 Rust 风格的输出能够准确定位源代码中的确切问题。错误信息应该包含尽可能修复问题的建议…… 我认为‘优化错误输出以便人类阅读’并‘在可能的情况下提供建议’的普遍政策在这里将是非常受欢迎的。” — 拥有 5-9 年经验的专业 Go 开发者 + +可以理解,工具链错误消息和运行时错误消息之间存在模糊的概念界限。例如,最常见的请求之一涉及改进堆栈跟踪或其他方法以帮助调试运行时崩溃(22%)。同样,4%的反馈中出现了一个令人意外的主题,关于使用 go 命令本身寻求帮助的挑战。这些都是 Go 社区帮助我们识别出的相关痛点,这些痛点在我们的视线之外。我们开始这项调查时着眼于改进编译时错误,但 Go 开发者希望改进的核心领域之一实际上与运行时错误相关,另一个则涉及 go 命令的帮助系统。 + +> “当出现错误时,调用堆栈可能非常庞大,包含了许多我不关心的文件。我只想知道我的代码出了问题,而不是我使用的库,或者 panic 是如何处理的。” — 拥有 1-2 年经验的专业 Go 开发者 +> +> “通过`go help run`获取帮助时,会显示一大堆文本,带有链接到进一步阅读以查找可用命令行标志的链接。或者它了解`go run –help`,但不是显示帮助,而是说‘请使用 go help run’。只需显示`go run –help`中的标志列表。” — 拥有 3-4 年经验的专业 Go 开发者 + +![image-20231217111700700](C:\Users\zhengxm\Documents\notes\Go开发者调查\23.png) + +## 微服务 + +我们经常听到开发者觉得 Go 非常适合微服务,但我们从未试图量化有多少 Go 开发者采用了这种服务架构类型,了解这些服务如何相互通信,以及开发者在处理它们时遇到的挑战。今年我们添加了一些问题来更好地了解这个领域。 + +大多数受访者表示他们主要开发微服务(43%),还有四分之一的受访者表示他们同时开发微服务和单体应用。只有约五分之一的受访者主要开发单体式的 Go 应用程序。这是我们看到根据受访者所在组织规模而有所不同的少数几个领域之一——大型组织似乎更有可能采用微服务架构,而不是较小的公司。大型组织(>1,000 名员工)的受访者中,最可能表示他们在开发微服务(55%),而仅有 11%的这些受访者主要从事单体式应用程序的开发。 + +![image-20231217111941582](C:\Users\zhengxm\Documents\notes\Go开发者调查\24.png) + +在 Go 平台中,我们看到微服务数量存在一些分歧。一组是由少数(2 到 5 个)服务组成的(40%),而另一组是更大的集合,至少包含 10 个组件服务(37%)。微服务的数量似乎与组织规模没有相关性。 + +![image-20231217112037815](C:\Users\zhengxm\Documents\notes\Go开发者调查\25.png) + +绝大多数受访者使用某种形式的直接响应请求(例如 RPC、HTTP 等)进行微服务通信(72%)。较少的比例使用消息队列(14%)或发布/订阅方法(9%);同样,我们在这方面没有看到与组织规模有关的差异。 + +![image-20231217112111719](C:\Users\zhengxm\Documents\notes\Go开发者调查\26.png) + +大多数受访者在构建微服务时使用多种语言,只有约四分之一的人完全使用 Go。Python 是最常用的辅助语言(33%),其次是 Node.js(28%)和 Java(26%)。同样,根据组织规模存在差异,较大的组织更有可能集成 Python(43%)和 Java(36%)微服务,而较小的组织更有可能仅使用 Go(30%)。其他语言在组织规模方面似乎使用相同。 + +![image-20231217112143892](C:\Users\zhengxm\Documents\notes\Go开发者调查\27.png) + +总体而言,受访者告诉我们,在编写基于微服务的应用程序时,测试和调试是他们面临的最大挑战,其次是操作复杂性。许多其他挑战在图表中占据了较长的尾部,尽管“可移植性”对大多数受访者来说并不是问题。我们理解为这样的服务并不打算可移植(除了基本的容器化);例如,如果一个组织的微服务最初由 PostgreSQL 数据库支持,开发人员并不担心将来可能将其转移到 Oracle 数据库上。 + +![image-20231217112222563](C:\Users\zhengxm\Documents\notes\Go开发者调查\28.png) + +## 模块的创作和维护 + +Go 拥有一个充满活力的由社区驱动的模块生态系统,我们想了解维护这些模块的开发人员所面临的动机和挑战。我们发现大约五分之一的受访者维护(或曾经维护过)一个开源的 Go 模块。这个比例让人惊讶地高,可能是由于我们分享这份调查的方式造成的:模块的维护者可能更有可能密切关注 Go 博客(这里发布了这项调查)而不同于其他 Go 开发人员。 + +![image-20231217112308089](C:\Users\zhengxm\Documents\notes\Go开发者调查\29.png) + +模块的维护者似乎在很大程度上是自我激励的 — 他们表示在个人(58%)或工作(56%)项目中需要这些模块,他们之所以这样做是因为他们喜欢从事这些模块的工作(63%),并且喜欢成为公共 Go 社区的一部分(44%),他们从模块维护中学到了有用的技能(44%)。更多的外部动机,比如获得认可(15%)、职业发展(36%)或金钱回报(20%),在名单的底部。 + +![image-20231217112417233](C:\Users\zhengxm\Documents\notes\Go开发者调查\30.png) + +维护模块的内在动机形式显示出,模块维护者面临的一个关键挑战是找到时间来专注于他们的模块(41%)。虽然这似乎不是一个可操作的发现(我们不能为 Go 开发人员多增加一两个小时,对吧?),但这是一个有益的视角,用来看待模块的工具和开发 — 这些任务很可能在开发者已经忙于其他事情的情况下进行,也许已经过去了几周或几个月,因此他们的记忆已经不新鲜了。因此,易于理解和操作的错误消息等方面可能会特别有帮助:与其要求某人再次搜索特定的 go 命令语法,也许错误输出可以直接在终端提供他们所需要的解决方案。 + +![image-20231217112455280](C:\Users\zhengxm\Documents\notes\Go开发者调查\31.png) + +## 工作信息统计 + +大多数调查受访者表示他们主要工作中使用 Go(78%),而且多数(59%)称他们在个人或开源项目中使用 Go。实际上,受访者通常在工作和个人/开源项目中都使用 Go,有 43% 的受访者表示他们在这两种情况下都在使用 Go。 + +![image-20231217112558356](C:\Users\zhengxm\Documents\notes\Go开发者调查\32.png) + +绝大多数受访者使用 Go 语言的时间不超过五年(68%)。和之前的调查相比,通过 VS Code 渠道发现此次调查的人员往往经验较少。 + +针对不同经验水平的受访者使用 Go 的情况,有两个显著的发现。首先,各经验水平的受访者中,大多数表示他们在专业领域使用 Go;事实上,对于那些有超过两年经验的人,绝大多数都在工作中使用 Go(85% - 91%)。类似的趋势也存在于开源开发中。第二个发现是,相较于经验更丰富的 Go 开发者,经验较少的开发者更有可能使用 Go 来扩展自己的技能(38%),或者在工作中评估它的使用情况(13%)。我们解读这一发现意味着,许多 Go 开发者最初将 Go 视为“技能提升”或拓展其软件开发理解的一部分,但在一两年内,他们更多地将 Go 视为一种实际应用工具而非学习工具。 + +![image-20231217112738341](C:\Users\zhengxm\Documents\notes\Go开发者调查\33.png) + +![image-20231217112927644](C:\Users\zhengxm\Documents\notes\Go开发者调查\34.png) + +Go 语言最常见的使用情景仍然是 API/RPC 服务(74%)和命令行工具(62%)。人们告诉我们,选择 Go 用于这些类型的软件有几个原因,包括其内置的 HTTP 服务器和并发原语、跨平台编译的便捷性,以及单一二进制部署的简易性。 + +这些工具的主要受众是商业领域(62%),有 17% 的受访者表示他们主要开发面向消费者的应用。这并不令人意外,因为相对于用于后端服务、命令行工具和云开发的广泛使用,Go 在消费者导向的应用(如桌面、移动或游戏)上的使用较少。但这个数据却有力地证实了在 B2B 环境中 Go 的广泛使用情况。 + +我们还根据受访者在 Go 使用经验和组织规模上的不同来寻找差异。更有经验的 Go 开发者表示他们在 Go 中构建了更多不同类型的应用和服务;这种趋势在每个应用或服务类别中都是一致的。根据组织规模,我们并未发现受访者在构建的应用和服务方面有明显的差异。 + +![image-20231217112946280](C:\Users\zhengxm\Documents\notes\Go开发者调查\35.png) + +![image-20231217113036351](C:\Users\zhengxm\Documents\notes\Go开发者调查\36.png) + +受访者在首次回答 Go 开发者调查和之前参与调查的比例大致相同。通过 Go 博客了解本次调查的人中,有 61% 报告曾经参与过此项调查,而通过 VS Code 的通知了解本次调查的人中,只有 31% 表示之前曾参与过。我们并不期望人们完美地记得他们在互联网上参与过的每一项调查,但这让我们对于每次调查都能听取到新旧受访者的均衡意见感到有些信心。此外,这也告诉我们,结合社交媒体发布和编辑器随机抽样对于听取到多元化的 Go 开发者意见是必要的。 + +![image-20231217113107813](C:\Users\zhengxm\Documents\notes\Go开发者调查\37.png) + +## 组织的特征和特性 + +调查对象称他们在各种不同类型的组织中工作,从千人以上的大型企业(27%),到中型企业(25%)和员工少于 100 人的小型组织(44%)不等。大约一半的受访者在科技行业工作(50%),比起下一个最常见的行业——金融服务行业——占 13%,这是一个显著增长。 + +这与过去几年的 Go 开发者调查数据统计基本保持不变——我们继续听取来自不同国家、不同规模和行业的人们的声音,比例保持了一致。 + +![image-20231217120123708](C:\Users\zhengxm\Documents\notes\Go开发者调查\38.png) + +![image-20231217120210172](C:\Users\zhengxm\Documents\notes\Go开发者调查\39.png) + +![image-20231217120226631](C:\Users\zhengxm\Documents\notes\Go开发者调查\40.png) + +## 调查方法 + +这次调查的大部分受访者是“自我选择”的,也就是说,他们是在 Go 博客或其他社交渠道上发现这个调查的。这种方法的潜在问题是,那些不关注这些渠道的人可能不太可能得知有关调查,而且他们的回答可能与那些关注这些渠道的人有所不同。约有 40% 的受访者是随机抽样的,也就是说,他们是在 VS Code 中看到调查提示后回答的(2023 年 7 月中旬至 8 月中旬,所有使用 VS Code Go 插件的人有 10% 的机会收到这个随机提示)。这个随机抽样的群体帮助我们将这些发现推广到更广泛的 Go 开发者群体中。 + +### 如何阅读这个结果 + +这份报告中我们使用了调查回答的图表来支持我们的发现。所有这些图表都采用了类似的格式。标题是受访者所见到的确切问题。除非另有说明,问题是多选的,参与者只能选择一个回答选项;每个图表的副标题将告诉读者问题是否允许多个回答选择,或者是一个开放式的文本框,而不是多选问题。对于开放式文本回答的图表,Go 团队成员读取并手动对回答进行了分类。许多开放式问题引发了各种各样的回答;为了保持图表的合理大小,我们将它们压缩到最多的前 10 个主题,其他主题都归为“其他”类别。图表中显示的百分比标签被舍入到最接近的整数(例如,1.4% 和 0.8% 都显示为 1%),但每个柱形和行的顺序都基于未舍入的值。 + +为了帮助读者了解每个发现背后的证据权重,我们包括了显示回答的 95% [置信区间](https://en.wikipedia.org/wiki/Confidence_interval)的误差条。较窄的条形表示更高的置信度。有时两个或更多个回答的误差条会重叠,这意味着这些回答的相对顺序在统计上没有意义(即这些回答在效果上是并列的)。每个图表的右下角显示了包含在图表中的回答者数量,格式为“n = [回答者数量]”。 + +我们引用了部分受访者的语录,以帮助澄清我们的许多发现。这些引用包括受访者使用 Go 的时长。如果受访者表示他们在工作中使用 Go,则称他们为“专业 Go 开发者”;如果他们在工作中不使用 Go,但在开源开发中使用 Go,则称他们为“开源 Go 开发者”。 + +## 结语 + +调查的最后一个问题总是询问受访者是否有关于 Go 想与我们分享的任何其他信息。提供的最常见反馈是“谢谢!”这一点在今年也不例外(33%)。在语言改进方面,我们看到改进表达能力(12%)、改进错误处理(12%)和提高类型安全性或可靠性(9%)三者之间存在统计上的并列。对于提高表达能力,受访者提出了各种各样的想法,总的趋势是“这是我经常写的一个具体事物,我希望在 Go 中更容易表达”。有关错误处理的问题仍然是对当前代码冗长性的抱怨,而有关类型安全性的反馈则最常涉及[和类型](https://en.wikipedia.org/wiki/Tagged_union)。这种高层次的反馈对于 Go 团队在未来一年制定重点领域时非常有用,因为它告诉我们社区希望引导生态系统的一般方向。 + +> “我知道 Go 对简洁性的态度,我很欣赏。我只是希望它有一些更多的特性。对我来说,更好的错误处理(不过不是异常),也许一些常见的便利功能,比如 map/reduce/filter 和三元运算符。任何不太晦涩的东西,可以帮我省下一些‘if’语句。” — 有 1 - 2 年经验的专业 Go 开发者 +> +> “请保持 Go 与早些时候确定的长期价值观一致 —— 语言和库的稳定性。[……] 这是一个我可以依赖的环境,它不会在 2 到 3 年后破坏我的代码。为此,非常感谢。” — 有 10 年以上经验的专业 Go 开发者 + +![image-20231217120430504](C:\Users\zhengxm\Documents\notes\Go开发者调查\41.png) + +以上就是 Go 开发者调查的全部内容。感谢每一个分享他们对 Go 的反馈的人,我们非常感谢你花时间帮助塑造 Go 的未来,我们希望你能在这份报告中看到你自己的一些反馈。🩵 + +——Todd(代表 Google 的 Go 团队) diff --git a/static/images/2023/w28/1.png b/static/images/2023/w28/1.png new file mode 100644 index 0000000..74f5e07 Binary files /dev/null and b/static/images/2023/w28/1.png differ diff --git a/static/images/2023/w28/10.png b/static/images/2023/w28/10.png new file mode 100644 index 0000000..73d047d Binary files /dev/null and b/static/images/2023/w28/10.png differ diff --git a/static/images/2023/w28/11.png b/static/images/2023/w28/11.png new file mode 100644 index 0000000..7dc07e7 Binary files /dev/null and b/static/images/2023/w28/11.png differ diff --git a/static/images/2023/w28/12.png b/static/images/2023/w28/12.png new file mode 100644 index 0000000..79d5f85 Binary files /dev/null and b/static/images/2023/w28/12.png differ diff --git a/static/images/2023/w28/13.png b/static/images/2023/w28/13.png new file mode 100644 index 0000000..ddb0124 Binary files /dev/null and b/static/images/2023/w28/13.png differ diff --git a/static/images/2023/w28/14.png b/static/images/2023/w28/14.png new file mode 100644 index 0000000..fc38747 Binary files /dev/null and b/static/images/2023/w28/14.png differ diff --git a/static/images/2023/w28/15.png b/static/images/2023/w28/15.png new file mode 100644 index 0000000..e7cd95d Binary files /dev/null and b/static/images/2023/w28/15.png differ diff --git a/static/images/2023/w28/16.png b/static/images/2023/w28/16.png new file mode 100644 index 0000000..09d4da3 Binary files /dev/null and b/static/images/2023/w28/16.png differ diff --git a/static/images/2023/w28/17.png b/static/images/2023/w28/17.png new file mode 100644 index 0000000..cc78266 Binary files /dev/null and b/static/images/2023/w28/17.png differ diff --git a/static/images/2023/w28/18.png b/static/images/2023/w28/18.png new file mode 100644 index 0000000..5b351e3 Binary files /dev/null and b/static/images/2023/w28/18.png differ diff --git a/static/images/2023/w28/19.png b/static/images/2023/w28/19.png new file mode 100644 index 0000000..cba6ecb Binary files /dev/null and b/static/images/2023/w28/19.png differ diff --git a/static/images/2023/w28/2.png b/static/images/2023/w28/2.png new file mode 100644 index 0000000..1488447 Binary files /dev/null and b/static/images/2023/w28/2.png differ diff --git a/static/images/2023/w28/20.png b/static/images/2023/w28/20.png new file mode 100644 index 0000000..c56a40e Binary files /dev/null and b/static/images/2023/w28/20.png differ diff --git a/static/images/2023/w28/21.png b/static/images/2023/w28/21.png new file mode 100644 index 0000000..5ff796e Binary files /dev/null and b/static/images/2023/w28/21.png differ diff --git a/static/images/2023/w28/22.png b/static/images/2023/w28/22.png new file mode 100644 index 0000000..75f045c Binary files /dev/null and b/static/images/2023/w28/22.png differ diff --git a/static/images/2023/w28/23.png b/static/images/2023/w28/23.png new file mode 100644 index 0000000..c70d71d Binary files /dev/null and b/static/images/2023/w28/23.png differ diff --git a/static/images/2023/w28/24.png b/static/images/2023/w28/24.png new file mode 100644 index 0000000..cfafb74 Binary files /dev/null and b/static/images/2023/w28/24.png differ diff --git a/static/images/2023/w28/25.png b/static/images/2023/w28/25.png new file mode 100644 index 0000000..af2cf9d Binary files /dev/null and b/static/images/2023/w28/25.png differ diff --git a/static/images/2023/w28/26.png b/static/images/2023/w28/26.png new file mode 100644 index 0000000..8624100 Binary files /dev/null and b/static/images/2023/w28/26.png differ diff --git a/static/images/2023/w28/27.png b/static/images/2023/w28/27.png new file mode 100644 index 0000000..e4a6191 Binary files /dev/null and b/static/images/2023/w28/27.png differ diff --git a/static/images/2023/w28/28.png b/static/images/2023/w28/28.png new file mode 100644 index 0000000..beba7b0 Binary files /dev/null and b/static/images/2023/w28/28.png differ diff --git a/static/images/2023/w28/29.png b/static/images/2023/w28/29.png new file mode 100644 index 0000000..08d1e2c Binary files /dev/null and b/static/images/2023/w28/29.png differ diff --git a/static/images/2023/w28/3.png b/static/images/2023/w28/3.png new file mode 100644 index 0000000..ea1721f Binary files /dev/null and b/static/images/2023/w28/3.png differ diff --git a/static/images/2023/w28/30.png b/static/images/2023/w28/30.png new file mode 100644 index 0000000..21a4285 Binary files /dev/null and b/static/images/2023/w28/30.png differ diff --git a/static/images/2023/w28/31.png b/static/images/2023/w28/31.png new file mode 100644 index 0000000..b5c278d Binary files /dev/null and b/static/images/2023/w28/31.png differ diff --git a/static/images/2023/w28/32.png b/static/images/2023/w28/32.png new file mode 100644 index 0000000..ebcc490 Binary files /dev/null and b/static/images/2023/w28/32.png differ diff --git a/static/images/2023/w28/33.png b/static/images/2023/w28/33.png new file mode 100644 index 0000000..0e81571 Binary files /dev/null and b/static/images/2023/w28/33.png differ diff --git a/static/images/2023/w28/34.png b/static/images/2023/w28/34.png new file mode 100644 index 0000000..9b3e40b Binary files /dev/null and b/static/images/2023/w28/34.png differ diff --git a/static/images/2023/w28/35.png b/static/images/2023/w28/35.png new file mode 100644 index 0000000..fa208ae Binary files /dev/null and b/static/images/2023/w28/35.png differ diff --git a/static/images/2023/w28/36.png b/static/images/2023/w28/36.png new file mode 100644 index 0000000..261b9dd Binary files /dev/null and b/static/images/2023/w28/36.png differ diff --git a/static/images/2023/w28/37.png b/static/images/2023/w28/37.png new file mode 100644 index 0000000..66f0b7b Binary files /dev/null and b/static/images/2023/w28/37.png differ diff --git a/static/images/2023/w28/38.png b/static/images/2023/w28/38.png new file mode 100644 index 0000000..9238771 Binary files /dev/null and b/static/images/2023/w28/38.png differ diff --git a/static/images/2023/w28/39.png b/static/images/2023/w28/39.png new file mode 100644 index 0000000..d52829c Binary files /dev/null and b/static/images/2023/w28/39.png differ diff --git a/static/images/2023/w28/4.png b/static/images/2023/w28/4.png new file mode 100644 index 0000000..77c5078 Binary files /dev/null and b/static/images/2023/w28/4.png differ diff --git a/static/images/2023/w28/40.png b/static/images/2023/w28/40.png new file mode 100644 index 0000000..88c5323 Binary files /dev/null and b/static/images/2023/w28/40.png differ diff --git a/static/images/2023/w28/41.png b/static/images/2023/w28/41.png new file mode 100644 index 0000000..7ae97d4 Binary files /dev/null and b/static/images/2023/w28/41.png differ diff --git a/static/images/2023/w28/5.png b/static/images/2023/w28/5.png new file mode 100644 index 0000000..1899471 Binary files /dev/null and b/static/images/2023/w28/5.png differ diff --git a/static/images/2023/w28/6.png b/static/images/2023/w28/6.png new file mode 100644 index 0000000..4714098 Binary files /dev/null and b/static/images/2023/w28/6.png differ diff --git a/static/images/2023/w28/7.png b/static/images/2023/w28/7.png new file mode 100644 index 0000000..73beacb Binary files /dev/null and b/static/images/2023/w28/7.png differ diff --git a/static/images/2023/w28/8.png b/static/images/2023/w28/8.png new file mode 100644 index 0000000..a80581b Binary files /dev/null and b/static/images/2023/w28/8.png differ diff --git a/static/images/2023/w28/9.png b/static/images/2023/w28/9.png new file mode 100644 index 0000000..b5524ec Binary files /dev/null and b/static/images/2023/w28/9.png differ