200行代码,基于 Go 1.18 Generics 封装一个异步的buffer

126 阅读1分钟

最近学习了 go1.18 的泛型,正好有个需求,flush 用户的实时日志到时序数据库,使用的时序数据库非常建议批量插入以提高性能,所以花点时间写了这样的一个库。

仓库地址如下:

woorui/async-buffer: Generic Asynchronous data buffer with Automatic and Manual flushing (github.com)

安装

go get -u github.com/woorui/async-buffer

主要的api 和 使用方法

package main

import (
	"errors"
	"fmt"
	"time"

	buffer "github.com/woorui/async-buffer"
)

type printer struct{}

func (p *printer) Flush(strs ...string) error {
	fmt.Printf("printer flush elements: %v, flush size: %d \n", strs, len(strs))
	return nil
}

func main() {
	buf, errch := buffer.New[string](6, 3*time.Second, &printer{})

	// 如果不需要处理 flush 的错误,这里可以忽略errch
	go errHandle(errch)

	// 1. 到达阈值的 flush
	buf.Write("a", "b", "c", "d", "e", "f")
	// Output
	// printer flush elements: [a b c d e f], flush size: 6

	// 2. 时间到了的 flush
	buf.Write("aaaaa")
	buf.Write("bbbbb")
	buf.Write("ccccc", "ddddd")
	time.Sleep(5 * time.Second)
	// Output
	// printer flush elements: [aaaaa bbbbb ccccc ddddd], flush size: 4

	// 3. 手动 flush
	buf.Write("eeeee", "fffff")
	buf.Flush()
	// Output
	// printer flush elements: [eeeee fffff], flush size: 2

	// waiting...
	select {}
}

func errHandle(errch <-chan error) {
	for {
		select {
		case err := <-errch:
			fmt.Printf("flush err %v \n", err)
			if se := new(buffer.ErrFlush[string]); errors.As(err, se) {
				fmt.Printf("flush err backup %v \n", se.Backup)
			}
		}
	}
}

api 很简单,主要是写需求的同时,练习泛型的api,

有了泛型,相交于之前版本的go,写出来的 buffer 表现力更强