记录常用的各种工具

用于Go项目 名称 描述 gopkgview 交互式的基于Web的Go包依赖图可视化 gup 更新由go install安装的二进制文件 gsa 分析编译后的Go二进制文件大小的工具,可以清晰的了解每个包或依赖在二进制中占用的大小 mockgen gomock是Go编程语言的模拟框架 nilaway 静态分析工具,用于检测Go代码中的潜在nil恐慌 命令行 名称 描述 bat cat的现代替代品 curlie curl的力量,httpie的使用便捷性 croc 轻松安全地将内容从一台计算机发送到另一台计算机 difft 结构化差异,理解语法。也可用于git diff direnv 加载或卸载当前目录(以及目录)的.envrc或.env fastfetch 功能丰富、面向性能、类似 neofetch 的系统信息工具 fd find的现代替代品 fzf 命令行模糊查找器 grpcurl 与cURL类似,但针对gRPC:与gRPC服务器交互的命令行工具 htop top的替代品。一个交互式流程查看器 hyperfine 命令行基准测试工具 jq 命令行JSON处理器 lsd ls的现代替代品 mycli MySQL自动补全和语法高亮的终端客户端 ouch 支持多种格式的压缩和解压缩,tar、zip、7z、gz、rar等 pgcli Postgres自动补全和语法高亮的终端客户端 rg 一个面向行的搜索工具,它递归地搜索当前目录中的正则表达式模式 scc 一个类似于cloc、sloccount和tokei的工具。用于统计多种编程语言的代码行数、空白行、注释行和源代码的物理行数 sd sed的现代替代品 vivid 具有丰富文件类型数据库的主题化LS_COLORS生成器 zoxide 更智能的cd命令,支持jump App 名称 描述 iina 适用于macOS的现代视频播放器 mac-mouse-fix macOS平滑滚动

2024-11-25 · 1 分钟

十亿行挑战

十亿行挑战(1️⃣🐝🏎️ The One Billion Row Challenge) 原始仓库 文中源码仓库: https://github.com/zzhaolei/1brc 目标 文本文件包含了一系列气象站的温度值。每行是一个测量值,格式为<string: station name>;<double: measurement>,其中测量值精确到一位小数。以下是一些示例行: 1 2 3 4 5 6 7 8 9 10 Hamburg;12.0 Bulawayo;8.9 Palembang;38.8 St. John's;15.2 Cracow;12.6 Bridgetown;26.9 Istanbul;6.2 Roseau;34.4 Conakry;31.2 Istanbul;23.0 任务是编写一个程序,该程序读取文本文件,计算每个气象站的最低、平均和最高温度值,并将结果输出到stdout, 格式如下(按气象站名称字母顺序排序,并且每个气象站的结果值格式为<min>/<mean>/<max>,保留一位小数点): 1 {Abha=-23.0/18.0/59.2, Abidjan=-16.2/26.0/67.3, Abéché=-10.0/29.4/69.0, Accra=-10.1/26.4/66.4, Addis Ababa=-23.7/16.0/67.0, Adelaide=-27.8/17.3/58.5, ...} 限制 只能使用标准库实现。 生成十亿行挑战所需的数据 克隆原始仓库: 1 2 3 git clone https://github.com/gunnarmorling/1brc cd 1brc/src/main/python python3 create_measurements.py 1000000000 生成的数据会在1brc/measurements.txt,约为15Gi的大小。 ...

2024-11-23 · 4 分钟

Go 在 Mac 或 Linux 上构建动态库

Go 可以导出 C ABI,然后在其它兼容 C ABI 的语言中调用。 下面详细讲解一下用法: Go 构建动态库 定义一个 go 文件,包含以下代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 package main import "C" //export Add func Add(a, b int) int { return a + b } //export Multiply func Multiply(a, b int) int { return a * b } func main() {} go 的文件名称在当前示例中无关紧要,这里定义为 main.go。 main 函数是必须的,但是可以为空。 注意:代码中的 //export Add 表示导出 Add 函数,export 和 // 之间没有空格。这是 Go 中的一种特殊指令,类似的还有 //go:build 等。 ...

