Go errgroup

我们知道在 go 语言中很容易开启携程进行并发任务,但是如何处理并发过程中的错误是非常棘手的,接下来我就来介绍 errgroup 的用法。 errgroup errgroup 包里主要是一个结构体和相应的方法,它可以让你在并发任务中处理错误。 type Group struct { // context 的 cancel 方法 cancel func() // 复用 WaitGroup wg sync.WaitGroup sem chan token // 用来保证只会接受一次错误 errOnce sync.Once // 保存第一个返回的错误 err error } func WithContext(ctx context.Context) (*Group, context.Context) func (g *Group) done() func (g *Group) Wait() error func (g *Group) Go(f func() error) func (g *Group) TryGo(f func() error) bool func (g *Group) SetLimit(n int) 通过 WithContext 可以创建一个可以取消的 Group,当然除此之外也可以零值的 Group 也可以直接使用,但是出错之后就不会取消其他的 goroutine 了。Go 方法 传入一个函数参数,会启动一个 goroutine 处理。 Wait 类似 WaitGroup 的 Wait 方法,等待所有的 goroutine 结束后退出,返回的错误是一个出错的 err。 TryGo 是和 SetLimit 配套的,只有当 group 中的 goroutines 数量小于配置的数量时,才会在 goroutine 中调用函数。TryGo 用来判断 goroutine 是否启动。 ...

五月 21, 2022 · 1 分钟 · overstarry

应用内存升高原因排查

起因 最近一个部署了 go 应用的服务器出现了 OOM 的现象,内存占用过高。 原因 通过 Pyroscope 分析得出是因为 Minio 的 go sdk 中的 PutObject 函数占用了大量的内存。Pyroscope 是什么,前面的文章已经介绍过了,这里就不过多介绍了。 接下来我们通过查看相关的源码来查看是什么原因。 // PutObject creates an object in a bucket. // // You must have WRITE permissions on a bucket to create an object. // // - For size smaller than 16MiB PutObject automatically does a // single atomic PUT operation. // // - For size larger than 16MiB PutObject automatically does a // multipart upload operation. // // - For size input as -1 PutObject does a multipart Put operation // until input stream reaches EOF. Maximum object size that can // be uploaded through this operation will be 5TiB. // // WARNING: Passing down '-1' will use memory and these cannot // be reused for best outcomes for PutObject(), pass the size always. // // NOTE: Upon errors during upload multipart operation is entirely aborted. func (c *Client) PutObject(ctx context.Context, bucketName, objectName string, reader io.Reader, objectSize int64, opts PutObjectOptions, ) (info UploadInfo, err error) { if objectSize < 0 && opts.DisableMultipart { return UploadInfo{}, errors.New("object size must be provided with disable multipart upload") } err = opts.validate() if err != nil { return UploadInfo{}, err } return c.putObjectCommon(ctx, bucketName, objectName, reader, objectSize, opts) } 从方法的注释可以看出,当传递的大小为 -1 时,会进行多次 put 操作,直到输入流结束。多次 put 操作的最大大小为 5TiB,并且不能重用内存,导致占用大量内存。 ...

五月 14, 2022 · 3 分钟 · overstarry

多平台博客发布工具 openwrite 使用

介绍 OpenWrite 是一款便捷的多平台博客发布工具,可以在 OpenWrite 编写 markdown 文档,然后发布到其他博客平台,目前已经支持 CSDN、SegmentFault、掘金、博客园、简书等知名平台。 使用 注册 进入 http://admin.openwrite.cn/, 注册并登陆账号。 安装浏览器插件 第一步需要安装浏览器插件,根据这个链接教程安装,只有安装了插件,才能配置分发的平台。根据对插件的源码的分析,插件是通过 cookie、storage 等浏览器数据进行认证登陆的。此插件还包括模拟写作和 mackdown 编辑器。 认证配置渠道 安装完插件后,点击渠道管理,可以看到可配置的平台,支持多个平台。相应的平台如果没有登陆则显示未登陆,这时需要你登陆相应的平台。登陆平台后,点击认证,即可配置渠道。 这里以掘金为例,认证成功后,可以进行相应的配置,配置文章分类和标题。 发布文章 点击文章管理界面,添加文章,添加文章后,可以选择配置的平台,并发布到相应的平台。 普通用户一个月只有 10 次机会,一个平台占用一次机会。

五月 7, 2022 · 1 分钟 · overstarry

Buf 初入门 2

上文讲了 Buf lint 命令的基础方法,本文将介绍 Buf lint 命令的一些常用配置。 如果你的项目中没有 buf.yaml 配置文件,Buf lint 会提供一个默认的配置文件,默认内容如下: version: v1 lint: use: - DEFAULT except: - FILE_LOWER_SNAKE_CASE ignore: - bat - ban/ban.proto ignore_only: ENUM_PASCAL_CASE: - foo/foo.proto - bar BASIC: - foo enum_zero_value_suffix: _UNSPECIFIED rpc_allow_same_request_response: false rpc_allow_google_protobuf_empty_requests: false rpc_allow_google_protobuf_empty_responses: false service_suffix: Service allow_comment_ignores: true 配置选项 接下来开始介绍 Buf lint 命令的配置选项。 use use 选项配置 lint 的类别,不同类别有相应的规则,有多种类别:DEFAULT、FILE_LOWER_SNAKE_CASE、BASIC 等。默认是 DEFAULT 类别。 except except 选项配置 lint 不使用的类别。 下面的配置,显示使用 DEFAULT 类别,但是不使用 ENUM_NO_ALLOW_ALIAS ,BASIC 类别中的规则。 version: v1 lint: use: - DEFAULT except: - ENUM_NO_ALLOW_ALIAS - BASIC ignore ignore 选项指定忽略的文件,可以是文件名,也可以是目录,注意路径是要相对于 buf.yaml 文件的。 ...

五月 4, 2022 · 1 分钟 · overstarry

Buf 初入门 1

Buf 的目标是将 API 开发转向模式驱动的范式,从而为未来铺平道路,使 API 以服务所有者和客户可以依赖的方式定义。 与简单地暴露 REST/JSON 服务相比,使用 IDL 来定义 API 有很多好处,今天,Protobuf 是业界最稳定、最广泛采用的 IDL。但就目前的情况来看,使用 Protobuf 比使用 JSON 作为数据传输格式要困难得多。 Buf 正在建立工具,使 Protobuf 对服务所有者和客户来说是可靠和友好的,同时保持它在技术上的明显优势。您的组织不需要重新发明轮子来高效地创建、维护和使用 Protobuf API。我们将为您处理您的 Protobuf 管理策略,因此您可以专注于重要的事情。 本篇文章是讲述 buf 使用的第一篇文章,主要讲解使用 buf 定义 proto 文件的 lint 规则。统一的 lint 规则,可以让个人或团队定义的 API 保持一致。 lint 1 创建 buf.yaml 文件 使用 buf mod init 命令创建 buf.yaml 文件。 version: v1 breaking: use: - FILE lint: use: - DEFAULT 使用默认 lint 规则。 2 运行 lint 命令 使用 buf lint 命令运行 lint。 ...

四月 23, 2022 · 10 分钟 · overstarry

Go 统计代码测试覆盖率

今天来讲一讲如何统计 go 代码的测试覆盖率,主要是 cover 命令。 cover 基本用法 1 先简单写个函数和相应的测试,代码如下: func Max(a, b int) int { if a > b { return a } return b } 这个函数就是简单的比较大小,如果 a > b,返回 a,否则返回 b。 测试代码如下 package main import "testing" func TestMax(t *testing.T) { type args struct { a int b int } tests := []struct { name string args args want int }{ { name: "a is larger than b", args: args{ a: 2, b: 1, }, want: 2, }, { name: "b is larger than a", args: args{ a: 1, b: 2, }, want: 2, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := Max(tt.args.a, tt.args.b); got != tt.want { t.Errorf("Max() = %v, want %v", got, tt.want) } }) } } 2 go test -cover 能够统计出代码测试的覆盖率,这是一种比统计函数是否被调用更强悍的手法。我们执行这个命令。输出如下: ...

