那些年我们一起用过的golang第三方库
引言
使用go做开发, 有一些会经常用到的三方库, 今天来盘点一下;
以下不分先后分类 祝各位同学看个开心.
优秀的第三方库
cobra
cobra : 如果你想要使用go做一个稍复杂的命令行工具, 那么cobra应该是一个不错的选择;
快速入门看他的官方文档即可: https://github.com/spf13/cobra/blob/main/site/content/user_guide.md
viper
在正式的项目里, 通常都有大量的配置需要在启动时或者运行时注入, 那么配置加载是必不可少的;
直接按他README里介绍的这几句代码就明白他的主要功能了:
viper.SetConfigName("config") // name of config file (without extension)
viper.SetConfigType("yaml") // REQUIRED if the config file does not have the extension in the name
viper.AddConfigPath("/etc/appname/") // path to look for the config file in
viper.AddConfigPath("$HOME/.appname") // call multiple times to add many search paths
viper.AddConfigPath(".") // optionally look for config in the working directory
err := viper.ReadInConfig() // Find and read the config file
if err != nil { // Handle errors reading the config file
panic(fmt.Errorf("fatal error config file: %w", err))
}
另外viper还支持配置热加载(即监听配置文件变化), 这样就可以在程序运行时根据配置变化来实现不同的逻辑:
viper.OnConfigChange(func(e fsnotify.Event) {
fmt.Println("Config file changed:", e.Name)
})
viper.WatchConfig()
lo
前端界非常有名的工具函数库: lodash , go这块也有类似的一个:
lo
在go支持泛型后, 这个库更加好用.
什么数组去重, slice转map等这种之前都要一堆for + map来手动实现的, 现在直接使用lo吧, 方便快捷, 使你专注主要逻辑, 示例:
// 数组去重
names := lo.Uniq([]string{"Samuel", "John", "Samuel"})
// []string{"Samuel", "John"}
singleflight
singleflight 如果你的程序有高并发操作, 那这个可能会用的上.
比如你有一个业务接口, 并发调用量很大, 但是很多调用可能都是重复的(参数啥的完全一样), 你期望的是 重复函数的并发调用合并为一次, 这样对底层的数据库资源可以减少访问;
示例:
package main
import (
"fmt"
"golang.org/x/sync/singleflight"
"time"
)
func getData(id int64) string {
fmt.Println("query...")
time.Sleep(10 * time.Second) // 模拟一个比较耗时的操作,比如可以是去mysql里做一个复杂的查询操作
return "real data"
}
func main() {
g := new(singleflight.Group)
// 第1次调用
go func() {
v1, _, shared := g.Do("getData", func() (interface{}, error) {
ret := getData(1)
return ret, nil
})
fmt.Printf("1st call: v1:%v, shared:%v\n", v1, shared)
}()
time.Sleep(2 * time.Second)
// 第2次调用(第1次调用已开始但未结束)
v2, _, shared := g.Do("getData", func() (interface{}, error) {
ret := getData(1)
return ret, nil
})
fmt.Printf("2nd call: v2:%v, shared:%v\n", v2, shared)
}
执行结果:
query…
1st call: v1:real data, shared:true
2nd call: v2:real data, shared:true
从输出可以看到getData函数只执行了一次(只打印了一次query...),但是两次调用都拿到了结果(real data)。
这就是singleflight包提供给我们的能力,避免了同时执行重复的函数。
wire
wire: “Compile-time Dependency Injection for Go”, 编译时的依赖注入工具, 无性能损耗;
快速入门看其官方示例即可: https://github.com/google/wire/blob/main/_tutorial/README.md
newc
[newc] https://github.com/Bin-Huang/newc 是一个通过 go generate 指令生成"构造函数"的工具; 看个示例立刻明白:
//go:generate newc
type UserService struct {
baseService
userRepository *repositories.UserRepository
proRepository *repositories.ProRepository
}
如果你用的Golang或者VsCode之类的集成开发工具, 会出现
run go generate ./... | run go generate 之类的按钮, 或者你可以使用命令行:
go generate ./...
在一切正常的情况下, 你会得到和相应文件同级的一个文件: “constructor_gen.go”
// Code generated by github.com/Bin-Huang/newc; DO NOT EDIT.
// NewUserService Create a new UserService
func NewUserService(baseService baseService, userRepository *repositories.UserRepository, proRepository *repositories.ProRepository) *UserService {
return &UserService{
baseService: baseService,
userRepository: userRepository,
proRepository: proRepository,
}
}
是不是很方便? 再也不用写NewXXX(…)这种无聊的函数了, 关键是结构体多一个成员(通常是其他依赖), 很容易忘记在NewXXX里添加对应的参数及实现;
不过目前的newc还不是很完美,不过一般的情况也够用了, 实现不行就自己手写NewXXX(…);
作者建议, 如果要团队内统一规范, 推荐这样:
//go:generate go run github.com/Bin-Huang/newc@v0.8.3
type UserService struct {
baseService
userRepository *repositories.UserRepository
proRepository *repositories.ProRepository
}
笔者在此基础上改造了下, 使得init方法支持返回error:
https://github.com/GerryLon/newc, 欢迎使用:

wire+newc
看了上面的newc, 有会可能会说, 这手写也行, 没必要引入一个新的工具进来, 增加团队成员的理解成本;
一开始我也觉得是这样, 直到我看到这篇文章: Wire:Go最优雅的依赖注入工具
后, 我才觉得引用newc太有必要了:
我要完全自动管理依赖, 不要人为管理这些依赖(自己也有手动写NewXXX导致依赖为nil的情况…)
不定期更新, 欢迎补充指正
魔乐社区(Modelers.cn) 是一个中立、公益的人工智能社区,提供人工智能工具、模型、数据的托管、展示与应用协同服务,为人工智能开发及爱好者搭建开放的学习交流平台。社区通过理事会方式运作,由全产业链共同建设、共同运营、共同享有,推动国产AI生态繁荣发展。
更多推荐


所有评论(0)