2024-09-11 · 2 分钟

使用AWS S3 SDK访问阿里云oss

目前业务上使用的是 aws 的 s3 服务,但是想兼容阿里云的 oss。根据oss的文档描述,oss支持使用 aws 的 sdk 进行访问,所以记录一下处理流程 访问AWS S3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 package main import ( "context" "log" "os" "github.com/aws/aws-sdk-go-v2/aws" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/credentials" "github.com/aws/aws-sdk-go-v2/service/s3" ) func NewS3Client() *s3.Client { accessKeyID := os.Getenv("ACCESS_KEY_ID") accessKeySecret := os.Getenv("ACCESS_KEY_SECRET") cfg, err := config.LoadDefaultConfig( context.TODO(), config.WithCredentialsProvider(credentials.NewStaticCredentialsProvider(accessKeyID, accessKeySecret, "")), config.WithEndpointResolverWithOptions( aws.EndpointResolverWithOptionsFunc(func(_, _ string, _ ...interface{}) (aws.Endpoint, error) { return aws.Endpoint{ PartitionID: "aws-cn", URL: "https://s3.cn-northwest-1.amazonaws.com.cn", SigningRegion: "cn-northwest-1", }, nil }), ), ) if err != nil { log.Fatal(err) } return s3.NewFromConfig(cfg, func(o *s3.Options) { // 此选项可用于调试 // o.ClientLogMode = aws.LogSigning | aws.LogRequest | aws.LogResponseWithBody o.UsePathStyle = true }) } func main() { bucket := os.Getenv("S3_BUCKET") uploadKey := os.Getenv("S3_KEY") file, _ := os.Open("test.txt") client := NewS3Client() _, err := client.PutObject(context.Background(), &s3.PutObjectInput{ Bucket: aws.String(bucket), Key: aws.String(uploadKey), Body: file, }) if err != nil { log.Fatal(err) } } 这是一个简单的 s3 文件上传,通过在 PutObjectInput 中指定Bucket 参数的形式。 ...

2024-09-01 · 3 分钟

Go设计模式——单例模式

介绍 单例模式同时解决了两个问题: 保证一个类只有一个实例,例如控制某些共享资源(如数据库或文件)的访问权限 为该实例提供一个全局访问节点 在Go中单例模式有两种实现,一种是饿汉式,一种是懒汉式。饿汉式简单,可以将问题及早暴露出来,懒汉式虽然支持延迟加载,但是也将可能的问题延迟到了第一次调用的时候,同时为了实现并发安全,也不得不加锁。 饿汉式 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // Package singleton package singleton // singleton 饿汉式 var singleton *File type File struct{} func init() { singleton = &File{} } func GetInstance() *File { return singleton } 单元测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package tests import ( "testing" "design-pattern/singleton" "github.com/stretchr/testify/assert" ) // TestSingleton 测试单例 func TestInstance(t *testing.T) { assert.Equal(t, singleton.GetInstance(), singleton.GetInstance()) } // BenchmarkSingleton 测试并发访问单例 func BenchmarkInstance(b *testing.B) { b.RunParallel(func(p *testing.PB) { for p.Next() { assert.Equal(b, singleton.GetInstance(), singleton.GetInstance()) } }) } 懒汉式 代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 // Package lazysingleton 懒汉式单例 package lazysingleton import "sync" var ( lazySingleton *File once sync.Once ) type File struct{} func GetLazyInstance() *File { if lazySingleton == nil { // 使用sync.Once来确保单例在并发时只被初始化一次 once.Do(func() { lazySingleton = &File{} }) } return lazySingleton } 单元测试 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 package tests import ( "testing" "design-pattern/lazysingleton" "github.com/stretchr/testify/assert" ) // TestLazySingleton 测试懒汉式单例 func TestLazyInstance(t *testing.T) { assert.Equal(t, lazysingleton.GetLazyInstance(), lazysingleton.GetLazyInstance()) } // BenchmarkGetLazyInstance 测试懒汉式单例 func BenchmarkGetLazyInstance(b *testing.B) { b.RunParallel(func(p *testing.PB) { for p.Next() { assert.Equal(b, lazysingleton.GetLazyInstance(), lazysingleton.GetLazyInstance()) } }) } 测试结果 1 2 3 4 5 6 7 8 ❯ go test -bench . goos: darwin goarch: arm64 pkg: design-pattern/tests BenchmarkGetLazyInstance-8 3113553 380.8 ns/op BenchmarkInstance-8 3156261 378.5 ns/op PASS ok design-pattern/tests 3.798s 根据bench结果可以发现加锁(sync.Once内部使用的atomic来进行处理)还是会对性能造成影响的,不过也在可接受范围内。 ...

2024-09-01 · 2 分钟

Go设计模式——开闭原则

介绍 简单的说就是:对扩展开放,对修改关闭。对扩展开放是为了应对需求的变化,对修改关闭就是为了保证已有代码的稳定性,最终是为了让系统更具有弹性,能更好的处理需求。 开闭原则也包含了单一职责原则。 我们以消息队列来进行举例。 坏的 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 // Package main 开闭原则 Open-Closed Principle // 开闭原则包含了:单一职责原则 package main import "fmt" type KafkaQueue struct{} func (k *KafkaQueue) SendMSG(msg string) error { fmt.Println("Kafka send msg success") return nil } type RabbitQueue struct{} func (r *RabbitQueue) SendMSG(msg string) error { fmt.Println("Rabbitmq send msg success") return nil } type Demo struct{} func (d *Demo) SendByKafka(queue KafkaQueue, msg string) error { return queue.SendMSG(msg) } func (d *Demo) SendByRabbit(queue RabbitQueue, msg string) error { return queue.SendMSG(msg) } func main() { } 通过这个例子,我们可以看出来,这段代码违背了我们的对扩展开放,对修改关闭的原则。当我们需要添加一个新的RocketMQ的时候,需要改动Demo的逻辑以及其他设计的业务逻辑,可扩展性可以说是一点也没有。 ...

2024-09-01 · 2 分钟

Go设计模式——单一职责原则

介绍 类的职责应该是单一的,对外只提供一种功能,而引起类变化的原因应该只有一个。简单的说就是每一个类只负责自己的事情,只有单一的功能。 我们现在以银行工作人员举例: 坏的 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 // Package main 单一职责原则 Single-Responsibility Principle package main import "fmt" type Banker struct{} // Save 存钱 func (b *Banker) Save(money uint64) error { fmt.Printf("成功存入: %d\n", money) return nil } // Transfer 转账 func (b *Banker) Transfer(money uint64, to string) error { fmt.Printf("成功向: %s转入: %d\n", to, money) return nil } 单一职责原则要求一个类/接口只有一个职责,而引起类变化的原因只能有一个。 从原则上讲,我们为Banker定义存钱和转账的操作是有道理的,因为我们接口中定义的都是银行工作人员可以执行的操作,引起变化的原因只能是Banker的属性和行为发生变化。 从这方便考虑,这种设计是有合理性的,如果能保证需求不会变化或者需求变化的可能行很小,那么这种设计就是合理的。 但是实际上我们知道,需求是不断变化的,今日增加一个股票业务,那么我们就需要增加一个股票的相关属性和行为,我们的接口和实现就需要全部变动。 最好的方式就是当我们开始定义的时候,根据属性和行为进行细分,抽象不同的接口出来,在Go里面也是主张小接口,这样我们可以通过组合的手段来随意构造我们想要的大接口。 好的 我们将Banker进行抽象,这样可以更好的进行扩展: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 // Package main 单一职责原则 Single-Responsibility Principle package main import "fmt" type Config struct { Money uint64 To string } type Banker interface { DoSomething(Config) error } type SaveBanker struct{} func (sb *SaveBanker) DoSomething(cfg Config) error { fmt.Printf("成功存入: %d\n", cfg.Money) return nil } type TransferBanker struct{} func (tb *TransferBanker) DoSomething(cfg Config) error { fmt.Printf("成功向: %s转入: %d\n", cfg.To, cfg.Money) return nil } 我们抽象出来了Banker接口,每一个不太的业务员都可以实现这个接口,对行为进行自定义。 ...

2024-09-01 · 1 分钟

如何在Go中使用POSIX命名信号量

go 本身提供的 semaphore 只能在同一个进程多个协程或线程间使用,无法在不同的 go 进程之间使用,所以本文介绍,如何使用 go 中的 syscall 来使用 POSIX 系统提供的命名信号量。 Go 中的系统调用 在 go 中,系统调用是通过 syscall 包提供的 Syscall 函数来进行系统调用的,不同的系统调用有不同的 trap,以及不同长度的参数。 trap go 在 syscall 包中定义了大量的系统调用码,具体定义在文件1.20.6/go/src/syscall/zsysnum_darwin_arm64.go 。不同操作系统上,定义所使用的文件是不同的,这些定义都是通过不同系统的c 语言头文件自动生成的。比如 linux amd64 操作系统的定义在1.20.6/go/src/syscall/zerrors_linux_amd64.go。 不同长度的参数 syscall 包有 Syscall、Syscall6 两个函数,对应于不同的操作系统调用参数长度的情况。 Syscall 总共接收 4 个参数,第一个是 trap 定义,描述具体的系统调用,剩下的 3 个是系统调用所需的参数。 Syscall6 总共接收 7 个参数,第一个是 trap 定义,描述具体的系统调用,剩下的 6 个是系统调用所需的参数。 如果使用 Syscall 或 Syscall6 时,系统调用所需的参数不满足函数形参所需的数量,则剩下的参数传0。 例如,在 POSIX 系统上打开一个命名信号量的系统调用是: 1 sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value); 因为系统调用的参数有 4 个,而 Syscall 接收的全部形参才 4 个,所以 Syscall 不能满足我们的需求,只能使用 Syscall6 这个函数。而 Syscall6 总共需要 7 个形参,其中有 6 个是系统调用参数,我们只有 4 个系统调用参数,那么剩下的 2 个系统调用参数,我们就可以使用 0 替代,例如: ...

2024-09-01 · 3 分钟

关于Go Modules的一些内容

启用Go Modules go mod在Go >= 1.13才默认启用,在Go >= 1.11已经开始支持了go mod。 设置环境变量 1 2 3 4 5 6 7 8 9 # 启用go module export GO111MODULE=on # 设置GOPATH,开启go mod之后,这个目录主要用来存放依赖包 export GOPATH=~/go_modules # 设置go代理,在运行go test/build等时会自动下载依赖 # 使用go get下载依赖需要在GOPATH中执行才会使用代理 export GOPROXY=https://goproxy.io go mod使用 在$GOPATH/src之外的任意目录创建一个目录, 1 2 mkdir -p /home/gopher/project cd /home/gopher/project 这个目录就是你项目的根目录,在目录中创建mod管理文件 1 go mod init project 如果你这个项目是放在github上的,那么在创建文件的时候可以这样写,project为你github项目名称 1 go mod init github.com/YourName/project go.mod的初始内容cat go.mod为: 1 2 3 module project go 1.12 go.mod只需要在项目的根目录创建一次即可,在项目中Go会自动查找当前目录的全部父级目录,直到找到go.mod。 ...

2019-06-01 · 1 分钟