四月 16, 2022 · 1 分钟 · overstarry

Kswapd0 is consuming a lot of CPU.

Issue Recently, a Linux server has been experiencing high CPU usage, causing the server to become unresponsive and other processes to fail to execute smoothly. By using the top command to check resource usage, it can be seen that the kswapd0 process is consuming a large amount of CPU resources. Cause and solution Based on my research, the kswapd0 process is responsible for managing virtual memory. Typically, a Linux system comprises RAM, swap, and EXT4 components. The EXT4 partition is used to store regular files and can be created on either an HDD or an SSD, though it is relatively slower compared to RAM. RAM, commonly referred to as memory, is used for high-speed program execution. The swap partition, on the other hand, is used as additional virtual memory and is normally allocated on physical disks, especially on machines with limited physical RAM. ...

四月 9, 2022 · 2 分钟 · overstarry

Kswapd0 消耗大量 cpu

问题 最近一台 Linux 服务器经常出现 cpu 过高,导致服务器卡顿,其他进程不能顺利进行。通过 top 命令查看资源的占用情况,可以看到 Kswapd0 进程 消耗了大量的 cpu 资源。 原因及解决 通过搜索得知,Kswapd0 进程是用来管理虚拟内存的。 一般的 Linux 都会有 RAM,swap, 和 EXT4 这几个部分,EXT4 分区就是用来存放一般的文件,可以在机械硬盘或者 SSD 上划分出 ext4 分区来保存文件,相对 RAM(内存)来说要稍微慢一些,RAM 就是日常所说的内存,用来做程序运行时的高速缓存,而 SWAP 是交换分区,一般在物理内存比较小的机器上会划分一块物理磁盘来作为 swap 分区。 swap 分区是一款虚拟的 RAM,一般在 HDD/SSD 上,当物理内存比较小的时侯,可能经常缺内存,那么系统就会使用 swap 分区,将物理内存中的内容搬迁到 swap 分区中暂存。当可用物理内存比较小的时候,kswapd0 进程就会将相对比较不常用的程序移动到 swap 分区中,这个时候就可能造成这些程序比较卡顿。例如一台机器的内存是 4G,而要运行一个需要 5G 内存的程序,那么至少有 1G 的内存会放到 swap 分区中。kswapd0 移动内存的过程就会造成 CPU 的大量使用。要解决这个问题有这样几个方式。 1 修改 /etc/sysctl.conf 文件 echo vm.swappiness=0 | sudo tee -a /etc/sysctl.conf ...

四月 9, 2022 · 1 分钟 · overstarry

Postgresql 修改序列产生器

问题 在我日常使用 PostgreSQL 数据库过程中,会遇到一个问题:我在重新创建一个数据库表时,往往会导入已有的数据,这样会导致新增表数据时,由于 id 采用了自增,会从 1 开始生成,然后由于已有数据的缘故,所以会导致 id 重复报错。 解决 查看了 stackoverflow 的一些回答,发现了一个解决方案:采用 ALTER SEQUENCE 语句进行修改。 ALTER SEQUENCE ALTER SEQUENCE — 更改序列生成器的定义 语法: ALTER SEQUENCE [ IF EXISTS ] name [ AS data_type ] [ INCREMENT [ BY ] increment ] [ MINVALUE minvalue | NO MINVALUE ] [ MAXVALUE maxvalue | NO MAXVALUE ] [ START [ WITH ] start ] [ RESTART [ [ WITH ] restart ] ] [ CACHE cache ] [ [ NO ] CYCLE ] [ OWNED BY { table_name.column_name | NONE } ] ALTER SEQUENCE [ IF EXISTS ] name OWNER TO { new_owner | CURRENT_ROLE | CURRENT_USER | SESSION_USER } ALTER SEQUENCE [ IF EXISTS ] name RENAME TO new_name ALTER SEQUENCE [ IF EXISTS ] name SET SCHEMA new_schema 参数: ...

四月 6, 2022 · 2 分钟 · overstarry

Kratos 业务状态码和 HTTP 状态码分离

起因 在我们使用 kratos 开发 web 应用时,由于 kratos 默认设计导致我们在自定义错误时,访问 api 时,相应的状态码是以自定义的 error code 作为 http status code 表现的。 如果我们想将 业务状态码和 HTTP 状态码分离,即所有的 http status code 都为 200,实际的业务状态码以在 response 中响应的 code 为主。 我们该如何实现呢?有 2 种方法,一种是自定义中间件将错误进行特别处理,另一种是自定义 Encoder。本篇文章主要就是介绍 Encoder 方法。 自定义 Encoder 根据我们的需要,我们需要自定义 2 个 Encoder, ErrorEncoder 和 ResponseEncoder 函数。需要的参数类型分别如下: // ResponseEncoder with response encoder. func ResponseEncoder(en EncodeResponseFunc) ServerOption { return func(o *Server) { o.enc = en } } // ErrorEncoder with error encoder. func ErrorEncoder(en EncodeErrorFunc) ServerOption { return func(o *Server) { o.ene = en } } // EncodeResponseFunc is encode response func. type EncodeResponseFunc func(http.ResponseWriter, *http.Request, interface{}) error // EncodeErrorFunc is encode error func. type EncodeErrorFunc func(http.ResponseWriter, *http.Request, error) 1 自定义 Response 结构 定义一个 Response 的结构体 ...

三月 27, 2022 · 2 分钟 · overstarry