├── .gitignore ├── sys ├── syncx │ ├── asm.s │ ├── linkname.go │ ├── rwmutex.go │ └── pool_race.go ├── nanotime │ ├── nanotime.s │ ├── nacotime.go │ └── nacotime_test.go ├── stringx │ ├── doc.go │ ├── README.md │ ├── is_test.go │ └── is.go ├── cpu │ ├── README.md │ ├── cpu_test.go │ ├── psutilcpu.go │ └── cpu.go ├── goid │ ├── gid_test.go │ └── gid.go ├── xxhash3 │ ├── accum.go │ ├── internal │ │ └── avo │ │ │ ├── gen.go │ │ │ └── build.sh │ ├── accum_amd64.go │ └── util.go ├── safe │ └── model.go ├── once │ └── once.go └── mutex │ ├── recursive_mutex_token.go │ ├── recursive_mutex.go │ └── mutex_test.go ├── log ├── log.go ├── value_test.go ├── std_test.go ├── logger_test.go ├── level.go └── std.go ├── internal ├── sys │ ├── cpu │ │ ├── README.md │ │ ├── cpu_test.go │ │ ├── psutilcpu.go │ │ └── cpu.go │ ├── goid │ │ ├── gid_test.go │ │ └── gid.go │ ├── safe │ │ └── model.go │ ├── hash │ │ └── hash.go │ ├── once │ │ └── once.go │ ├── mutex │ │ ├── recursive_mutex_token.go │ │ ├── recursive_mutex.go │ │ └── mutex_test.go │ └── safe_map │ │ └── map.go ├── internal.go ├── stat │ ├── stat.go │ ├── metric.go │ ├── iterator.go │ ├── rolling_counter.go │ ├── window_test.go │ └── reduce.go ├── runtimex │ ├── runtime.go │ ├── asm.s │ ├── ppin_test.go │ ├── ppin.go │ ├── readunaligned_bigendian.go │ └── readunaligned.go ├── clock │ └── ticker.go ├── hack │ └── hack.go ├── wyhash │ └── wyhash_test.go └── benchmark │ └── linkedq │ └── linkedq.go ├── CONTRIBUTING.md ├── egroup ├── egroup.go ├── conf.go ├── liftcycle_test.go ├── option.go └── example_test.go ├── concurrent ├── concurrent.go ├── pipeline.go ├── or_done_test.go ├── map_reduce.go ├── orderly.go ├── fan_out.go ├── or_done.go ├── orderly_test.go ├── fan_out_test.go ├── fan_in.go └── fan_in_test.go ├── overload ├── restrictor.go ├── bbr │ ├── code.go │ ├── example_test.go │ └── middle.go └── limiter.go ├── timeout ├── timeout.go ├── example_test.go ├── ctime_test.go ├── stamp.go ├── human.go ├── ctime.go ├── date.go ├── d_time.go └── datetime.go ├── cache ├── buffer │ ├── buffer.go │ ├── byte_pool_test.go │ ├── example_test.go │ └── iobuffer_pool.go ├── local_cache │ ├── local_cache.go │ ├── Iterator.go │ ├── err.go │ ├── sentinel.go │ └── option.go ├── mbuffer │ ├── README.md │ ├── utils.go │ └── utils_test.go └── singleflight │ ├── singleflight.go │ └── example_test.go ├── structure ├── skipmap │ ├── bench.sh │ ├── asm.s │ ├── util.go │ └── oparray.go ├── skipset │ ├── bench.sh │ ├── asm.s │ ├── util.go │ ├── flag_test.go │ └── oparry.go └── zset │ └── opt.go ├── distributed ├── controller │ ├── error.go │ └── controller.go ├── schedule │ ├── timing.go │ ├── constantdelay.go │ └── option.go ├── task │ ├── args.go │ ├── processor.go │ ├── validate.go │ ├── meta.go │ ├── error.go │ ├── workflow_test.go │ ├── result.go │ ├── result_test.go │ └── workflow.go ├── locker │ ├── locker.go │ └── lock_ridis │ │ └── option.go ├── broker │ └── option.go ├── retry │ ├── retry.go │ ├── fibonacci.go │ └── fibonacci_test.go ├── backend │ ├── backend_mongodb │ │ └── option.go │ └── backend.go ├── example │ └── worker │ │ └── worker.go └── option.go ├── options └── options.go ├── parser ├── parse.go ├── parse_pb │ ├── model_test.go │ ├── transform.go │ └── parse_pb.go ├── demo │ ├── test.proto │ └── demo.api └── parse_go │ ├── transform.go │ └── model_test.go ├── container ├── group │ ├── grouper.go │ ├── example_test.go │ ├── group.go │ └── group_test.go ├── queue │ └── codel │ │ └── example_test.go └── pool │ └── example_test.go ├── generator ├── generator.go └── example_test.go ├── tools ├── float │ └── float.go ├── bind │ ├── url.go │ ├── query.go │ ├── option.go │ ├── xml.go │ ├── yaml.go │ ├── internal │ │ ├── bytesconv │ │ │ └── bytesconv.go │ │ └── json │ │ │ ├── json.go │ │ │ └── jsoniter.go │ ├── msgpack.go │ ├── all.go │ ├── header.go │ ├── protobuf.go │ ├── form.go │ └── json.go ├── error.go ├── reflect2value │ └── err.go ├── pointer │ ├── time_time.go │ ├── string.go │ ├── bool.go │ ├── template.txt │ ├── time_duration.go │ └── type_gen.go ├── deepcopy │ └── deepcopy_test.go ├── rand_string │ └── rand_string.go ├── vto │ └── default.go └── stm │ └── stm.go ├── gctuner ├── mem.go ├── mem_test.go ├── finalizer_test.go └── finalizer.go ├── window ├── example_test.go ├── slidingwindow.go ├── bucket.go ├── init_test.go └── init.go ├── errors └── errors.proto ├── net ├── port │ └── port.go └── tcp │ ├── conf.go │ └── conn_func.go ├── goroutine ├── ggroup.go ├── goroutine_test.go ├── example_test.go └── option.go ├── metrics └── metrics.go ├── registry ├── instance.go ├── subset.go └── registry.go ├── coding ├── proto │ └── proto.go ├── xml │ └── xml.go ├── yaml │ └── yaml.go ├── coding.go └── json │ └── json.go ├── page_token ├── page_token.go ├── option.go └── token_test.go ├── restrictor ├── rate │ ├── rate.go │ ├── example_test.go │ └── rate_test.go ├── ratelimite │ ├── example_test.go │ ├── ratelimite.go │ └── ratelimite_test.go ├── client_throttling │ └── client_throttling.go └── limiter.go ├── watching ├── example │ ├── cpu_explode │ │ └── cpu_explode.go │ ├── 1gbslice │ │ └── 1gbslice.go │ ├── deadloop │ │ └── deadloop.go │ ├── channelblock │ │ └── channelblock.go │ ├── alloc │ │ └── alloc.go │ ├── slowlyleak │ │ └── slowlyleak.go │ ├── deadlock │ │ └── deadlock.go │ ├── run_in_docker │ │ └── run_in_docker.go │ ├── thread_trigger │ │ └── thread_trigger.go │ └── gcheap │ │ └── gcheap.go └── ring.go ├── middleware ├── middleware.go └── middleware_test.go ├── trace ├── option.go ├── stats_handler.go ├── metadata.go └── middle.go ├── delayed └── option.go ├── wgroup └── wgroup.go ├── downgrade ├── hystrix.go ├── downgrade.go └── example_test.go └── encrypt └── aes └── aes_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/* -------------------------------------------------------------------------------- /sys/syncx/asm.s: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sys/nanotime/nanotime.s: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sys/stringx/doc.go: -------------------------------------------------------------------------------- 1 | package stringx 2 | -------------------------------------------------------------------------------- /sys/cpu/README.md: -------------------------------------------------------------------------------- 1 | ## 获取Linux平台下的系统信息,包括cpu主频、cpu使用率等 -------------------------------------------------------------------------------- /log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | // package log: 规范日志接口 4 | -------------------------------------------------------------------------------- /internal/sys/cpu/README.md: -------------------------------------------------------------------------------- 1 | ## 获取Linux平台下的系统信息,包括cpu主频、cpu使用率等 -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | songzhibin97 2 | SliverHorn 3 | CC11001100 4 | ShuBo6 -------------------------------------------------------------------------------- /egroup/egroup.go: -------------------------------------------------------------------------------- 1 | package egroup 2 | 3 | // package egroup: 控制组件生命周期 4 | -------------------------------------------------------------------------------- /concurrent/concurrent.go: -------------------------------------------------------------------------------- 1 | package concurrent 2 | 3 | // concurrent 并发控制 4 | -------------------------------------------------------------------------------- /internal/internal.go: -------------------------------------------------------------------------------- 1 | package internal 2 | 3 | // package internal: 内部包 4 | -------------------------------------------------------------------------------- /internal/stat/stat.go: -------------------------------------------------------------------------------- 1 | package stat 2 | 3 | // package stat: 数据统计、监控采集 4 | -------------------------------------------------------------------------------- /overload/restrictor.go: -------------------------------------------------------------------------------- 1 | package overload 2 | 3 | // package overload: 过载保护 4 | -------------------------------------------------------------------------------- /timeout/timeout.go: -------------------------------------------------------------------------------- 1 | package timeout 2 | 3 | // package timeout: 管理组件间传递超时控制 4 | -------------------------------------------------------------------------------- /cache/buffer/buffer.go: -------------------------------------------------------------------------------- 1 | package buffer 2 | 3 | // package buffer: 4 | // Byte 复用 5 | -------------------------------------------------------------------------------- /overload/bbr/code.go: -------------------------------------------------------------------------------- 1 | package bbr 2 | 3 | import "errors" 4 | 5 | var LimitExceed = errors.New("509:过载保护") 6 | -------------------------------------------------------------------------------- /cache/local_cache/local_cache.go: -------------------------------------------------------------------------------- 1 | package local_cache 2 | 3 | // package local_cache 类似 memcached 构建本地缓存机制 4 | // 具有过期机制且并发安全 5 | -------------------------------------------------------------------------------- /structure/skipmap/bench.sh: -------------------------------------------------------------------------------- 1 | go test -run=NOTEST -bench=. -count=10 -timeout=60m -benchtime=100000x -benchmem > x.txt && benchstat x.txt -------------------------------------------------------------------------------- /sys/goid/gid_test.go: -------------------------------------------------------------------------------- 1 | package goid 2 | 3 | import "testing" 4 | 5 | func TestGetGID(t *testing.T) { 6 | t.Log(GetGID()) 7 | } 8 | -------------------------------------------------------------------------------- /structure/skipset/bench.sh: -------------------------------------------------------------------------------- 1 | go test -run=NOTEST -bench=. -benchtime=100000x -benchmem -count=10 -timeout=60m > x.txt && benchstat x.txt -------------------------------------------------------------------------------- /internal/sys/goid/gid_test.go: -------------------------------------------------------------------------------- 1 | package goid 2 | 3 | import "testing" 4 | 5 | func TestGetGID(t *testing.T) { 6 | t.Log(GetGID()) 7 | } 8 | -------------------------------------------------------------------------------- /distributed/controller/error.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import "errors" 4 | 5 | var ErrorConnectClose = errors.New("connect is closed") 6 | -------------------------------------------------------------------------------- /egroup/conf.go: -------------------------------------------------------------------------------- 1 | package egroup 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | const ( 8 | startTimeout = -1 9 | stopTimeout = 20 * time.Second 10 | ) 11 | -------------------------------------------------------------------------------- /options/options.go: -------------------------------------------------------------------------------- 1 | package options 2 | 3 | // package options: 选项模式汇总 4 | 5 | // Option 选项模式 6 | // Option(*Config{}) 7 | type Option func(o interface{}) 8 | -------------------------------------------------------------------------------- /distributed/schedule/timing.go: -------------------------------------------------------------------------------- 1 | package schedule 2 | 3 | import "time" 4 | 5 | // Schedule 返回下一次执行的时间 6 | type Schedule interface { 7 | Next(time time.Time) time.Time 8 | } 9 | -------------------------------------------------------------------------------- /parser/parse.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | // package parse: 根据 go struct 转化成pb文件并构建注册代码 4 | 5 | type Parser interface { 6 | PackageName() string 7 | Generate() string 8 | } 9 | -------------------------------------------------------------------------------- /cache/buffer/byte_pool_test.go: -------------------------------------------------------------------------------- 1 | package buffer 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func Test_newBytePool(t *testing.T) { 8 | p := localBytePool 9 | t.Log(p) 10 | } 11 | -------------------------------------------------------------------------------- /container/group/grouper.go: -------------------------------------------------------------------------------- 1 | package group 2 | 3 | // LazyLoadGroup 懒加载结构化 4 | type LazyLoadGroup interface { 5 | Get(key string) interface{} 6 | ReSet(nf func() interface{}) 7 | Clear() 8 | } 9 | -------------------------------------------------------------------------------- /generator/generator.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | // package generator: 发号器 4 | 5 | // Generator 发号器 6 | type Generator interface { 7 | // NextID 获取下一个ID 8 | NextID() (uint64, error) 9 | } 10 | -------------------------------------------------------------------------------- /tools/float/float.go: -------------------------------------------------------------------------------- 1 | package float 2 | 3 | import "math" 4 | 5 | // TruncFloat 截断float, prec 保留小数的位数 6 | func TruncFloat(f float64, prec int) float64 { 7 | bit := math.Pow10(prec) 8 | return math.Trunc(f*bit) / bit 9 | } 10 | -------------------------------------------------------------------------------- /distributed/task/args.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | // Arg task中的参数 4 | type Arg struct { 5 | Type string `json:"type" bson:"type"` 6 | Key string `json:"key" bson:"key"` 7 | Value interface{} `json:"value" bson:"value"` 8 | } 9 | -------------------------------------------------------------------------------- /gctuner/mem.go: -------------------------------------------------------------------------------- 1 | package gctuner 2 | 3 | import ( 4 | "runtime" 5 | ) 6 | 7 | var memStats runtime.MemStats 8 | 9 | func readMemoryInuse() uint64 { 10 | runtime.ReadMemStats(&memStats) 11 | return memStats.HeapInuse 12 | } 13 | -------------------------------------------------------------------------------- /sys/xxhash3/accum.go: -------------------------------------------------------------------------------- 1 | //go:build !amd64 2 | // +build !amd64 3 | 4 | package xxhash3 5 | 6 | import "unsafe" 7 | 8 | func accum(xacc *[8]uint64, xinput, xsecret unsafe.Pointer, l uintptr) { 9 | accumScalar(xacc, xinput, xsecret, l) 10 | } 11 | -------------------------------------------------------------------------------- /window/example_test.go: -------------------------------------------------------------------------------- 1 | package window 2 | 3 | func ExampleInitWindow() { 4 | // 初始化窗口 5 | w := NewWindow() 6 | 7 | // 增加指标 8 | // key:权重 9 | w.AddIndex("key", 1) 10 | 11 | // Show: 返回当前指标 12 | slice := w.Show() 13 | _ = slice 14 | } 15 | -------------------------------------------------------------------------------- /internal/runtimex/runtime.go: -------------------------------------------------------------------------------- 1 | // Package runtimex implements https://github.com/bytedance/gopkg 2 | package runtimex 3 | 4 | import ( 5 | _ "unsafe" // for linkname 6 | ) 7 | 8 | //go:linkname Fastrand runtime.fastrand 9 | func Fastrand() uint32 10 | -------------------------------------------------------------------------------- /tools/bind/url.go: -------------------------------------------------------------------------------- 1 | package bind 2 | 3 | type uriBinding struct{} 4 | 5 | func (uriBinding) Name() string { 6 | return "uri" 7 | } 8 | 9 | func (uriBinding) BindUri(m map[string][]string, obj interface{}) error { 10 | return mapUri(obj, m) 11 | } 12 | -------------------------------------------------------------------------------- /cache/mbuffer/README.md: -------------------------------------------------------------------------------- 1 | # mcache 2 | 3 | ## Introduction 4 | 5 | `mcache` is a memory pool which preserves memory in `sync.Pool` to improve malloc performance. 6 | 7 | The usage is quite simple: call `mcache.Malloc` directly, and don't forget to `Free` it! 8 | -------------------------------------------------------------------------------- /log/value_test.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import "testing" 4 | 5 | func TestValue(t *testing.T) { 6 | logger := DefaultLogger 7 | logger = With(logger, "ts", DefaultTimestamp, "caller", DefaultCaller) 8 | logger.Log(LevelInfo, "msg", "helloworld") 9 | } 10 | -------------------------------------------------------------------------------- /sys/stringx/README.md: -------------------------------------------------------------------------------- 1 | # stringx 2 | 3 | ## Introduction 4 | Extension/Helper of String Operation. 5 | 6 | ## Features 7 | - Transform(Reverse, Rotate, Shuffle ...) 8 | - Construction(Pad, Repeat...) 9 | - Matching(IsAlpha, IsAlphanumeric, IsNumeric ...) 10 | -------------------------------------------------------------------------------- /generator/example_test.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | func ExampleNewSnowflake() { 8 | // 生成对象 9 | generate := NewSnowflake(time.Now(), 1) 10 | nid, err := generate.NextID() 11 | if err != nil { 12 | // 处理错误 13 | } 14 | _ = nid 15 | } 16 | -------------------------------------------------------------------------------- /distributed/task/processor.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | // Processor 任务处理器 4 | type Processor interface { 5 | // Process 处理程序 6 | Process(t *Signature) error 7 | // ConsumeQueue 消费队列 8 | ConsumeQueue() string 9 | // PreConsumeHandler 是否进行预处理 10 | PreConsumeHandler() bool 11 | } 12 | -------------------------------------------------------------------------------- /concurrent/pipeline.go: -------------------------------------------------------------------------------- 1 | package concurrent 2 | 3 | // Pipeline 串联模式 4 | func Pipeline(in chan interface{}) <-chan interface{} { 5 | out := make(chan interface{}, 1) 6 | go func() { 7 | defer close(out) 8 | for v := range in { 9 | out <- v 10 | } 11 | }() 12 | return out 13 | } 14 | -------------------------------------------------------------------------------- /parser/parse_pb/model_test.go: -------------------------------------------------------------------------------- 1 | package parse_pb 2 | 3 | import "testing" 4 | 5 | func TestParsePb(t *testing.T) { 6 | r, err := ParsePb("/Users/songzhibin/go/src/github.com/songzhibin97/gkit/parse/demo/test.proto") 7 | if err != nil { 8 | panic(err) 9 | } 10 | t.Log(r.Generate()) 11 | } 12 | -------------------------------------------------------------------------------- /sys/safe/model.go: -------------------------------------------------------------------------------- 1 | package safe 2 | 3 | import "unsafe" 4 | 5 | // SliceModel slice底层模型 6 | type SliceModel struct { 7 | Data unsafe.Pointer 8 | Len int 9 | Cap int 10 | } 11 | 12 | // StringModel string底层模型 13 | type StringModel struct { 14 | Data unsafe.Pointer 15 | Len int 16 | } 17 | -------------------------------------------------------------------------------- /internal/sys/safe/model.go: -------------------------------------------------------------------------------- 1 | package safe 2 | 3 | import "unsafe" 4 | 5 | // SliceModel slice底层模型 6 | type SliceModel struct { 7 | Data unsafe.Pointer 8 | Len int 9 | Cap int 10 | } 11 | 12 | // StringModel string底层模型 13 | type StringModel struct { 14 | Data unsafe.Pointer 15 | Len int 16 | } 17 | -------------------------------------------------------------------------------- /window/slidingwindow.go: -------------------------------------------------------------------------------- 1 | package window 2 | 3 | // SlidingWindow 滑动窗口接口化 4 | type SlidingWindow interface { 5 | // sentinel 哨兵 6 | sentinel() 7 | 8 | // Shutdown 优雅关闭 9 | Shutdown() 10 | 11 | // AddIndex 添加指标信息 12 | AddIndex(k string, v uint) 13 | 14 | // Show 展示信息 15 | Show() []interface{} 16 | } 17 | -------------------------------------------------------------------------------- /tools/bind/query.go: -------------------------------------------------------------------------------- 1 | package bind 2 | 3 | import "net/http" 4 | 5 | type queryBinding struct{} 6 | 7 | func (queryBinding) Name() string { 8 | return "query" 9 | } 10 | 11 | func (queryBinding) Bind(req *http.Request, obj interface{}) error { 12 | values := req.URL.Query() 13 | return mapForm(obj, values) 14 | } 15 | -------------------------------------------------------------------------------- /tools/error.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import "errors" 4 | 5 | var ( 6 | ErrorMustPtr = errors.New("dst or src must ptr") 7 | ErrorNoEquals = errors.New("dst and src must equal type") 8 | ErrorMustStructPtr = errors.New("dst or src must struct ptr") 9 | ErrorInvalidValue = errors.New("invalid value") 10 | ) 11 | -------------------------------------------------------------------------------- /internal/runtimex/asm.s: -------------------------------------------------------------------------------- 1 | 2 | 3 | // The runtime package uses //go:linkname to push a few functions into this 4 | // package but we still need a .s file so the Go tool does not pass -complete 5 | // to the go tool compile so the latter does not complain about Go functions 6 | // with no bodies. 7 | // See https://github.com/golang/go/issues/23311 -------------------------------------------------------------------------------- /tools/reflect2value/err.go: -------------------------------------------------------------------------------- 1 | package reflect2value 2 | 3 | type errNonsupportType struct { 4 | valueType string 5 | } 6 | 7 | func NewErrNonsupportType(valueType string) error { 8 | return &errNonsupportType{valueType: valueType} 9 | } 10 | 11 | func (e *errNonsupportType) Error() string { 12 | return e.valueType + ":不是支持类型" 13 | } 14 | -------------------------------------------------------------------------------- /errors/errors.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package gkit.errors; 4 | 5 | option go_package = "./;errors"; 6 | 7 | message Error { 8 | // code: status code 9 | int32 code = 1; 10 | 11 | // reason: 12 | string reason = 2; 13 | 14 | // message: 15 | string message = 3; 16 | 17 | // metadata: 18 | map metadata = 4; 19 | } -------------------------------------------------------------------------------- /sys/xxhash3/internal/avo/gen.go: -------------------------------------------------------------------------------- 1 | //go:build ignore 2 | // +build ignore 3 | 4 | package main 5 | 6 | import "flag" 7 | 8 | var ( 9 | avx = flag.Bool("avx2", false, "avx2") 10 | sse = flag.Bool("sse2", false, "sse2") 11 | ) 12 | 13 | func main() { 14 | flag.Parse() 15 | 16 | if *avx { 17 | AVX2() 18 | } else if *sse { 19 | SSE2() 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /distributed/locker/locker.go: -------------------------------------------------------------------------------- 1 | package locker 2 | 3 | // 分布式锁 4 | 5 | type Locker interface { 6 | // Lock 获取锁 7 | // key 锁名称 8 | // mark 锁的凭证,用于释放锁的唯一标志 9 | // expire 锁过期失效,以Millisecond为单位 1000 = 1s 10 | Lock(key string, expire int, mark string) error 11 | 12 | // UnLock 解锁 13 | // key 锁名称 14 | // mark 锁的凭证,用于释放锁的唯一标志 15 | UnLock(key string, mark string) error 16 | } 17 | -------------------------------------------------------------------------------- /net/port/port.go: -------------------------------------------------------------------------------- 1 | package port 2 | 3 | import "net" 4 | 5 | func GetLocalFreePort() (int, error) { 6 | addr, err := net.ResolveTCPAddr("tcp", "localhost:0") 7 | if err != nil { 8 | return 0, err 9 | } 10 | 11 | l, err := net.ListenTCP("tcp", addr) 12 | if err != nil { 13 | return 0, err 14 | } 15 | defer l.Close() 16 | return l.Addr().(*net.TCPAddr).Port, nil 17 | } 18 | -------------------------------------------------------------------------------- /sys/nanotime/nacotime.go: -------------------------------------------------------------------------------- 1 | package nanotime 2 | 3 | import ( 4 | "time" 5 | _ "unsafe" 6 | ) 7 | 8 | //go:linkname RuntimeNanotime runtime.nanotime 9 | func RuntimeNanotime() int64 10 | 11 | func SinceSeconds(t int64) float64 { 12 | return Since(t, time.Second) 13 | } 14 | 15 | func Since(t int64, unit time.Duration) float64 { 16 | return float64(RuntimeNanotime()-t) / float64(unit) 17 | } 18 | -------------------------------------------------------------------------------- /log/std_test.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import "testing" 4 | 5 | func TestStdLogger(t *testing.T) { 6 | logger := DefaultLogger 7 | logger = With(logger, "caller", DefaultCaller, "ts", DefaultTimestamp) 8 | 9 | logger.Log(LevelInfo, "msg", "test debug") 10 | logger.Log(LevelInfo, "msg", "test info") 11 | logger.Log(LevelInfo, "msg", "test warn") 12 | logger.Log(LevelInfo, "msg", "test error") 13 | } 14 | -------------------------------------------------------------------------------- /sys/syncx/linkname.go: -------------------------------------------------------------------------------- 1 | //go:build !race 2 | // +build !race 3 | 4 | package syncx 5 | 6 | import ( 7 | _ "sync" 8 | _ "unsafe" 9 | ) 10 | 11 | //go:noescape 12 | //go:linkname runtime_registerPoolCleanup sync.runtime_registerPoolCleanup 13 | func runtime_registerPoolCleanup(cleanup func()) 14 | 15 | //go:noescape 16 | //go:linkname runtime_poolCleanup sync.poolCleanup 17 | func runtime_poolCleanup() 18 | -------------------------------------------------------------------------------- /goroutine/ggroup.go: -------------------------------------------------------------------------------- 1 | package goroutine 2 | 3 | import "context" 4 | 5 | type GGroup interface { 6 | // ChangeMax 更改buffer大小 7 | ChangeMax(m int64) 8 | 9 | // AddTask 添加需要 `go function` 10 | AddTask(f func()) bool 11 | 12 | // AddTaskN 异步添加任务,有超时机制 13 | AddTaskN(ctx context.Context, f func()) bool 14 | 15 | // Shutdown 回收资源 16 | Shutdown() error 17 | 18 | // Trick debug 19 | Trick() string 20 | } 21 | -------------------------------------------------------------------------------- /distributed/broker/option.go: -------------------------------------------------------------------------------- 1 | package broker 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/songzhibin97/gkit/options" 7 | ) 8 | 9 | func SetRetry(retry bool) options.Option { 10 | return func(c interface{}) { 11 | c.(*Broker).retry = retry 12 | } 13 | } 14 | 15 | func SetRetryFn(fn func(ctx context.Context)) options.Option { 16 | return func(c interface{}) { 17 | c.(*Broker).retryFn = fn 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /internal/runtimex/ppin_test.go: -------------------------------------------------------------------------------- 1 | package runtimex 2 | 3 | import ( 4 | "runtime" 5 | "sync" 6 | "testing" 7 | ) 8 | 9 | func TestPin(t *testing.T) { 10 | n := Pin() 11 | Unpin() 12 | t.Log(n) 13 | var wg sync.WaitGroup 14 | for i := 0; i < runtime.GOMAXPROCS(0); i++ { 15 | wg.Add(1) 16 | go func() { 17 | n := Pin() 18 | Unpin() 19 | t.Log(n) 20 | wg.Done() 21 | }() 22 | } 23 | wg.Wait() 24 | } 25 | -------------------------------------------------------------------------------- /tools/bind/option.go: -------------------------------------------------------------------------------- 1 | package bind 2 | 3 | import "github.com/songzhibin97/gkit/options" 4 | 5 | var defaultParse = []Binding{Query, FormPost, Header} 6 | 7 | func DefaultParse(contentType string) []Binding { 8 | return append(defaultParse, contentTypeSelect(contentType)) 9 | } 10 | 11 | func SetSelectorParse(bind []Binding) options.Option { 12 | return func(o interface{}) { 13 | o.(*all).selectorParse = bind 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /distributed/retry/retry.go: -------------------------------------------------------------------------------- 1 | package retry 2 | 3 | import ( 4 | "context" 5 | "time" 6 | ) 7 | 8 | func Retry() func(ctx context.Context) { 9 | retryIn := 0 10 | fibonacci := Fibonacci() 11 | return func(ctx context.Context) { 12 | if retryIn > 0 { 13 | select { 14 | case <-ctx.Done(): 15 | break 16 | case <-time.After(time.Second * time.Duration(retryIn)): 17 | break 18 | } 19 | } 20 | retryIn = fibonacci() 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /gctuner/mem_test.go: -------------------------------------------------------------------------------- 1 | package gctuner 2 | 3 | import ( 4 | "runtime" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestMem(t *testing.T) { 11 | defer runtime.GC() // make it will not affect other tests 12 | 13 | is := assert.New(t) 14 | const mb = 1024 * 1024 15 | 16 | heap := make([]byte, 100*mb+1) 17 | inuse := readMemoryInuse() 18 | t.Logf("mem inuse: %d MB", inuse/mb) 19 | is.GreaterOrEqual(inuse, uint64(100*mb)) 20 | heap[0] = 0 21 | } 22 | -------------------------------------------------------------------------------- /internal/stat/metric.go: -------------------------------------------------------------------------------- 1 | package stat 2 | 3 | // Metric 简单实现 4 | // 度量标准软件包中度量标准的实现是:Counter, Gauge,PointGauge, RollingCounter and RollingGauge. 5 | type Metric interface { 6 | // Add 将给定值添加到当前窗口 7 | Add(int64) 8 | 9 | // Value 获取当前值 10 | // 如果是 类型是 PointGauge, RollingCounter, RollingGauge 11 | // 返回窗口总和 12 | Value() int64 13 | } 14 | 15 | // Aggregation 聚合接口 16 | type Aggregation interface { 17 | Min() float64 18 | Max() float64 19 | Avg() float64 20 | Sum() float64 21 | } 22 | -------------------------------------------------------------------------------- /internal/sys/hash/hash.go: -------------------------------------------------------------------------------- 1 | package hash 2 | 3 | import "hash/crc32" 4 | 5 | func Uint16(s string, table *crc32.Table) uint16 { 6 | return uint16(crc32.Checksum([]byte(s), table)) 7 | } 8 | 9 | func Uint32(s string, table *crc32.Table) uint32 { 10 | return crc32.Checksum([]byte(s), table) 11 | } 12 | 13 | func Uint16IEEE(s string) uint16 { 14 | return uint16(crc32.ChecksumIEEE([]byte(s))) 15 | } 16 | 17 | func Uint32IEEE(s string) uint32 { 18 | return crc32.ChecksumIEEE([]byte(s)) 19 | } 20 | -------------------------------------------------------------------------------- /sys/goid/gid.go: -------------------------------------------------------------------------------- 1 | package goid 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | // GetGID 获取goroutine唯一ID 11 | func GetGID() int64 { 12 | var buf [64]byte 13 | n := runtime.Stack(buf[:], false) 14 | // 得到id字符串 15 | idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0] 16 | id, err := strconv.Atoi(idField) 17 | if err != nil { 18 | panic(fmt.Sprintf("cannot get goroutine id: %v", err)) 19 | } 20 | return int64(id) 21 | } 22 | -------------------------------------------------------------------------------- /internal/sys/goid/gid.go: -------------------------------------------------------------------------------- 1 | package goid 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "strconv" 7 | "strings" 8 | ) 9 | 10 | // GetGID 获取goroutine唯一ID 11 | func GetGID() int64 { 12 | var buf [64]byte 13 | n := runtime.Stack(buf[:], false) 14 | // 得到id字符串 15 | idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0] 16 | id, err := strconv.Atoi(idField) 17 | if err != nil { 18 | panic(fmt.Sprintf("cannot get goroutine id: %v", err)) 19 | } 20 | return int64(id) 21 | } 22 | -------------------------------------------------------------------------------- /sys/xxhash3/accum_amd64.go: -------------------------------------------------------------------------------- 1 | package xxhash3 2 | 3 | import "unsafe" 4 | 5 | func accumAVX2(acc *[8]uint64, xinput, xsecret unsafe.Pointer, len uintptr) 6 | func accumSSE2(acc *[8]uint64, xinput, xsecret unsafe.Pointer, len uintptr) 7 | 8 | func accum(xacc *[8]uint64, xinput, xsecret unsafe.Pointer, l uintptr) { 9 | if avx2 { 10 | accumAVX2(xacc, xinput, xsecret, l) 11 | } else if sse2 { 12 | accumSSE2(xacc, xinput, xsecret, l) 13 | } else { 14 | accumScalar(xacc, xinput, xsecret, l) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /metrics/metrics.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | // package metrics: 指标信息 4 | 5 | // Counter is metrics counter. 6 | type Counter interface { 7 | With(lvs ...string) Counter 8 | Inc() 9 | Add(delta float64) 10 | } 11 | 12 | // Gauge is metrics gauge. 13 | type Gauge interface { 14 | With(lvs ...string) Gauge 15 | Set(value float64) 16 | Add(delta float64) 17 | Sub(delta float64) 18 | } 19 | 20 | // Observer is metrics observer. 21 | type Observer interface { 22 | With(lvs ...string) Observer 23 | Observe(float64) 24 | } 25 | -------------------------------------------------------------------------------- /log/logger_test.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "os" 5 | "testing" 6 | ) 7 | 8 | func TestInfo(t *testing.T) { 9 | logger := DefaultLogger 10 | logger = With(logger, "ts", DefaultTimestamp, "caller", DefaultCaller) 11 | _ = logger.Log(LevelInfo, "key1", "value1") 12 | } 13 | 14 | func TestWrapper(t *testing.T) { 15 | out := NewStdLogger(os.Stdout) 16 | err := NewStdLogger(os.Stderr) 17 | 18 | l := With(WithLogs(out, err), "ts", DefaultTimestamp, "caller", DefaultCaller) 19 | _ = l.Log(LevelInfo, "msg", "test") 20 | } 21 | -------------------------------------------------------------------------------- /registry/instance.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | // ServiceInstance 服务发现的实例 4 | type ServiceInstance struct { 5 | // ID: 全局唯一ID 6 | ID string `json:"id"` 7 | // Name: 注册服务的名称 8 | Name string `json:"name"` 9 | // Version: 版本信息 10 | Version string `json:"version"` 11 | // Metadata: 可携带的元数据 12 | Metadata map[string]string `json:"metadata"` 13 | // Endpoints: 服务实例的端点地址 14 | // schema: 15 | // http://127.0.0.1:8000?isSecure=false 16 | // grpc://127.0.0.1:9000?isSecure=false 17 | Endpoints []string `json:"endpoints"` 18 | } 19 | -------------------------------------------------------------------------------- /log/level.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | type Lever int8 4 | 5 | // 预定义Level等级 6 | const ( 7 | LevelDebug Lever = iota 8 | LevelInfo 9 | LevelWarn 10 | LevelError 11 | ) 12 | 13 | var m = map[Lever]string{ 14 | LevelDebug: "[Debug]", 15 | LevelInfo: "[Info]", 16 | LevelWarn: "[Warn]", 17 | LevelError: "[Error]", 18 | } 19 | 20 | // Allow 允许是否可以打印 21 | func (l Lever) Allow(lv Lever) bool { 22 | return lv >= l 23 | } 24 | 25 | // String 语义转义 26 | func (l Lever) String() string { 27 | if v, ok := m[l]; ok { 28 | return v 29 | } 30 | return "UNKNOWN" 31 | } 32 | -------------------------------------------------------------------------------- /net/tcp/conf.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import "time" 4 | 5 | var ( 6 | // DefaultWaitTimeout 默认等待超时时间 7 | DefaultWaitTimeout = time.Millisecond 8 | 9 | // DefaultConnTimeout 默认连接超时时间 10 | DefaultConnTimeout = 30 * time.Second 11 | 12 | // DefaultRetryInterval 默认重试间隔 13 | DefaultRetryInterval = 100 * time.Millisecond 14 | 15 | // DefaultReadBuffer 默认读取buffer大小 16 | DefaultReadBuffer = 1 << 12 17 | 18 | // DefaultServer 默认的服务名称 19 | DefaultServer = "Default" 20 | ) 21 | 22 | const ( 23 | // Status 24 | 25 | UNKNOWN = iota 26 | ACTIVE 27 | ERROR 28 | ) 29 | -------------------------------------------------------------------------------- /cache/local_cache/Iterator.go: -------------------------------------------------------------------------------- 1 | package local_cache 2 | 3 | import "time" 4 | 5 | // Iterator cache存储的实际成员 6 | type Iterator struct { 7 | // Val 实际存储的对象 8 | Val interface{} 9 | 10 | // Expire 过期时间 11 | // 0 不设置过期时间 12 | Expire int64 13 | } 14 | 15 | // Expired 判断是否过期,过期返回 true 16 | func (i Iterator) Expired(v ...int64) bool { 17 | if i.Expire == 0 { 18 | return false 19 | } 20 | if len(v) != 0 { 21 | return v[0] > i.Expire 22 | } 23 | return time.Now().UnixNano() > i.Expire 24 | } 25 | 26 | type kv struct { 27 | key string 28 | value interface{} 29 | } 30 | -------------------------------------------------------------------------------- /coding/proto/proto.go: -------------------------------------------------------------------------------- 1 | package proto 2 | 3 | import ( 4 | "github.com/songzhibin97/gkit/coding" 5 | "google.golang.org/protobuf/proto" 6 | ) 7 | 8 | const Name = "proto" 9 | 10 | func init() { 11 | _ = coding.RegisterCode(code{}) 12 | } 13 | 14 | type code struct{} 15 | 16 | func (c code) Marshal(v interface{}) ([]byte, error) { 17 | return proto.Marshal(v.(proto.Message)) 18 | } 19 | 20 | func (c code) Unmarshal(data []byte, v interface{}) error { 21 | return proto.Unmarshal(data, v.(proto.Message)) 22 | } 23 | 24 | func (c code) Name() string { 25 | return Name 26 | } 27 | -------------------------------------------------------------------------------- /distributed/retry/fibonacci.go: -------------------------------------------------------------------------------- 1 | package retry 2 | 3 | // Fibonacci returns successive Fibonacci numbers starting from 1 4 | func Fibonacci(max ...int) func() int { 5 | max = append(max, 20) 6 | a, b := 0, 1 7 | return func() int { 8 | if max[0] == 0 { 9 | return a 10 | } 11 | max[0]-- 12 | a, b = b, a+b 13 | return a 14 | } 15 | } 16 | 17 | // FibonacciNext returns next number in Fibonacci sequence greater than start 18 | func FibonacciNext(start int) int { 19 | fib := Fibonacci() 20 | num := fib() 21 | for num <= start { 22 | num = fib() 23 | } 24 | return num 25 | } 26 | -------------------------------------------------------------------------------- /concurrent/or_done_test.go: -------------------------------------------------------------------------------- 1 | package concurrent 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func signal(d time.Duration) <-chan interface{} { 9 | c := make(chan interface{}, 1) 10 | go func() { 11 | defer close(c) 12 | time.Sleep(d) 13 | }() 14 | return c 15 | } 16 | 17 | func TestOrDone(t *testing.T) { 18 | start := time.Now() 19 | <-OrDone( 20 | signal(10*time.Second), 21 | signal(20*time.Second), 22 | signal(30*time.Second), 23 | signal(40*time.Second), 24 | signal(50*time.Second), 25 | signal(3*time.Second), 26 | ) 27 | t.Log("done", time.Since(start)) 28 | } 29 | -------------------------------------------------------------------------------- /page_token/page_token.go: -------------------------------------------------------------------------------- 1 | package page_token 2 | 3 | // https://google.aip.dev/158 4 | 5 | type PageToken interface { 6 | TokenGenerator 7 | ProcessPageTokens 8 | } 9 | 10 | type TokenGenerator interface { 11 | ForIndex(int) string 12 | GetIndex(string) (int, error) 13 | } 14 | 15 | type ProcessPageTokens interface { 16 | // ProcessPageTokens 17 | // numElements: total number of elements 18 | // pageSize: number of elements per page 19 | // pageToken: page token 20 | 21 | ProcessPageTokens(numElements int, pageSize int, pageToken string) (start, end int, nextToken string, err error) 22 | } 23 | -------------------------------------------------------------------------------- /restrictor/rate/rate.go: -------------------------------------------------------------------------------- 1 | package rate 2 | 3 | // package rate: https://pkg.go.dev/golang.org/x/time/rate 实现 limiter 接口 4 | 5 | import ( 6 | "context" 7 | "time" 8 | 9 | "github.com/songzhibin97/gkit/restrictor" 10 | "golang.org/x/time/rate" 11 | ) 12 | 13 | // NewRate 返回limiter对应的 restrictor.AllowFunc, restrictor.WaitFunc 14 | func NewRate(limiter *rate.Limiter) (restrictor.AllowFunc, restrictor.WaitFunc) { 15 | return func(now time.Time, n int) bool { 16 | return limiter.AllowN(now, n) 17 | }, func(ctx context.Context, n int) error { 18 | return limiter.WaitN(ctx, n) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /tools/bind/xml.go: -------------------------------------------------------------------------------- 1 | package bind 2 | 3 | import ( 4 | "bytes" 5 | "encoding/xml" 6 | "io" 7 | "net/http" 8 | ) 9 | 10 | type xmlBinding struct{} 11 | 12 | func (xmlBinding) Name() string { 13 | return "xml" 14 | } 15 | 16 | func (xmlBinding) Bind(req *http.Request, obj interface{}) error { 17 | return decodeXML(req.Body, obj) 18 | } 19 | 20 | func (xmlBinding) BindBody(body []byte, obj interface{}) error { 21 | return decodeXML(bytes.NewReader(body), obj) 22 | } 23 | 24 | func decodeXML(r io.Reader, obj interface{}) error { 25 | decoder := xml.NewDecoder(r) 26 | return decoder.Decode(obj) 27 | } 28 | -------------------------------------------------------------------------------- /concurrent/map_reduce.go: -------------------------------------------------------------------------------- 1 | package concurrent 2 | 3 | func MapChan(in <-chan interface{}, fn func(interface{}) interface{}) <-chan interface{} { 4 | out := make(chan interface{}, 1) 5 | if in == nil { 6 | close(out) 7 | return out 8 | } 9 | go func() { 10 | defer close(out) 11 | for v := range in { 12 | out <- fn(v) 13 | } 14 | }() 15 | return out 16 | } 17 | 18 | func ReduceChan(in <-chan interface{}, fn func(r, v interface{}) interface{}) interface{} { 19 | if in == nil { 20 | return nil 21 | } 22 | out := <-in 23 | for v := range in { 24 | out = fn(out, v) 25 | } 26 | return out 27 | } 28 | -------------------------------------------------------------------------------- /sys/nanotime/nacotime_test.go: -------------------------------------------------------------------------------- 1 | package nanotime 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | ) 7 | 8 | func BenchmarkTimeNow(b *testing.B) { 9 | for i := 0; i < b.N; i++ { 10 | cost := time.Now() 11 | time.Since(cost).Seconds() 12 | } 13 | } 14 | 15 | func BenchmarkRuntimeNanotime(b *testing.B) { 16 | for i := 0; i < b.N; i++ { 17 | cost := RuntimeNanotime() 18 | _ = SinceSeconds(cost) 19 | } 20 | } 21 | 22 | func TestProxy_Time(t *testing.T) { 23 | n1 := time.Now() 24 | n2 := RuntimeNanotime() 25 | time.Sleep(time.Second) 26 | t.Log(time.Since(n1).Seconds()) 27 | t.Log(SinceSeconds(n2)) 28 | } 29 | -------------------------------------------------------------------------------- /tools/bind/yaml.go: -------------------------------------------------------------------------------- 1 | package bind 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "net/http" 7 | 8 | "gopkg.in/yaml.v2" 9 | ) 10 | 11 | type yamlBinding struct{} 12 | 13 | func (yamlBinding) Name() string { 14 | return "yaml" 15 | } 16 | 17 | func (yamlBinding) Bind(req *http.Request, obj interface{}) error { 18 | return decodeYAML(req.Body, obj) 19 | } 20 | 21 | func (yamlBinding) BindBody(body []byte, obj interface{}) error { 22 | return decodeYAML(bytes.NewReader(body), obj) 23 | } 24 | 25 | func decodeYAML(r io.Reader, obj interface{}) error { 26 | decoder := yaml.NewDecoder(r) 27 | return decoder.Decode(obj) 28 | } 29 | -------------------------------------------------------------------------------- /internal/stat/iterator.go: -------------------------------------------------------------------------------- 1 | package stat 2 | 3 | import "fmt" 4 | 5 | // Iterator 迭代窗口中所有桶 6 | type Iterator struct { 7 | count int 8 | iteratedCount int 9 | cur *Bucket 10 | } 11 | 12 | // Next 返回 true 表示已经全部迭代完毕 13 | func (i *Iterator) Next() bool { 14 | return i.count != i.iteratedCount 15 | } 16 | 17 | // Bucket 获取当前存储通 18 | func (i *Iterator) Bucket() Bucket { 19 | if !(i.Next()) { 20 | panic(fmt.Errorf("stat/iterator: iteration out of range iteratedCount: %d count: %d", i.iteratedCount, i.count)) 21 | } 22 | bucket := *i.cur 23 | i.iteratedCount++ 24 | i.cur = i.cur.Next() 25 | return bucket 26 | } 27 | -------------------------------------------------------------------------------- /timeout/example_test.go: -------------------------------------------------------------------------------- 1 | package timeout 2 | 3 | import ( 4 | "context" 5 | "time" 6 | ) 7 | 8 | func ExampleShrink() { 9 | // timeout.Shrink 方法提供全链路的超时控制 10 | // 只需要传入一个父节点的ctx 和需要设置的超时时间,他会帮你确认这个ctx是否之前设置过超时时间, 11 | // 如果设置过超时时间的话会和你当前设置的超时时间进行比较,选择一个最小的进行设置,保证链路超时时间不会被下游影响 12 | // d: 代表剩余的超时时间 13 | // nCtx: 新的context对象 14 | // cancel: 如果是成功真正设置了超时时间会返回一个cancel()方法,未设置成功会返回一个无效的cancel,不过别担心,还是可以正常调用的 15 | d, nCtx, cancel := Shrink(context.Background(), 5*time.Second) 16 | // d 根据需要判断 17 | // 一般判断该服务的下游超时时间,如果d过于小,可以直接放弃 18 | select { 19 | case <-nCtx.Done(): 20 | cancel() 21 | default: 22 | // ... 23 | } 24 | _ = d 25 | } 26 | -------------------------------------------------------------------------------- /concurrent/orderly.go: -------------------------------------------------------------------------------- 1 | package concurrent 2 | 3 | import "sync" 4 | 5 | type WaitGroup interface { 6 | Add(int) 7 | Wait() 8 | Done() 9 | Do() 10 | } 11 | 12 | type OrderlyTask struct { 13 | sync.WaitGroup 14 | fn func() 15 | } 16 | 17 | // Do 执行任务 18 | func (o *OrderlyTask) Do() { 19 | o.Add(1) 20 | go func() { 21 | defer o.Done() 22 | o.fn() 23 | }() 24 | } 25 | 26 | // NewOrderTask 初始化任务 27 | func NewOrderTask(fn func()) *OrderlyTask { 28 | return &OrderlyTask{ 29 | fn: fn, 30 | } 31 | } 32 | 33 | // Orderly 顺序执行 34 | func Orderly(tasks []*OrderlyTask) { 35 | for _, task := range tasks { 36 | task.Do() 37 | task.Wait() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /cache/buffer/example_test.go: -------------------------------------------------------------------------------- 1 | package buffer 2 | 3 | // Byte 复用 4 | func ExampleGetBytes() { 5 | // size 2^6 - 2^18 6 | // 返回向上取整的 2的整数倍 cap, len == size 7 | // 其他特殊的或者在运行期间扩容的 将会被清空 8 | slice := GetBytes(1024) 9 | _ = slice 10 | } 11 | 12 | func ExamplePutBytes() { 13 | slice := make([]byte, 1024) 14 | // 将slice回收 15 | PutBytes(&slice) 16 | } 17 | 18 | // IOByte 复用 19 | 20 | func ExampleGetIoPool() { 21 | // 创建一个缓冲区为 cap大小的 io对象 22 | io := GetIoPool(1024) 23 | _ = io 24 | } 25 | 26 | func ExamplePutIoPool() { 27 | mockIoPool := newIoBuffer(1024) 28 | err := PutIoPool(mockIoPool) 29 | if err != nil { 30 | // 如果一个对象已经被回收了,再次引用被回收的对象会触发错误 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /concurrent/fan_out.go: -------------------------------------------------------------------------------- 1 | package concurrent 2 | 3 | import "sync" 4 | 5 | // FanOut 扇出模式 6 | func FanOut(in <-chan interface{}, out []chan interface{}, async bool) { 7 | wg := sync.WaitGroup{} 8 | go func() { 9 | defer func() { 10 | wg.Wait() 11 | for i := 0; i < len(out); i++ { 12 | close(out[i]) 13 | } 14 | }() 15 | for v := range in { 16 | for i := 0; i < len(out); i++ { 17 | if async { 18 | // 异步 19 | wg.Add(1) 20 | go func(ch chan interface{}, v interface{}) { 21 | defer wg.Done() 22 | ch <- v 23 | }(out[i], v) 24 | } else { 25 | // 同步 26 | out[i] <- v 27 | } 28 | } 29 | } 30 | }() 31 | } 32 | -------------------------------------------------------------------------------- /cache/singleflight/singleflight.go: -------------------------------------------------------------------------------- 1 | package singleflight 2 | 3 | import "golang.org/x/sync/singleflight" 4 | 5 | // SingleFlight Merge back to source 6 | type SingleFlight interface { 7 | // Do 同步调用单飞 8 | Do(key string, fn func() (interface{}, error)) (v interface{}, err error, shared bool) 9 | 10 | // DoChan 异步调用单飞 11 | DoChan(key string, fn func() (interface{}, error)) <-chan singleflight.Result 12 | 13 | // Forget 可以取消已经下发未执行的任务 14 | Forget(key string) 15 | } 16 | 17 | type Group struct { 18 | // import "golang.org/x/sync/singleflight" 19 | singleflight.Group 20 | } 21 | 22 | // NewSingleFlight 实例化 23 | func NewSingleFlight() SingleFlight { 24 | return &Group{} 25 | } 26 | -------------------------------------------------------------------------------- /internal/runtimex/ppin.go: -------------------------------------------------------------------------------- 1 | package runtimex 2 | 3 | import ( 4 | _ "unsafe" 5 | ) 6 | 7 | //go:noescape 8 | //go:linkname runtime_procPin runtime.procPin 9 | func runtime_procPin() int 10 | 11 | //go:noescape 12 | //go:linkname runtime_procUnpin runtime.procUnpin 13 | func runtime_procUnpin() 14 | 15 | // Pin pins current p, return pid. 16 | // DO NOT USE if you don't know what this is. 17 | func Pin() int { 18 | return runtime_procPin() 19 | } 20 | 21 | // Unpin unpins current p. 22 | func Unpin() { 23 | runtime_procUnpin() 24 | } 25 | 26 | // Pid returns the id of current p. 27 | func Pid() (id int) { 28 | id = runtime_procPin() 29 | runtime_procUnpin() 30 | return 31 | } 32 | -------------------------------------------------------------------------------- /overload/limiter.go: -------------------------------------------------------------------------------- 1 | package overload 2 | 3 | import ( 4 | "context" 5 | ) 6 | 7 | // Op 操作类型 8 | type Op int 9 | 10 | const ( 11 | Success Op = iota 12 | Ignore 13 | Drop 14 | ) 15 | 16 | type allowOptions struct{} 17 | 18 | // AllowOption AllowOptions allow options. 19 | type AllowOption interface { 20 | Apply(*allowOptions) 21 | } 22 | 23 | // DoneInfo 完成信息 24 | type DoneInfo struct { 25 | Err error 26 | Op Op 27 | } 28 | 29 | // DefaultAllowOpts 返回默认选项 30 | func DefaultAllowOpts() allowOptions { 31 | return allowOptions{} 32 | } 33 | 34 | // Limiter 限制接口 35 | type Limiter interface { 36 | Allow(ctx context.Context, opts ...AllowOption) (func(info DoneInfo), error) 37 | } 38 | -------------------------------------------------------------------------------- /tools/bind/internal/bytesconv/bytesconv.go: -------------------------------------------------------------------------------- 1 | // Copyright 2020 Gin Core Team. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | package bytesconv 6 | 7 | import ( 8 | "unsafe" 9 | ) 10 | 11 | // StringToBytes converts string to byte slice without a memory allocation. 12 | func StringToBytes(s string) []byte { 13 | return *(*[]byte)(unsafe.Pointer( 14 | &struct { 15 | string 16 | Cap int 17 | }{s, len(s)}, 18 | )) 19 | } 20 | 21 | // BytesToString converts byte slice to string without a memory allocation. 22 | func BytesToString(b []byte) string { 23 | return *(*string)(unsafe.Pointer(&b)) 24 | } 25 | -------------------------------------------------------------------------------- /parser/parse_pb/transform.go: -------------------------------------------------------------------------------- 1 | package parse_pb 2 | 3 | var PbToGoMapping = map[string]string{ 4 | "double": "float64", 5 | "float": "float32", 6 | "int32": "int32", 7 | "uint32": "uint32", 8 | "uint64": "uint64", 9 | "sint32": "int32", 10 | "sint64": "int64", 11 | "fixed32": "uint32", 12 | "fixed64": "uint64", 13 | "sfixed32": "int32", 14 | "sfixed64": "int64", 15 | "bool": "bool", 16 | "string": "string", 17 | "bytes": "[]byte", 18 | } 19 | 20 | // PbTypeToGo go type 转化成 pb type 21 | func PbTypeToGo(s string) string { 22 | if v, ok := PbToGoMapping[s]; ok { 23 | return v 24 | } 25 | return s 26 | } 27 | 28 | func addOne(a int) int { 29 | return a + 1 30 | } 31 | -------------------------------------------------------------------------------- /coding/xml/xml.go: -------------------------------------------------------------------------------- 1 | package xml 2 | 3 | import ( 4 | "encoding/xml" 5 | "reflect" 6 | 7 | "github.com/songzhibin97/gkit/coding" 8 | ) 9 | 10 | const Name = "xml" 11 | 12 | func init() { 13 | _ = coding.RegisterCode(code{}) 14 | } 15 | 16 | type code struct{} 17 | 18 | func (c code) Marshal(v interface{}) ([]byte, error) { 19 | return xml.Marshal(v) 20 | } 21 | 22 | func (c code) Unmarshal(data []byte, v interface{}) error { 23 | rv := reflect.ValueOf(v) 24 | for rv.Kind() == reflect.Ptr { 25 | if rv.IsNil() { 26 | rv.Set(reflect.New(rv.Type().Elem())) 27 | } 28 | rv = rv.Elem() 29 | } 30 | return xml.Unmarshal(data, v) 31 | } 32 | 33 | func (c code) Name() string { 34 | return Name 35 | } 36 | -------------------------------------------------------------------------------- /watching/example/cpu_explode/cpu_explode.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | 7 | "github.com/songzhibin97/gkit/watching" 8 | ) 9 | 10 | func init() { 11 | http.HandleFunc("/cpuex", cpuex) 12 | go http.ListenAndServe(":10003", nil) 13 | } 14 | 15 | func main() { 16 | w := watching.NewWatching( 17 | watching.WithCollectInterval("2s"), 18 | watching.WithCoolDown("1m"), 19 | watching.WithDumpPath("/tmp"), 20 | watching.WithCPUDump(20, 25, 80), 21 | ) 22 | w.EnableCPUDump().Start() 23 | time.Sleep(time.Hour) 24 | } 25 | 26 | func cpuex(wr http.ResponseWriter, req *http.Request) { 27 | go func() { 28 | for { 29 | time.Sleep(time.Millisecond) 30 | } 31 | }() 32 | } 33 | -------------------------------------------------------------------------------- /container/group/example_test.go: -------------------------------------------------------------------------------- 1 | package group 2 | 3 | func createResources() interface{} { 4 | return map[int]int{} 5 | } 6 | 7 | func createResources2() interface{} { 8 | return []int{} 9 | } 10 | 11 | var group LazyLoadGroup 12 | 13 | func ExampleNewGroup() { 14 | // 类似 sync.Pool 一样 15 | // 初始化一个group 16 | group = NewGroup(createResources) 17 | } 18 | 19 | func ExampleGroup_Get() { 20 | // 如果key 不存在 调用 NewGroup 传入的 function 创建资源 21 | // 如果存在则返回创建的资源信息 22 | v := group.Get("test") 23 | _ = v 24 | } 25 | 26 | func ExampleGroup_ReSet() { 27 | // ReSet 重置初始化函数,同时会对缓存的 key进行清空 28 | group.ReSet(createResources2) 29 | } 30 | 31 | func ExampleGroup_Clear() { 32 | // 清空缓存的 buffer 33 | group.Clear() 34 | } 35 | -------------------------------------------------------------------------------- /watching/example/1gbslice/1gbslice.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | 7 | "github.com/songzhibin97/gkit/watching" 8 | ) 9 | 10 | func init() { 11 | http.HandleFunc("/make1gb", make1gbslice) 12 | go http.ListenAndServe(":10003", nil) 13 | } 14 | 15 | func main() { 16 | w := watching.NewWatching( 17 | watching.WithCollectInterval("2s"), 18 | watching.WithCoolDown("1m"), 19 | // watching.WithDumpPath("./tmp"), 20 | watching.WithTextDump(), 21 | watching.WithMemDump(1, 5, 10), 22 | ) 23 | w.EnableMemDump().Start() 24 | time.Sleep(time.Hour) 25 | } 26 | 27 | func make1gbslice(wr http.ResponseWriter, req *http.Request) { 28 | a := make([]byte, 1073741824) 29 | _ = a 30 | } 31 | -------------------------------------------------------------------------------- /coding/yaml/yaml.go: -------------------------------------------------------------------------------- 1 | package yaml 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/songzhibin97/gkit/coding" 7 | "gopkg.in/yaml.v2" 8 | ) 9 | 10 | const Name = "yaml" 11 | 12 | func init() { 13 | _ = coding.RegisterCode(code{}) 14 | } 15 | 16 | type code struct{} 17 | 18 | func (c code) Marshal(v interface{}) ([]byte, error) { 19 | return yaml.Marshal(v) 20 | } 21 | 22 | func (c code) Unmarshal(data []byte, v interface{}) error { 23 | rv := reflect.ValueOf(v) 24 | for rv.Kind() == reflect.Ptr { 25 | if rv.IsNil() { 26 | rv.Set(reflect.New(rv.Type().Elem())) 27 | } 28 | rv = rv.Elem() 29 | } 30 | return yaml.Unmarshal(data, v) 31 | } 32 | 33 | func (c code) Name() string { 34 | return Name 35 | } 36 | -------------------------------------------------------------------------------- /distributed/locker/lock_ridis/option.go: -------------------------------------------------------------------------------- 1 | package lock_ridis 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/songzhibin97/gkit/options" 7 | ) 8 | 9 | // config 10 | type config struct { 11 | // interval: 重试间隔时间 12 | // 只有 retries > 0 才有效 13 | // interval < 0 的话 retries 同样无效 14 | interval time.Duration 15 | 16 | // retries间隔次数 17 | // retries > 0 18 | retries int 19 | } 20 | 21 | // SetInterval 设置重试间隔时间 22 | func SetInterval(duration time.Duration) options.Option { 23 | return func(c interface{}) { 24 | c.(*config).interval = duration 25 | } 26 | } 27 | 28 | // SetRetries 设置重试次数 29 | func SetRetries(retries int) options.Option { 30 | return func(c interface{}) { 31 | c.(*config).retries = retries 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /internal/clock/ticker.go: -------------------------------------------------------------------------------- 1 | package clock 2 | 3 | import ( 4 | "sync/atomic" 5 | "time" 6 | ) 7 | 8 | var nowInMs = uint64(0) 9 | 10 | // StartTicker 启动一个后台任务,该任务每毫秒缓存当前时间戳,在高并发情况下可能会提供更好的性能. 11 | func StartTicker() { 12 | atomic.StoreUint64(&nowInMs, uint64(time.Now().UnixNano())/UnixTimeUnitOffset) 13 | go func() { 14 | defer func() { 15 | if err := recover(); err != nil { 16 | // no possible 17 | } 18 | }() 19 | for { 20 | now := uint64(time.Now().UnixNano()) / UnixTimeUnitOffset 21 | atomic.StoreUint64(&nowInMs, now) 22 | time.Sleep(time.Millisecond) 23 | } 24 | }() 25 | } 26 | 27 | // GetTimestamp 获得当前时间戳,单位是ms 28 | func GetTimestamp() uint64 { 29 | return atomic.LoadUint64(&nowInMs) 30 | } 31 | -------------------------------------------------------------------------------- /middleware/middleware.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import "context" 4 | 5 | // package middleware: 封装中间件格式 6 | 7 | // Endpoint 8 | // func(ctx context.Context,request interface{}) (response interface{},err error) 9 | type Endpoint func(context.Context, interface{}) (interface{}, error) 10 | 11 | // MiddleWare 方便链式操作 12 | type MiddleWare func(Endpoint) Endpoint 13 | 14 | // HandlerFunc 错误处理 15 | type HandlerFunc func(error) error 16 | 17 | // Chain 连接成链路 18 | // outer 最外层的 19 | func Chain(outer MiddleWare, others ...MiddleWare) MiddleWare { 20 | return func(next Endpoint) Endpoint { 21 | for i := len(others) - 1; i >= 0; i-- { // reverse 22 | next = others[i](next) 23 | } 24 | return outer(next) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /registry/subset.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "math/rand" 5 | ) 6 | 7 | // subset google src 子集选择法 8 | 9 | // Subset 子集选择法 10 | // instances 实例列表 11 | // size 选取的子集长度 12 | func Subset(instances []interface{}, clientID int, size int) []interface{} { 13 | if len(instances) <= size { 14 | return instances 15 | } 16 | count := len(instances) / size 17 | // 将客户端划分为多轮,每一轮计算使用同样的随机排列的列表 18 | round := clientID / count 19 | s := rand.NewSource(int64(round)) 20 | ra := rand.New(s) 21 | ra.Shuffle(len(instances), func(i, j int) { 22 | instances[i], instances[j] = instances[j], instances[i] 23 | }) 24 | // clientID 代表目前的客户端 25 | start := (clientID % count) * size 26 | return instances[start : start+size] 27 | } 28 | -------------------------------------------------------------------------------- /restrictor/ratelimite/example_test.go: -------------------------------------------------------------------------------- 1 | package ratelimite 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/juju/ratelimit" 8 | ) 9 | 10 | func ExampleNewRateLimit() { 11 | // ratelimit github.com/juju/ratelimit 12 | bucket := ratelimit.NewBucket(time.Second/2, 4) 13 | 14 | af, wf := NewRateLimit(bucket) 15 | // af.Allow()bool: 默认取1个token 16 | // af.Allow() == af.AllowN(time.Now(), 1) 17 | af.Allow() 18 | 19 | // af.AllowN(ctx,n)bool: 可以取N个token 20 | af.AllowN(time.Now(), 5) 21 | 22 | // wf.Wait(ctx) err: 等待ctx超时,默认取1个token 23 | // wf.Wait(ctx) == wf.WaitN(ctx, 1) 24 | _ = wf.Wait(context.TODO()) 25 | 26 | // wf.WaitN(ctx, n) err: 等待ctx超时,可以取N个token 27 | _ = wf.WaitN(context.TODO(), 5) 28 | } 29 | -------------------------------------------------------------------------------- /watching/example/deadloop/deadloop.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | 7 | "github.com/songzhibin97/gkit/watching" 8 | ) 9 | 10 | func init() { 11 | http.HandleFunc("/deadloop", deadloop) 12 | go http.ListenAndServe(":10003", nil) 13 | } 14 | 15 | func main() { 16 | w := watching.NewWatching( 17 | watching.WithCollectInterval("2s"), 18 | watching.WithCoolDown("1m"), 19 | watching.WithDumpPath("/tmp"), 20 | watching.WithCPUDump(10, 25, 80), 21 | ) 22 | w.EnableCPUDump().Start() 23 | time.Sleep(time.Hour) 24 | } 25 | 26 | func deadloop(wr http.ResponseWriter, req *http.Request) { 27 | for i := 0; i < 4; i++ { 28 | for { 29 | time.Sleep(time.Millisecond) 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /tools/bind/msgpack.go: -------------------------------------------------------------------------------- 1 | //go:build !nomsgpack 2 | // +build !nomsgpack 3 | 4 | package bind 5 | 6 | import ( 7 | "bytes" 8 | "io" 9 | "net/http" 10 | 11 | "github.com/ugorji/go/codec" 12 | ) 13 | 14 | type msgpackBinding struct{} 15 | 16 | func (msgpackBinding) Name() string { 17 | return "msgpack" 18 | } 19 | 20 | func (msgpackBinding) Bind(req *http.Request, obj interface{}) error { 21 | return decodeMsgPack(req.Body, obj) 22 | } 23 | 24 | func (msgpackBinding) BindBody(body []byte, obj interface{}) error { 25 | return decodeMsgPack(bytes.NewReader(body), obj) 26 | } 27 | 28 | func decodeMsgPack(r io.Reader, obj interface{}) error { 29 | cdc := new(codec.MsgpackHandle) 30 | return codec.NewDecoder(r, cdc).Decode(&obj) 31 | } 32 | -------------------------------------------------------------------------------- /watching/example/channelblock/channelblock.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | 7 | "github.com/songzhibin97/gkit/watching" 8 | ) 9 | 10 | func init() { 11 | http.HandleFunc("/chanblock", channelBlock) 12 | go http.ListenAndServe(":10003", nil) 13 | } 14 | 15 | func main() { 16 | w := watching.NewWatching( 17 | watching.WithCollectInterval("5s"), 18 | watching.WithCoolDown("1m"), 19 | watching.WithDumpPath("/tmp"), 20 | watching.WithTextDump(), 21 | watching.WithGoroutineDump(10, 25, 2000, 74), 22 | ) 23 | w.EnableGoroutineDump().Start() 24 | time.Sleep(time.Hour) 25 | } 26 | 27 | var nilCh chan int 28 | 29 | func channelBlock(wr http.ResponseWriter, req *http.Request) { 30 | nilCh <- 1 31 | } 32 | -------------------------------------------------------------------------------- /concurrent/or_done.go: -------------------------------------------------------------------------------- 1 | package concurrent 2 | 3 | import "reflect" 4 | 5 | // OrDone 任意channel完成后返回 6 | func OrDone(channels ...<-chan interface{}) <-chan interface{} { 7 | switch len(channels) { 8 | case 0: 9 | // 返回已经关闭的channel 通知各个接受者关闭 10 | c := make(chan interface{}) 11 | close(c) 12 | return c 13 | case 1: 14 | return channels[0] 15 | } 16 | orDone := make(chan interface{}, 1) 17 | go func() { 18 | defer close(orDone) 19 | var cases []reflect.SelectCase 20 | for _, channel := range channels { 21 | cases = append(cases, reflect.SelectCase{ 22 | Dir: reflect.SelectRecv, 23 | Chan: reflect.ValueOf(channel), 24 | }) 25 | } 26 | // 选择一个可用的 27 | reflect.Select(cases) 28 | }() 29 | return orDone 30 | } 31 | -------------------------------------------------------------------------------- /distributed/retry/fibonacci_test.go: -------------------------------------------------------------------------------- 1 | package retry 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func TestFibonacci(t *testing.T) { 9 | fibonacci := Fibonacci() 10 | 11 | sequence := []int{ 12 | fibonacci(), 13 | fibonacci(), 14 | fibonacci(), 15 | fibonacci(), 16 | fibonacci(), 17 | fibonacci(), 18 | } 19 | 20 | assert.EqualValues(t, sequence, []int{1, 1, 2, 3, 5, 8}) 21 | } 22 | 23 | func TestFibonacciNext(t *testing.T) { 24 | assert.Equal(t, 1, FibonacciNext(0)) 25 | assert.Equal(t, 2, FibonacciNext(1)) 26 | assert.Equal(t, 5, FibonacciNext(3)) 27 | assert.Equal(t, 5, FibonacciNext(4)) 28 | assert.Equal(t, 8, FibonacciNext(5)) 29 | assert.Equal(t, 13, FibonacciNext(8)) 30 | } 31 | -------------------------------------------------------------------------------- /restrictor/client_throttling/client_throttling.go: -------------------------------------------------------------------------------- 1 | package client_throttling 2 | 3 | import "github.com/songzhibin97/gkit/tools/float" 4 | 5 | // 客户端节流算法 6 | // google sre p330 7 | 8 | var DefaultK = 2.0 9 | 10 | // RejectionProbability 客户端节流算法 11 | // requests 请求数量 应用层代码发出的所有请求的数量总计(指运行于自适应节流系统之上的应用代码 12 | // accepts 请求接受数量 后端任务接受的请求数量 13 | // k 倍值 降低倍值会使自适应节流算法更加激进 14 | // 举例来说,假设将客户端请求的上限从request=2 * accepts调整为request=1.1* accepts,那么就意味着每10个后端请求之中只有1个会被拒绝 15 | func RejectionProbability(requests int, accepts int, k float64) float64 { 16 | return max(0, float.TruncFloat((float64(requests)-k*float64(accepts))/float64(requests+1), 2)) 17 | } 18 | 19 | func max(a, b float64) float64 { 20 | if a > b { 21 | return a 22 | } 23 | return b 24 | } 25 | -------------------------------------------------------------------------------- /parser/demo/test.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package api; 3 | 4 | // message2 5 | // message 6 | message Request{ 7 | map MapField = 1; 8 | fixed32 SliceField = 2; 9 | repeated string StringField = 3; 10 | uint32 Uint32Field = 4; 11 | } 12 | 13 | // notes 14 | 15 | // notessss 16 | 17 | message Response{ 18 | string Message = 1; 19 | string Username = 2; 20 | string Password = 3; 21 | } 22 | 23 | 24 | // server 25 | service User{ 26 | rpc Register ( Request) returns (Response) { 27 | option (google.api.http) = { 28 | post: "/register" 29 | }; 30 | } 31 | rpc Register2 (Request) returns (Response) { 32 | option (google.api.http) = { 33 | get: "/register2" 34 | }; 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /tools/bind/all.go: -------------------------------------------------------------------------------- 1 | package bind 2 | 3 | import ( 4 | "net/http" 5 | 6 | "github.com/songzhibin97/gkit/options" 7 | ) 8 | 9 | type all struct { 10 | contentType string 11 | selectorParse []Binding 12 | } 13 | 14 | func (*all) Name() string { 15 | return "all" 16 | } 17 | 18 | func (a *all) Bind(req *http.Request, obj interface{}) error { 19 | for _, binding := range a.selectorParse { 20 | if err := binding.Bind(req, obj); err != nil { 21 | return err 22 | } 23 | } 24 | return validate(obj) 25 | } 26 | 27 | func CreateBindAll(contentType string, option ...options.Option) Binding { 28 | a := &all{contentType: contentType, selectorParse: DefaultParse(contentType)} 29 | for _, o := range option { 30 | o(a) 31 | } 32 | return a 33 | } 34 | -------------------------------------------------------------------------------- /trace/option.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "github.com/songzhibin97/gkit/options" 5 | "go.opentelemetry.io/otel/propagation" 6 | "go.opentelemetry.io/otel/trace" 7 | ) 8 | 9 | type config struct { 10 | tracerProvider trace.TracerProvider 11 | propagator propagation.TextMapPropagator 12 | } 13 | 14 | // WithPropagator with tracer propagator. 15 | func WithPropagator(propagator propagation.TextMapPropagator) options.Option { 16 | return func(o interface{}) { 17 | o.(*config).propagator = propagator 18 | } 19 | } 20 | 21 | // WithTracerProvider with tracer provider. 22 | func WithTracerProvider(provider trace.TracerProvider) options.Option { 23 | return func(o interface{}) { 24 | o.(*config).tracerProvider = provider 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /cache/local_cache/err.go: -------------------------------------------------------------------------------- 1 | package local_cache 2 | 3 | import "errors" 4 | 5 | var ( 6 | CacheExist = errors.New("local_cache: cache exist") 7 | CacheNoExist = errors.New("local_cache: cache no exist") 8 | CacheExpire = errors.New("local_cache: cache expire") 9 | CacheTypeErr = errors.New("local_cache: cache incr type err") 10 | CacheGobErr = errors.New("local_cache: cache save gob err") 11 | ) 12 | 13 | func CacheErrExist(e error) bool { 14 | return errors.Is(e, CacheExist) 15 | } 16 | 17 | func CacheErrNoExist(e error) bool { 18 | return errors.Is(e, CacheNoExist) 19 | } 20 | 21 | func CacheErrExpire(e error) bool { 22 | return errors.Is(e, CacheExpire) 23 | } 24 | 25 | func CacheErrTypeErr(e error) bool { 26 | return errors.Is(e, CacheTypeErr) 27 | } 28 | -------------------------------------------------------------------------------- /concurrent/orderly_test.go: -------------------------------------------------------------------------------- 1 | package concurrent 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func productionOrder() (ret []*OrderlyTask) { 11 | for i := 0; i < 10; i++ { 12 | i := i 13 | ret = append(ret, NewOrderTask(func() { 14 | fmt.Println(i) 15 | })) 16 | } 17 | return ret 18 | } 19 | 20 | func TestOrderly(t *testing.T) { 21 | var slice []int 22 | var productionOrder func() []*OrderlyTask = func() (ret []*OrderlyTask) { 23 | for i := 0; i < 10; i++ { 24 | i := i 25 | ret = append(ret, NewOrderTask(func() { 26 | slice = append(slice, i) 27 | })) 28 | } 29 | return ret 30 | } 31 | Orderly(productionOrder()) 32 | assert.Equal(t, slice, []int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}) 33 | } 34 | -------------------------------------------------------------------------------- /sys/cpu/cpu_test.go: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func Test_CPUUsage(t *testing.T) { 12 | var stat Stat 13 | ReadStat(&stat) 14 | fmt.Println(stat) 15 | time.Sleep(time.Millisecond * 1000) 16 | for i := 0; i < 6; i++ { 17 | time.Sleep(time.Millisecond * 500) 18 | ReadStat(&stat) 19 | if stat.Usage == 0 { 20 | t.Fatalf("get cpu failed!cpu usage is zero!") 21 | } 22 | fmt.Println(stat) 23 | } 24 | } 25 | 26 | func TestStat(t *testing.T) { 27 | time.Sleep(time.Second * 2) 28 | var s Stat 29 | var i Info 30 | ReadStat(&s) 31 | i = GetInfo() 32 | 33 | assert.NotZero(t, s.Usage) 34 | assert.NotZero(t, i.Frequency) 35 | assert.NotZero(t, i.Quota) 36 | } 37 | -------------------------------------------------------------------------------- /cache/local_cache/sentinel.go: -------------------------------------------------------------------------------- 1 | package local_cache 2 | 3 | import ( 4 | "context" 5 | "time" 6 | ) 7 | 8 | // sentinel 哨兵 9 | type sentinel struct { 10 | // interval 间隔时间 0 不开启哨兵 11 | interval time.Duration 12 | // ctx context 13 | ctx context.Context 14 | // fn 哨兵周期执行的函数 15 | fn func() 16 | } 17 | 18 | func (s *sentinel) Start() { 19 | if s.interval <= 0 { 20 | return 21 | } 22 | tick := time.NewTicker(s.interval) 23 | defer tick.Stop() 24 | for { 25 | select { 26 | case <-tick.C: 27 | s.fn() 28 | case <-s.ctx.Done(): 29 | return 30 | } 31 | } 32 | } 33 | 34 | func NewSentinel(ctx context.Context, interval time.Duration, fn func()) *sentinel { 35 | return &sentinel{ 36 | interval: interval, 37 | ctx: ctx, 38 | fn: fn, 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /internal/sys/cpu/cpu_test.go: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | "time" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func Test_CPUUsage(t *testing.T) { 12 | var stat Stat 13 | ReadStat(&stat) 14 | fmt.Println(stat) 15 | time.Sleep(time.Millisecond * 1000) 16 | for i := 0; i < 6; i++ { 17 | time.Sleep(time.Millisecond * 500) 18 | ReadStat(&stat) 19 | if stat.Usage == 0 { 20 | t.Fatalf("get cpu failed!cpu usage is zero!") 21 | } 22 | fmt.Println(stat) 23 | } 24 | } 25 | 26 | func TestStat(t *testing.T) { 27 | time.Sleep(time.Second * 2) 28 | var s Stat 29 | var i Info 30 | ReadStat(&s) 31 | i = GetInfo() 32 | 33 | assert.NotZero(t, s.Usage) 34 | assert.NotZero(t, i.Frequency) 35 | assert.NotZero(t, i.Quota) 36 | } 37 | -------------------------------------------------------------------------------- /sys/stringx/is_test.go: -------------------------------------------------------------------------------- 1 | package stringx 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestIs(t *testing.T) { 10 | is := assert.New(t) 11 | 12 | is.False(IsNumeric("")) 13 | is.False(IsNumeric(" ")) 14 | is.False(IsNumeric(" bob ")) 15 | is.True(IsNumeric("123")) 16 | 17 | is.False(IsAlpha("")) 18 | is.False(IsAlpha(" ")) 19 | is.False(IsAlpha(" Voa ")) 20 | is.False(IsAlpha("123")) 21 | is.True(IsAlpha("Voa")) 22 | is.True(IsAlpha("bròwn")) 23 | 24 | is.False(IsAlphanumeric("")) 25 | is.False(IsAlphanumeric(" ")) 26 | is.False(IsAlphanumeric(" Voa ")) 27 | is.True(IsAlphanumeric("Voa")) 28 | is.True(IsAlphanumeric("123")) 29 | is.True(IsAlphanumeric("v123oa")) 30 | is.False(IsAlphanumeric("v123oa,")) 31 | } 32 | -------------------------------------------------------------------------------- /tools/bind/internal/json/json.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Bo-Yi Wu. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build !jsoniter 6 | // +build !jsoniter 7 | 8 | package json 9 | 10 | import ( 11 | json "github.com/json-iterator/go" 12 | ) 13 | 14 | var ( 15 | // Marshal is exported by gin/json package. 16 | Marshal = json.Marshal 17 | // Unmarshal is exported by gin/json package. 18 | Unmarshal = json.Unmarshal 19 | // MarshalIndent is exported by gin/json package. 20 | MarshalIndent = json.MarshalIndent 21 | // NewDecoder is exported by gin/json package. 22 | NewDecoder = json.NewDecoder 23 | // NewEncoder is exported by gin/json package. 24 | NewEncoder = json.NewEncoder 25 | ) 26 | -------------------------------------------------------------------------------- /tools/pointer/time_time.go: -------------------------------------------------------------------------------- 1 | package pointer 2 | 3 | import "time" 4 | 5 | // NowTimePointer 返回当前时间的指针表示形式 *time.Time 6 | func NowTimePointer() *time.Time { 7 | f := time.Now() 8 | return &f 9 | } 10 | 11 | // ToTimePointer 将time.Time类型转换为指针,如果时间为零值则返回空指针 12 | func ToTimePointer(t time.Time) *time.Time { 13 | if t.IsZero() { 14 | return nil 15 | } 16 | return &t 17 | } 18 | 19 | // FromTimePointer 从time.Time类型的指针中读取时间,如果为空指针,则读取到零值 20 | func FromTimePointer(t *time.Time) time.Time { 21 | return FromTimePointerOrDefault(t, time.Time{}) 22 | } 23 | 24 | // FromTimePointerOrDefault 从time.Time类型的指针中读取时间,如果为空指针,则返回defaultValue 25 | func FromTimePointerOrDefault(t *time.Time, defaultValue time.Time) time.Time { 26 | if t == nil { 27 | return defaultValue 28 | } 29 | return *t 30 | } 31 | -------------------------------------------------------------------------------- /sys/once/once.go: -------------------------------------------------------------------------------- 1 | package once 2 | 3 | import ( 4 | "sync" 5 | "sync/atomic" 6 | ) 7 | 8 | // Once 更强大的Once实现 9 | // 在调用 f 失败的时候不会设置 done 并且可以返回失败信息 10 | // 在失败的情况下也可以继续调用 Once 重试 11 | type Once struct { 12 | m sync.Mutex 13 | done uint32 14 | } 15 | 16 | // Do 传入的函数f有返回值error,如果初始化失败,需要返回失败的error 17 | // Do方法会把这个error返回给调用者 18 | func (o *Once) Do(f func() error) error { 19 | if atomic.LoadUint32(&o.done) == 1 { // fast path 20 | return nil 21 | } 22 | return o.slowDo(f) 23 | } 24 | 25 | // 如果还没有初始化 26 | func (o *Once) slowDo(f func() error) error { 27 | o.m.Lock() 28 | defer o.m.Unlock() 29 | var err error 30 | if o.done == 0 { // 双检查,还没有初始化 31 | err = f() 32 | if err == nil { // 初始化成功才将标记置为已初始化 33 | atomic.StoreUint32(&o.done, 1) 34 | } 35 | } 36 | return err 37 | } 38 | -------------------------------------------------------------------------------- /tools/pointer/string.go: -------------------------------------------------------------------------------- 1 | package pointer 2 | 3 | // ToStringPointer 将string类型的变量转换为对应的*string指针类型 4 | func ToStringPointer(v string) *string { 5 | return &v 6 | } 7 | 8 | // ToStringPointerOrNilIfEmpty 将string类型的变量转换为对应的*string指针类型,如果变量的值为空字符串的话则返回nil指针 9 | func ToStringPointerOrNilIfEmpty(v string) *string { 10 | if v == "" { 11 | return nil 12 | } 13 | return &v 14 | } 15 | 16 | // FromStringPointer 获取*string类型的指针的实际值,如果指针为nil的话则返回空字符串 17 | func FromStringPointer(p *string) string { 18 | return FromStringPointerOrDefaultIfNil(p, "") 19 | } 20 | 21 | // FromStringPointerOrDefaultIfNil 获取*string类型的指针的实际值,如果指针为nil的话则返回defaultValue 22 | func FromStringPointerOrDefaultIfNil(v *string, defaultValue string) string { 23 | if v == nil { 24 | return defaultValue 25 | } 26 | return *v 27 | } 28 | -------------------------------------------------------------------------------- /watching/example/alloc/alloc.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "net/http" 6 | "time" 7 | 8 | "github.com/songzhibin97/gkit/watching" 9 | ) 10 | 11 | func init() { 12 | http.HandleFunc("/alloc", alloc) 13 | go http.ListenAndServe(":10003", nil) 14 | } 15 | 16 | func main() { 17 | w := watching.NewWatching( 18 | watching.WithCollectInterval("2s"), 19 | watching.WithCoolDown("1m"), 20 | watching.WithDumpPath("./tmp"), 21 | watching.WithTextDump(), 22 | watching.WithMemDump(3, 25, 80), 23 | ) 24 | w.EnableMemDump().Start() 25 | time.Sleep(time.Hour) 26 | } 27 | 28 | func alloc(wr http.ResponseWriter, req *http.Request) { 29 | m := make(map[string]string, 102400) 30 | for i := 0; i < 1000; i++ { 31 | m[fmt.Sprint(i)] = fmt.Sprint(i) 32 | } 33 | _ = m 34 | } 35 | -------------------------------------------------------------------------------- /distributed/task/validate.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "errors" 5 | "reflect" 6 | ) 7 | 8 | var ( 9 | ErrTaskMustFunc = errors.New("task type must func") 10 | ErrTaskReturnNoValue = errors.New("task return is no value") 11 | ErrTaskReturnNoErr = errors.New("task return last values is must be error") 12 | ) 13 | 14 | func ValidateTask(task interface{}) error { 15 | v := reflect.ValueOf(task) 16 | t := v.Type() 17 | if t.Kind() != reflect.Func { 18 | return ErrTaskMustFunc 19 | } 20 | 21 | if t.NumOut() < 1 { 22 | return ErrTaskReturnNoValue 23 | } 24 | lastReturnType := t.Out(t.NumOut() - 1) 25 | errorInterface := reflect.TypeOf((*error)(nil)).Elem() 26 | if !lastReturnType.Implements(errorInterface) { 27 | return ErrTaskReturnNoErr 28 | } 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /internal/sys/once/once.go: -------------------------------------------------------------------------------- 1 | package once 2 | 3 | import ( 4 | "sync" 5 | "sync/atomic" 6 | ) 7 | 8 | // Once 更强大的Once实现 9 | // 在调用 f 失败的时候不会设置 done 并且可以返回失败信息 10 | // 在失败的情况下也可以继续调用 Once 重试 11 | type Once struct { 12 | m sync.Mutex 13 | done uint32 14 | } 15 | 16 | // Do 传入的函数f有返回值error,如果初始化失败,需要返回失败的error 17 | // Do方法会把这个error返回给调用者 18 | func (o *Once) Do(f func() error) error { 19 | if atomic.LoadUint32(&o.done) == 1 { // fast path 20 | return nil 21 | } 22 | return o.slowDo(f) 23 | } 24 | 25 | // 如果还没有初始化 26 | func (o *Once) slowDo(f func() error) error { 27 | o.m.Lock() 28 | defer o.m.Unlock() 29 | var err error 30 | if o.done == 0 { // 双检查,还没有初始化 31 | err = f() 32 | if err == nil { // 初始化成功才将标记置为已初始化 33 | atomic.StoreUint32(&o.done, 1) 34 | } 35 | } 36 | return err 37 | } 38 | -------------------------------------------------------------------------------- /tools/pointer/bool.go: -------------------------------------------------------------------------------- 1 | package pointer 2 | 3 | // ToBoolPointer 将布尔变量转换为布尔指针 4 | func ToBoolPointer(boolValue bool) *bool { 5 | return &boolValue 6 | } 7 | 8 | // ToBoolPointerOrNilIfFalse 将布尔变量转换为布尔类型的指针,如果变量的值为false的话则转换为nil指针 9 | func ToBoolPointerOrNilIfFalse(boolValue bool) *bool { 10 | if boolValue { 11 | return &boolValue 12 | } 13 | return nil 14 | } 15 | 16 | // FromBoolPointer 获取布尔指针实际指向的值,如果指针为nil的话则返回false 17 | func FromBoolPointer(boolPointer *bool) bool { 18 | return FromBoolPointerOrDefault(boolPointer, false) 19 | } 20 | 21 | // FromBoolPointerOrDefault 获取布尔指针实际指向的值,如果指针为nil的话则返回defaultValue 22 | func FromBoolPointerOrDefault(boolPointer *bool, defaultValue bool) bool { 23 | if boolPointer == nil { 24 | return defaultValue 25 | } else { 26 | return *boolPointer 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tools/bind/header.go: -------------------------------------------------------------------------------- 1 | package bind 2 | 3 | import ( 4 | "net/http" 5 | "net/textproto" 6 | "reflect" 7 | ) 8 | 9 | type headerBinding struct{} 10 | 11 | func (headerBinding) Name() string { 12 | return "header" 13 | } 14 | 15 | func (headerBinding) Bind(req *http.Request, obj interface{}) error { 16 | return mapHeader(obj, req.Header) 17 | } 18 | 19 | func mapHeader(ptr interface{}, h map[string][]string) error { 20 | return mappingByPtr(ptr, headerSource(h), "header") 21 | } 22 | 23 | type headerSource map[string][]string 24 | 25 | var _ setter = headerSource(nil) 26 | 27 | func (hs headerSource) TrySet(value reflect.Value, field reflect.StructField, tagValue string, opt setOptions) (isSetted bool, err error) { 28 | return setByForm(value, field, hs, textproto.CanonicalMIMEHeaderKey(tagValue), opt) 29 | } 30 | -------------------------------------------------------------------------------- /distributed/backend/backend_mongodb/option.go: -------------------------------------------------------------------------------- 1 | package backend_mongodb 2 | 3 | import "github.com/songzhibin97/gkit/options" 4 | 5 | type config struct { 6 | // DatabaseName db名称 7 | databaseName string 8 | // TableTaskName 任务表名称 9 | tableTaskName string 10 | // TableGroupName 组表名称 11 | tableGroupName string 12 | } 13 | 14 | func SetDatabaseName(databaseName string) options.Option { 15 | return func(c interface{}) { 16 | c.(*config).databaseName = databaseName 17 | } 18 | } 19 | 20 | func SetTableTaskName(tableTaskName string) options.Option { 21 | return func(c interface{}) { 22 | c.(*config).tableTaskName = tableTaskName 23 | } 24 | } 25 | 26 | func SetTableGroupName(tableGroupName string) options.Option { 27 | return func(c interface{}) { 28 | c.(*config).tableGroupName = tableGroupName 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /tools/bind/internal/json/jsoniter.go: -------------------------------------------------------------------------------- 1 | // Copyright 2017 Bo-Yi Wu. All rights reserved. 2 | // Use of this source code is governed by a MIT style 3 | // license that can be found in the LICENSE file. 4 | 5 | //go:build jsoniter 6 | // +build jsoniter 7 | 8 | package json 9 | 10 | import jsoniter "github.com/json-iterator/go" 11 | 12 | var ( 13 | json = jsoniter.ConfigCompatibleWithStandardLibrary 14 | // Marshal is exported by gin/json package. 15 | Marshal = json.Marshal 16 | // Unmarshal is exported by gin/json package. 17 | Unmarshal = json.Unmarshal 18 | // MarshalIndent is exported by gin/json package. 19 | MarshalIndent = json.MarshalIndent 20 | // NewDecoder is exported by gin/json package. 21 | NewDecoder = json.NewDecoder 22 | // NewEncoder is exported by gin/json package. 23 | NewEncoder = json.NewEncoder 24 | ) 25 | -------------------------------------------------------------------------------- /structure/zset/opt.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ByteDance Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package zset 16 | 17 | // RangeOpt describes the whether the min/max is exclusive in score range. 18 | type RangeOpt struct { 19 | ExcludeMin bool 20 | ExcludeMax bool 21 | } 22 | -------------------------------------------------------------------------------- /overload/bbr/example_test.go: -------------------------------------------------------------------------------- 1 | package bbr 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/songzhibin97/gkit/overload" 7 | ) 8 | 9 | func ExampleNewGroup() { 10 | group := NewGroup() 11 | // 如果没有就会创建 12 | limiter := group.Get("key") 13 | f, err := limiter.Allow(context.TODO()) 14 | if err != nil { 15 | // 代表已经过载了,服务不允许接入 16 | return 17 | } 18 | // Op:流量实际的操作类型回写记录指标 19 | f(overload.DoneInfo{Op: overload.Success}) 20 | } 21 | 22 | func ExampleNewLimiter() { 23 | // 建立Group 中间件 24 | middle := NewLimiter() 25 | 26 | // 在middleware中 27 | // ctx中携带这两个可配置的有效数据 28 | // 可以通过 ctx.Set 29 | 30 | // 配置获取限制器类型,可以根据不同api获取不同的限制器 31 | ctx := context.WithValue(context.TODO(), LimitKey, "key") 32 | 33 | // 可配置成功是否上报 34 | // 必须是 overload.Op 类型 35 | ctx = context.WithValue(ctx, LimitOp, overload.Success) 36 | 37 | _ = middle 38 | } 39 | -------------------------------------------------------------------------------- /timeout/ctime_test.go: -------------------------------------------------------------------------------- 1 | package timeout 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestShrink(t *testing.T) { 10 | c1, cancel := context.WithTimeout(context.Background(), 5) 11 | defer cancel() 12 | type args struct { 13 | c context.Context 14 | d time.Duration 15 | } 16 | tests := []struct { 17 | name string 18 | args args 19 | want time.Duration 20 | }{ 21 | {"上游没有设置链路超时时间", args{c: context.Background(), d: 10}, 10}, 22 | 23 | {"上游链路设置链路时间且当前时间超时时间大于流转链路时间", args{c: c1, d: 10}, 5}, 24 | {"上路链路设置超时时间事件小于当前时间", args{c: c1, d: 3}, 3}, 25 | } 26 | for _, tt := range tests { 27 | t.Run(tt.name, func(t *testing.T) { 28 | got, _, _ := Shrink(tt.args.c, tt.args.d) 29 | if got > tt.want { 30 | t.Errorf("Shrink() got = %v, want %v", got, tt.want) 31 | } 32 | }) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /distributed/example/worker/worker.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/songzhibin97/gkit/distributed/example" 7 | "github.com/songzhibin97/gkit/distributed/task" 8 | ) 9 | 10 | func main() { 11 | server := example.InitServer() 12 | if server == nil { 13 | panic("server is empty") 14 | } 15 | worker := server.NewWorker("worker", 0, server.GetConfig().ConsumeQueue) 16 | worker.SetErrorHandler(func(err error) { 17 | fmt.Println("I am an error handler:", err) 18 | }) 19 | worker.SetBeforeTaskHandler(func(task *task.Signature) { 20 | fmt.Println("I am a start of task handler for:", task.Name) 21 | }) 22 | 23 | worker.SetAfterTaskHandler(func(task *task.Signature) { 24 | fmt.Println("I am a end of task handler for:", task.Name) 25 | }) 26 | err := worker.Start() 27 | fmt.Println("end worker:", err) 28 | } 29 | -------------------------------------------------------------------------------- /tools/bind/protobuf.go: -------------------------------------------------------------------------------- 1 | package bind 2 | 3 | import ( 4 | "io/ioutil" 5 | "net/http" 6 | 7 | "google.golang.org/protobuf/proto" 8 | ) 9 | 10 | type protobufBinding struct{} 11 | 12 | func (protobufBinding) Name() string { 13 | return "protobuf" 14 | } 15 | 16 | func (b protobufBinding) Bind(req *http.Request, obj interface{}) error { 17 | buf, err := ioutil.ReadAll(req.Body) 18 | if err != nil { 19 | return err 20 | } 21 | return b.BindBody(buf, obj) 22 | } 23 | 24 | func (protobufBinding) BindBody(body []byte, obj interface{}) error { 25 | if err := proto.Unmarshal(body, obj.(proto.Message)); err != nil { 26 | return err 27 | } 28 | // Here it's same to return validate(obj), but util now we can't add 29 | // `binding:""` to the struct which automatically generate by gen-proto 30 | return nil 31 | // return validate(obj) 32 | } 33 | -------------------------------------------------------------------------------- /container/queue/codel/example_test.go: -------------------------------------------------------------------------------- 1 | package codel 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/songzhibin97/gkit/overload/bbr" 7 | ) 8 | 9 | var queue *Queue 10 | 11 | func ExampleNew() { 12 | // 默认配置 13 | // queue = NewQueue() 14 | 15 | // 可供选择配置选项 16 | 17 | // 设置对列延时 18 | // SetTarget(40) 19 | 20 | // 设置滑动窗口最小时间宽度 21 | // SetInternal(1000) 22 | 23 | queue = NewQueue(SetTarget(40), SetInternal(1000)) 24 | } 25 | 26 | func ExampleQueue_Stat() { 27 | // start 体现 CoDel 状态信息 28 | start := queue.Stat() 29 | 30 | _ = start 31 | } 32 | 33 | func ExampleQueue_Push() { 34 | // 入队 35 | if err := queue.Push(context.TODO()); err != nil { 36 | if err == bbr.LimitExceed { 37 | // todo 处理过载保护错误 38 | } else { 39 | // todo 处理其他错误 40 | } 41 | } 42 | } 43 | 44 | func ExampleQueue_Pop() { 45 | // 出队,没有请求则会阻塞 46 | queue.Pop() 47 | } 48 | -------------------------------------------------------------------------------- /tools/pointer/template.txt: -------------------------------------------------------------------------------- 1 | package pointer 2 | 3 | // To{{upper}}Pointer 将{{lower}}类型的变量转换为对应的*{{lower}}指针类型 4 | func To{{upper}}Pointer(v {{lower}}) *{{lower}} { 5 | return &v 6 | } 7 | 8 | // To{{upper}}PointerOrNilIfZero 将{{lower}}类型的变量转换为对应的*{{lower}}指针类型,如果变量的值为0的话则返回nil指针 9 | func To{{upper}}PointerOrNilIfZero(v {{lower}}) *{{lower}} { 10 | if v == 0 { 11 | return nil 12 | } 13 | return &v 14 | } 15 | 16 | // From{{upper}}Pointer 获取*{{lower}}类型的指针的实际值,如果指针为nil的话则返回0 17 | func From{{upper}}Pointer(p *{{lower}}) {{lower}} { 18 | return From{{upper}}PointerOrDefaultIfNil(p, 0) 19 | } 20 | 21 | // From{{upper}}PointerOrDefaultIfNil 获取*{{lower}}类型的指针的实际值,如果指针为nil的话则返回defaultValue 22 | func From{{upper}}PointerOrDefaultIfNil(v *{{lower}}, defaultValue {{lower}}) {{lower}} { 23 | if v == nil { 24 | return defaultValue 25 | } 26 | return *v 27 | } 28 | -------------------------------------------------------------------------------- /cache/mbuffer/utils.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ByteDance Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package mbuffer 16 | 17 | import "math/bits" 18 | 19 | func bsr(x int) int { 20 | return bits.Len(uint(x)) - 1 21 | } 22 | 23 | func isPowerOfTwo(x int) bool { 24 | return (x & (-x)) == x 25 | } 26 | -------------------------------------------------------------------------------- /timeout/stamp.go: -------------------------------------------------------------------------------- 1 | package timeout 2 | 3 | import ( 4 | "database/sql/driver" 5 | "strconv" 6 | "time" 7 | ) 8 | 9 | // Stamp 用于MySQL时间戳转换 10 | // 实现了 sql.Scanner 接口 11 | type Stamp int64 12 | 13 | // Scan 扫描赋值 14 | func (jt *Stamp) Scan(src interface{}) (err error) { 15 | // 断言,只处理string以及原生的time.Time 16 | switch sc := src.(type) { 17 | case []byte: 18 | var i int64 19 | i, err = strconv.ParseInt(string(sc), 10, 64) 20 | *jt = Stamp(i) 21 | case time.Time: 22 | *jt = Stamp(sc.Unix()) 23 | case string: 24 | var i int64 25 | i, err = strconv.ParseInt(sc, 10, 64) 26 | *jt = Stamp(i) 27 | } 28 | return 29 | } 30 | 31 | // Value 获取driver.Value 32 | func (jt Stamp) Value() driver.Value { 33 | return time.Unix(int64(jt), 0) 34 | } 35 | 36 | // Time 转化time.Time 37 | func (jt Stamp) Time() time.Time { 38 | return time.Unix(int64(jt), 0) 39 | } 40 | -------------------------------------------------------------------------------- /registry/registry.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import "context" 4 | 5 | // package registry: 服务注册发现 6 | 7 | // Registrar 注册抽象 8 | type Registrar interface { 9 | // Register 注册 10 | Register(ctx context.Context, service *ServiceInstance) error 11 | // Deregister 注销 12 | Deregister(ctx context.Context, service *ServiceInstance) error 13 | } 14 | 15 | // Discovery 服务发现抽象 16 | type Discovery interface { 17 | // GetService 返回服务名相关的服务实例 18 | GetService(ctx context.Context, serviceName string) ([]*ServiceInstance, error) 19 | // Watch 根据服务名创建监控 20 | Watch(ctx context.Context, serviceName string) (Watcher, error) 21 | } 22 | 23 | // Watcher 服务监控 24 | // Watch需要满足以下条件 25 | // 1. 第一次 GetService 的列表不为空 26 | // 2. 发现任何服务实例更改 27 | // 不满足以上两种条件,Next则会无限等待直到上下文截止 28 | type Watcher interface { 29 | Next() ([]*ServiceInstance, error) 30 | // Stop 停止监控行为 31 | Stop() error 32 | } 33 | -------------------------------------------------------------------------------- /watching/ring.go: -------------------------------------------------------------------------------- 1 | package watching 2 | 3 | type ring struct { 4 | data []int 5 | idx int 6 | sum int 7 | maxLen int 8 | } 9 | 10 | func newRing(maxLen int) ring { 11 | return ring{ 12 | data: make([]int, 0, maxLen), 13 | idx: 0, 14 | maxLen: maxLen, 15 | } 16 | } 17 | 18 | func (r *ring) push(i int) { 19 | if r.maxLen == 0 { 20 | return 21 | } 22 | 23 | // the first round 24 | if len(r.data) < r.maxLen { 25 | r.sum += i 26 | r.data = append(r.data, i) 27 | return 28 | } 29 | 30 | r.sum += i - r.data[r.idx] 31 | 32 | // the ring is expanded, just write to the position 33 | r.data[r.idx] = i 34 | r.idx = (r.idx + 1) % r.maxLen 35 | } 36 | 37 | func (r *ring) avg() int { 38 | // Check if the len(r.data) is zero before dividing 39 | if r.maxLen == 0 || len(r.data) == 0 { 40 | return 0 41 | } 42 | return r.sum / len(r.data) 43 | } 44 | -------------------------------------------------------------------------------- /tools/deepcopy/deepcopy_test.go: -------------------------------------------------------------------------------- 1 | package deepcopy 2 | 3 | import ( 4 | "github.com/stretchr/testify/assert" 5 | "testing" 6 | ) 7 | 8 | func Test_deepCopy(t *testing.T) { 9 | type ( 10 | mock struct { 11 | Name string 12 | Age int 13 | Money float64 14 | Attribute map[string]string 15 | Friends []string 16 | } 17 | ) 18 | var ( 19 | m1 = &mock{ 20 | Name: "gkit", 21 | Age: 21, 22 | Money: 10.01, 23 | Attribute: map[string]string{"job": "engineer"}, 24 | Friends: []string{"one", "two", "three"}, 25 | } 26 | m2 = mock{ 27 | Name: "tikg", 28 | Age: 12, 29 | Money: 1.01, 30 | Attribute: map[string]string{"engineer": "job"}, 31 | Friends: []string{"three", "two", "one"}, 32 | } 33 | ) 34 | err := DeepCopy(m1, &m2) 35 | assert.NoError(t, err) 36 | assert.Equal(t, *m1, m2) 37 | } 38 | -------------------------------------------------------------------------------- /page_token/option.go: -------------------------------------------------------------------------------- 1 | package page_token 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/songzhibin97/gkit/encrypt/aes" 7 | 8 | "github.com/songzhibin97/gkit/options" 9 | ) 10 | 11 | func SetMaxIndex(max int) options.Option { 12 | return func(o interface{}) { 13 | if t, ok := o.(*token); ok { 14 | t.maxIndex = max 15 | } 16 | } 17 | } 18 | 19 | func SetMaxElements(max int) options.Option { 20 | return func(o interface{}) { 21 | if t, ok := o.(*token); ok { 22 | t.maxElements = max 23 | } 24 | } 25 | } 26 | 27 | func SetSalt(salt string) options.Option { 28 | return func(o interface{}) { 29 | if t, ok := o.(*token); ok { 30 | t.salt = aes.PadKey(salt) 31 | } 32 | } 33 | } 34 | 35 | func SetTimeLimitation(d time.Duration) options.Option { 36 | return func(o interface{}) { 37 | if t, ok := o.(*token); ok { 38 | t.timeLimitation = d 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /timeout/human.go: -------------------------------------------------------------------------------- 1 | package timeout 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | func HumanDurationFormat(stamp int64, designation ...time.Time) string { 9 | designation = append(designation, time.Now()) 10 | now := designation[0].Sub(time.Unix(stamp, 0)) 11 | 12 | if now < time.Minute { 13 | return "刚刚" 14 | } 15 | 16 | if now < time.Hour { 17 | return fmt.Sprintf("%d分钟前", now/(time.Minute)) 18 | } 19 | 20 | if now < 24*time.Hour { 21 | return fmt.Sprintf("%d小时前", now/(time.Hour)) 22 | } 23 | 24 | if now < 7*24*time.Hour { 25 | return fmt.Sprintf("%d天前", now/(24*time.Hour)) 26 | } 27 | 28 | if now < 30*7*24*time.Hour { 29 | return fmt.Sprintf("%d周前", now/(7*24*time.Hour)) 30 | } 31 | 32 | if now < 12*30*7*24*time.Hour { 33 | return fmt.Sprintf("%d月前", now/(30*7*24*time.Hour)) 34 | } 35 | 36 | return fmt.Sprintf("%d年前", now/(12*30*7*24*time.Hour)) 37 | } 38 | -------------------------------------------------------------------------------- /distributed/task/meta.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import "sync" 4 | 5 | // Meta task可以携带元信息 6 | type Meta struct { 7 | meta map[string]interface{} 8 | sync.RWMutex 9 | safe bool 10 | } 11 | 12 | // NewMeta 生成meta信息 13 | func NewMeta(safe bool) *Meta { 14 | return &Meta{ 15 | meta: make(map[string]interface{}), 16 | safe: safe, 17 | } 18 | } 19 | 20 | // Set 如果存在会覆盖 21 | func (m *Meta) Set(key string, value interface{}) { 22 | if m.safe { 23 | m.Lock() 24 | defer m.Unlock() 25 | } 26 | m.meta[key] = value 27 | } 28 | 29 | func (m *Meta) Get(key string) (interface{}, bool) { 30 | if m.safe { 31 | m.RLock() 32 | defer m.RUnlock() 33 | } 34 | v, ok := m.meta[key] 35 | return v, ok 36 | } 37 | 38 | func (m *Meta) Range(f func(key string, value interface{})) { 39 | if m.safe { 40 | m.Lock() 41 | defer m.Unlock() 42 | } 43 | for k, v := range m.meta { 44 | f(k, v) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /gctuner/finalizer_test.go: -------------------------------------------------------------------------------- 1 | package gctuner 2 | 3 | import ( 4 | "runtime" 5 | "runtime/debug" 6 | "sync/atomic" 7 | "testing" 8 | 9 | "github.com/stretchr/testify/assert" 10 | ) 11 | 12 | func TestFinalizer(t *testing.T) { 13 | // disable gc 14 | debug.SetGCPercent(-1) 15 | defer debug.SetGCPercent(100) 16 | 17 | maxCount := int32(16) 18 | is := assert.New(t) 19 | var count int32 20 | f := newFinalizer(func() { 21 | n := atomic.AddInt32(&count, 1) 22 | if n > maxCount { 23 | t.Fatalf("cannot exec finalizer callback after f has been gc") 24 | } 25 | }) 26 | for atomic.LoadInt32(&count) < maxCount { 27 | runtime.GC() 28 | } 29 | is.Nil(f.ref) 30 | f.stop() 31 | // when f stopped, finalizer callback will not be called 32 | lastCount := atomic.LoadInt32(&count) 33 | for i := 0; i < 10; i++ { 34 | runtime.GC() 35 | is.Equal(lastCount, atomic.LoadInt32(&count)) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /delayed/option.go: -------------------------------------------------------------------------------- 1 | package delayed 2 | 3 | import ( 4 | "github.com/songzhibin97/gkit/options" 5 | "os" 6 | "time" 7 | ) 8 | 9 | // SetCheckTime 设置检查时间 10 | func SetCheckTime(checkTime time.Duration) options.Option { 11 | return func(o interface{}) { 12 | o.(*DispatchingDelayed).checkTime = checkTime 13 | } 14 | } 15 | 16 | // SetWorkerNumber 设置并发数 17 | func SetWorkerNumber(w int64) options.Option { 18 | return func(o interface{}) { 19 | o.(*DispatchingDelayed).Worker = w 20 | } 21 | } 22 | 23 | // SetSingle 设置监控信号 24 | func SetSingle(signal ...os.Signal) options.Option { 25 | return func(o interface{}) { 26 | o.(*DispatchingDelayed).signal = signal 27 | } 28 | } 29 | 30 | // SetSingleCallback 设置监控信号回调 31 | func SetSingleCallback(callback func(signal os.Signal, d *DispatchingDelayed)) options.Option { 32 | return func(o interface{}) { 33 | o.(*DispatchingDelayed).signalCallback = callback 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /distributed/controller/controller.go: -------------------------------------------------------------------------------- 1 | package controller 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/songzhibin97/gkit/distributed/task" 7 | ) 8 | 9 | type Controller interface { 10 | // RegisterTask 注册任务 11 | RegisterTask(name ...string) 12 | 13 | // IsRegisterTask 判断任务是否注册 14 | IsRegisterTask(name string) bool 15 | 16 | // StartConsuming 开始消费 17 | StartConsuming(concurrency int, handler task.Processor) (bool, error) 18 | 19 | // StopConsuming 停止消费 20 | StopConsuming() 21 | 22 | // Publish 任务发布 23 | Publish(ctx context.Context, t *task.Signature) error 24 | 25 | // GetPendingTasks 获取等待任务 26 | GetPendingTasks(queue string) ([]*task.Signature, error) 27 | 28 | // GetDelayedTasks 获取延时任务 29 | GetDelayedTasks() ([]*task.Signature, error) 30 | 31 | // SetConsumingQueue 设置消费队列名称 32 | SetConsumingQueue(consumingQueue string) 33 | 34 | // SetDelayedQueue 设置延迟队列名称 35 | SetDelayedQueue(delayedQueue string) 36 | } 37 | -------------------------------------------------------------------------------- /tools/pointer/time_duration.go: -------------------------------------------------------------------------------- 1 | package pointer 2 | 3 | import "time" 4 | 5 | // ToDurationPointer 将time.Duration类型的变量转换为对应的*time.Duration指针类型 6 | func ToDurationPointer(v time.Duration) *time.Duration { 7 | return &v 8 | } 9 | 10 | // ToDurationPointerOrNilIfZero 将time.Duration类型的变量转换为对应的*time.Duration指针类型,如果变量的值为0的话则返回nil指针 11 | func ToDurationPointerOrNilIfZero(v time.Duration) *time.Duration { 12 | if v == 0 { 13 | return nil 14 | } 15 | return &v 16 | } 17 | 18 | // FromDurationPointer 获取*time.Duration类型的指针的实际值,如果指针为nil的话则返回0 19 | func FromDurationPointer(p *time.Duration) time.Duration { 20 | return FromDurationPointerOrDefaultIfNil(p, 0) 21 | } 22 | 23 | // FromDurationPointerOrDefaultIfNil 获取*time.Duration类型的指针的实际值,如果指针为nil的话则返回defaultValue 24 | func FromDurationPointerOrDefaultIfNil(v *time.Duration, defaultValue time.Duration) time.Duration { 25 | if v == nil { 26 | return defaultValue 27 | } 28 | return *v 29 | } 30 | -------------------------------------------------------------------------------- /sys/cpu/psutilcpu.go: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import ( 4 | "time" 5 | 6 | c "github.com/shirou/gopsutil/cpu" 7 | ) 8 | 9 | type psutilCPU struct { 10 | interval time.Duration 11 | } 12 | 13 | func newPsutilCPU(interval time.Duration) (cpu *psutilCPU, err error) { 14 | cpu = &psutilCPU{interval: interval} 15 | _, err = cpu.Usage() 16 | if err != nil { 17 | return 18 | } 19 | return 20 | } 21 | 22 | func (ps *psutilCPU) Usage() (u uint64, err error) { 23 | var percents []float64 24 | percents, err = c.Percent(ps.interval, false) 25 | if err == nil { 26 | u = uint64(percents[0] * 10) 27 | } 28 | return 29 | } 30 | 31 | func (ps *psutilCPU) Info() (info Info) { 32 | stats, err := c.Info() 33 | if err != nil { 34 | return 35 | } 36 | cores, err := c.Counts(true) 37 | if err != nil { 38 | return 39 | } 40 | info = Info{ 41 | Frequency: uint64(stats[0].Mhz), 42 | Quota: float64(cores), 43 | } 44 | return 45 | } 46 | -------------------------------------------------------------------------------- /wgroup/wgroup.go: -------------------------------------------------------------------------------- 1 | package wgroup 2 | 3 | import ( 4 | "context" 5 | "sync" 6 | 7 | "github.com/songzhibin97/gkit/goroutine" 8 | ) 9 | 10 | type Group struct { 11 | wg sync.WaitGroup 12 | goroutine goroutine.GGroup 13 | } 14 | 15 | func (g *Group) Wait() { 16 | g.wg.Wait() 17 | } 18 | 19 | func (g *Group) AddTask(f func()) bool { 20 | g.wg.Add(1) 21 | return g.goroutine.AddTask(func() { 22 | defer g.wg.Done() 23 | f() 24 | }) 25 | } 26 | 27 | func (g *Group) AddTaskN(ctx context.Context, f func()) bool { 28 | g.wg.Add(1) 29 | return g.goroutine.AddTaskN(ctx, func() { 30 | defer g.wg.Done() 31 | f() 32 | }) 33 | } 34 | 35 | func WithContextGroup(group goroutine.GGroup) *Group { 36 | g := &Group{} 37 | g.goroutine = group 38 | return g 39 | } 40 | 41 | // WithContext 实例化方法 42 | func WithContext(ctx context.Context) *Group { 43 | g := &Group{} 44 | g.goroutine = goroutine.NewGoroutine(ctx) 45 | return g 46 | } 47 | -------------------------------------------------------------------------------- /restrictor/rate/example_test.go: -------------------------------------------------------------------------------- 1 | package rate 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "golang.org/x/time/rate" 8 | ) 9 | 10 | func ExampleNewRate() { 11 | // 第一个参数是 r Limit。代表每秒可以向 Token 桶中产生多少 token。Limit 实际上是 float64 的别名 12 | // 第二个参数是 b int。b 代表 Token 桶的容量大小。 13 | // limit := Every(100 * time.Millisecond); 14 | // limiter := rate.NewLimiter(limit, 4) 15 | // 以上就表示每 100ms 往桶中放一个 Token。本质上也就是一秒钟产生 10 个。 16 | 17 | // rate: golang.org/x/time/rate 18 | limiter := rate.NewLimiter(2, 4) 19 | 20 | af, wf := NewRate(limiter) 21 | 22 | // af.Allow()bool: 默认取1个token 23 | // af.Allow() == af.AllowN(time.Now(), 1) 24 | af.Allow() 25 | 26 | // af.AllowN(ctx,n)bool: 可以取N个token 27 | af.AllowN(time.Now(), 5) 28 | 29 | // wf.Wait(ctx) err: 等待ctx超时,默认取1个token 30 | // wf.Wait(ctx) == wf.WaitN(ctx, 1) 31 | _ = wf.Wait(context.TODO()) 32 | 33 | // wf.WaitN(ctx, n) err: 等待ctx超时,可以取N个token 34 | _ = wf.WaitN(context.TODO(), 5) 35 | } 36 | -------------------------------------------------------------------------------- /internal/sys/cpu/psutilcpu.go: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import ( 4 | "time" 5 | 6 | c "github.com/shirou/gopsutil/cpu" 7 | ) 8 | 9 | type psutilCPU struct { 10 | interval time.Duration 11 | } 12 | 13 | func newPsutilCPU(interval time.Duration) (cpu *psutilCPU, err error) { 14 | cpu = &psutilCPU{interval: interval} 15 | _, err = cpu.Usage() 16 | if err != nil { 17 | return 18 | } 19 | return 20 | } 21 | 22 | func (ps *psutilCPU) Usage() (u uint64, err error) { 23 | var percents []float64 24 | percents, err = c.Percent(ps.interval, false) 25 | if err == nil { 26 | u = uint64(percents[0] * 10) 27 | } 28 | return 29 | } 30 | 31 | func (ps *psutilCPU) Info() (info Info) { 32 | stats, err := c.Info() 33 | if err != nil { 34 | return 35 | } 36 | cores, err := c.Counts(true) 37 | if err != nil { 38 | return 39 | } 40 | info = Info{ 41 | Frequency: uint64(stats[0].Mhz), 42 | Quota: float64(cores), 43 | } 44 | return 45 | } 46 | -------------------------------------------------------------------------------- /internal/runtimex/readunaligned_bigendian.go: -------------------------------------------------------------------------------- 1 | //go:build ppc64 || s390x || mips || mips64 2 | // +build ppc64 s390x mips mips64 3 | 4 | // 5 | // from golang-go/src/os/endian_little.go 6 | 7 | package runtimex 8 | 9 | import ( 10 | "unsafe" 11 | ) 12 | 13 | func ReadUnaligned64(p unsafe.Pointer) uint64 { 14 | // Equal to runtime.readUnaligned64, but this function can be inlined 15 | // compared to use runtime.readUnaligned64 via go:linkname. 16 | q := (*[8]byte)(p) 17 | return uint64(q[7]) | uint64(q[6])<<8 | uint64(q[5])<<16 | uint64(q[4])<<24 | 18 | uint64(q[3])<<32 | uint64(q[2])<<40 | uint64(q[1])<<48 | uint64(q[0])<<56 19 | } 20 | 21 | func ReadUnaligned32(p unsafe.Pointer) uint64 { 22 | q := (*[4]byte)(p) 23 | return uint64(uint32(q[3]) | uint32(q[2])<<8 | uint32(q[1])<<16 | uint32(q[0])<<24) 24 | } 25 | 26 | func ReadUnaligned16(p unsafe.Pointer) uint64 { 27 | q := (*[2]byte)(p) 28 | return uint64(uint32(q[1]) | uint32(q[0])<<8) 29 | } 30 | -------------------------------------------------------------------------------- /window/bucket.go: -------------------------------------------------------------------------------- 1 | package window 2 | 3 | import ( 4 | "sync/atomic" 5 | ) 6 | 7 | const ( 8 | // PtrOffSize 指针偏移量大小 9 | PtrOffSize = uint64(8) 10 | ) 11 | 12 | // BucketBuilder Bucket 生成器 13 | type BucketBuilder interface { 14 | // NewEmptyBucket 生成一个空桶 15 | NewEmptyBucket() interface{} 16 | 17 | // Reset 重置桶 18 | Reset(b *Bucket, startTime uint64) *Bucket 19 | } 20 | 21 | // Bucket 滑动窗口的承载的最小元素 22 | type Bucket struct { 23 | // Start 存储了这个桶的起始时间 24 | Start uint64 25 | 26 | // Value 实际挂载对象 27 | Value atomic.Value 28 | } 29 | 30 | // reset 重置 Bucket.Start 属性 31 | func (b *Bucket) reset(start uint64) { 32 | b.Start = start 33 | } 34 | 35 | // isHit 判断 now 是否命中了该桶 36 | func (b *Bucket) isHit(now uint64, bucketSize uint64) bool { 37 | return b.Start <= now && now < b.Start+bucketSize 38 | } 39 | 40 | // calculateStartTime 计算初始桶的时间 41 | func calculateStartTime(now uint64, bucketSize uint64) uint64 { 42 | return now - (now % bucketSize) 43 | } 44 | -------------------------------------------------------------------------------- /internal/hack/hack.go: -------------------------------------------------------------------------------- 1 | package hack 2 | 3 | import ( 4 | "reflect" 5 | "unsafe" 6 | ) 7 | 8 | // StringToBytes converts a string to a byte slice. 9 | // 10 | // This is a shallow copy, means that the returned byte slice reuse 11 | // the underlying array in string, so you can't change the returned 12 | // byte slice in any situations. 13 | func StringToBytes(s string) (b []byte) { 14 | bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) 15 | sh := (*reflect.StringHeader)(unsafe.Pointer(&s)) 16 | bh.Data = sh.Data 17 | bh.Len = sh.Len 18 | bh.Cap = sh.Len 19 | return b 20 | } 21 | 22 | // BytesToString converts a byte slice to a string. 23 | // 24 | // This is a shallow copy, means that the returned string reuse the 25 | // underlying array in byte slice, it's your responsibility to keep 26 | // the input byte slice survive until you don't access the string anymore. 27 | func BytesToString(b []byte) string { 28 | return *(*string)(unsafe.Pointer(&b)) 29 | } 30 | -------------------------------------------------------------------------------- /timeout/ctime.go: -------------------------------------------------------------------------------- 1 | package timeout 2 | 3 | import ( 4 | "context" 5 | "time" 6 | ) 7 | 8 | // ctime GKIT时间模块 9 | // 主要提供context超时控制 10 | 11 | // Shrink 用于链路超时时间以及当前节点的超时时间控制 12 | func Shrink(c context.Context, d time.Duration) (time.Duration, context.Context, context.CancelFunc) { 13 | if deadline, ok := c.Deadline(); ok { 14 | if timeout := time.Until(deadline); timeout < d { 15 | // 链路超时时间已经小于当前节点的超时时间了,所以以上流链路为准,不重新设置 16 | return timeout, c, func() {} 17 | } 18 | } 19 | // 说明没有设置timeout或者deadline 20 | ctx, cancel := context.WithTimeout(c, d) 21 | return d, ctx, cancel 22 | } 23 | 24 | // Compare 用于比较两个context的超时时间,返回超时时间最小的context 25 | func Compare(c1, c2 context.Context) context.Context { 26 | c1Deadline, ok := c1.Deadline() 27 | if !ok { 28 | return c2 29 | } 30 | c2Deadline, ok := c2.Deadline() 31 | if !ok { 32 | return c1 33 | } 34 | if c1Deadline.Before(c2Deadline) { 35 | return c1 36 | } else { 37 | return c2 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /egroup/liftcycle_test.go: -------------------------------------------------------------------------------- 1 | package egroup 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "net/http" 7 | "testing" 8 | 9 | "github.com/songzhibin97/gkit/goroutine" 10 | ) 11 | 12 | var _admin = NewLifeAdmin() 13 | 14 | func TestLifeAdmin_Start(t *testing.T) { 15 | srv := &http.Server{ 16 | Addr: ":8080", 17 | } 18 | _admin.Add(Member{ 19 | Start: func(ctx context.Context) error { 20 | t.Log("http start") 21 | return goroutine.Delegate(ctx, -1, func(ctx context.Context) error { 22 | return srv.ListenAndServe() 23 | }) 24 | }, 25 | Shutdown: func(ctx context.Context) error { 26 | t.Log("http shutdown") 27 | return srv.Shutdown(context.Background()) 28 | }, 29 | }) 30 | //_admin.Add(Member{ 31 | // Start: func(ctx context.Context) error { 32 | // time.Sleep(5 * time.Second) 33 | // t.Log("error") 34 | // return errors.New("error") 35 | // }, 36 | //}) 37 | fmt.Println("error", _admin.Start()) 38 | defer _admin.shutdown() 39 | } 40 | -------------------------------------------------------------------------------- /restrictor/ratelimite/ratelimite.go: -------------------------------------------------------------------------------- 1 | package ratelimite 2 | 3 | import ( 4 | "context" 5 | "errors" 6 | "time" 7 | 8 | "github.com/juju/ratelimit" 9 | "github.com/songzhibin97/gkit/restrictor" 10 | ) 11 | 12 | var ErrTimeOut = errors.New("restrictor/ratelimit: 超时") 13 | 14 | // package ratelimite: https://pkg.go.dev/github.com/juju/ratelimit 实现 limiter 接口 15 | 16 | func NewRateLimit(bucket *ratelimit.Bucket) (restrictor.AllowFunc, restrictor.WaitFunc) { 17 | return func(now time.Time, n int) bool { 18 | return bucket.TakeAvailable(int64(n)) >= int64(n) 19 | }, 20 | func(ctx context.Context, n int) error { 21 | // 获取超时时间 22 | if d, ok := ctx.Deadline(); ok { 23 | if !bucket.WaitMaxDuration(int64(n), time.Until(d)) { 24 | return ErrTimeOut 25 | } 26 | return nil 27 | } 28 | // 表示context没有设置超时时间 29 | if bucket.WaitMaxDuration(int64(n), 100*time.Millisecond) { 30 | return ErrTimeOut 31 | } 32 | return nil 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /structure/skipset/asm.s: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ByteDance Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // The runtime package uses //go:linkname to push a few functions into this 16 | // package but we still need a .s file so the Go tool does not pass -complete 17 | // to the go tool compile so the latter does not complain about Go functions 18 | // with no bodies. 19 | // See https://github.com/golang/go/issues/23311 -------------------------------------------------------------------------------- /structure/skipmap/asm.s: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ByteDance Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | // The runtime package uses //go:linkname to push a few functions into this 16 | // package but we still need a .s file so the Go tool does not pass -complete 17 | // to the go tool compile so the latter does not complain about Go functions 18 | // with no bodies. 19 | // See https://github.com/golang/go/issues/23311 20 | -------------------------------------------------------------------------------- /cache/singleflight/example_test.go: -------------------------------------------------------------------------------- 1 | package singleflight 2 | 3 | // getResources 一般用于去数据库去获取数据 4 | func getResources() (interface{}, error) { 5 | return "test", nil 6 | } 7 | 8 | // cache 填充到 缓存中的数据 9 | func cache(v interface{}) { 10 | return 11 | } 12 | 13 | // ExampleNewSingleFlight 14 | func ExampleNewSingleFlight() { 15 | singleFlight := NewSingleFlight() 16 | 17 | // 如果在key相同的情况下, 同一时间只有一个 func 可以去执行,其他的等待 18 | // 多用于缓存失效后,构造缓存,缓解服务器压力 19 | 20 | // 同步: 21 | v, err, _ := singleFlight.Do("test1", func() (interface{}, error) { 22 | // todo 这里去获取资源 23 | return getResources() 24 | }) 25 | if err != nil { 26 | // todo 处理错误 27 | } 28 | // v 就是获取到的资源 29 | cache(v) 30 | 31 | // 异步: 32 | ch := singleFlight.DoChan("test2", func() (interface{}, error) { 33 | // todo 这里去获取资源 34 | return getResources() 35 | }) 36 | 37 | result := <-ch 38 | if result.Err != nil { 39 | // todo 处理错误 40 | } 41 | cache(result.Val) 42 | 43 | // 尽力取消 44 | singleFlight.Forget("test2") 45 | } 46 | -------------------------------------------------------------------------------- /downgrade/hystrix.go: -------------------------------------------------------------------------------- 1 | package downgrade 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/afex/hystrix-go/hystrix" 7 | ) 8 | 9 | type Hystrix struct{} 10 | 11 | func (h *Hystrix) Do(name string, run RunFunc, fallback FallbackFunc) error { 12 | return hystrix.Do(name, run, fallback) 13 | } 14 | 15 | func (h *Hystrix) DoC(ctx context.Context, name string, run RunFuncC, fallback FallbackFuncC) error { 16 | return hystrix.DoC(ctx, name, run, fallback) 17 | } 18 | 19 | func (h *Hystrix) Go(name string, run RunFunc, fallback FallbackFunc) chan error { 20 | return hystrix.Go(name, run, fallback) 21 | } 22 | 23 | func (h *Hystrix) GoC(ctx context.Context, name string, run RunFuncC, fallback FallbackFuncC) chan error { 24 | return hystrix.GoC(ctx, name, run, fallback) 25 | } 26 | 27 | func (h *Hystrix) ConfigureCommand(name string, config hystrix.CommandConfig) { 28 | hystrix.ConfigureCommand(name, config) 29 | } 30 | 31 | // NewFuse 实例化方法 32 | func NewFuse() Fuse { 33 | return &Hystrix{} 34 | } 35 | -------------------------------------------------------------------------------- /parser/demo/demo.api: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | 4 | type Request struct { 5 | MapField map[string]int 6 | SliceField []int 7 | StringField string 8 | Uint32Field uint32 `gkit:"pType=fixed32;"` 9 | // 注释1 10 | // 注释1.1 11 | 12 | // 注释1.2 13 | InterfaceField interface{} 14 | InterField Inter 15 | EmptyField 16 | } 17 | 18 | 19 | 20 | type Inter interface { 21 | Inter() 22 | } 23 | 24 | type EmptyField interface { 25 | Empty() 26 | } 27 | 28 | type Response struct { 29 | Message string 30 | Username string 31 | Password string 32 | } 33 | 34 | // 注释2 35 | // 注释3 36 | 37 | /* 38 | 注释5 39 | 注释6 40 | */ 41 | 42 | // @service:User 43 | // @method:post 44 | // @router:/register 45 | func Register(req Request)(Response){ 46 | 47 | 48 | 49 | // start 50 | var _ = 1 51 | // end 52 | 53 | } 54 | 55 | 56 | 57 | // @method:get 58 | // @router:/register2 59 | func Register2(req Request)(Response) { 60 | // 注释func 61 | } -------------------------------------------------------------------------------- /log/std.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "log" 8 | "sync" 9 | ) 10 | 11 | var _ Logger = (*stdLogger)(nil) 12 | 13 | type stdLogger struct { 14 | log *log.Logger 15 | pool *sync.Pool 16 | } 17 | 18 | // NewStdLogger new a logger with writer. 19 | func NewStdLogger(w io.Writer) Logger { 20 | return &stdLogger{ 21 | log: log.New(w, "", 0), 22 | pool: &sync.Pool{ 23 | New: func() interface{} { 24 | return new(bytes.Buffer) 25 | }, 26 | }, 27 | } 28 | } 29 | 30 | // Log print the kv pairs log. 31 | func (l *stdLogger) Log(level Lever, kvs ...interface{}) error { 32 | if len(kvs) == 0 { 33 | return nil 34 | } 35 | if len(kvs)%2 != 0 { 36 | kvs = append(kvs, "") 37 | } 38 | buf := l.pool.Get().(*bytes.Buffer) 39 | buf.WriteString(level.String()) 40 | for i := 0; i < len(kvs); i += 2 { 41 | fmt.Fprintf(buf, " %s=%v", kvs[i], kvs[i+1]) 42 | } 43 | l.log.Output(4, buf.String()) 44 | buf.Reset() 45 | l.pool.Put(buf) 46 | return nil 47 | } 48 | -------------------------------------------------------------------------------- /internal/runtimex/readunaligned.go: -------------------------------------------------------------------------------- 1 | //go:build 386 || amd64 || arm || arm64 || ppc64le || mips64le || mipsle || riscv64 || wasm 2 | // +build 386 amd64 arm arm64 ppc64le mips64le mipsle riscv64 wasm 3 | 4 | // 5 | // from golang-go/src/os/endian_big.go 6 | 7 | package runtimex 8 | 9 | import ( 10 | "unsafe" 11 | ) 12 | 13 | func ReadUnaligned64(p unsafe.Pointer) uint64 { 14 | // Equal to runtime.readUnaligned64, but this function can be inlined 15 | // compared to use runtime.readUnaligned64 via go:linkname. 16 | q := (*[8]byte)(p) 17 | return uint64(q[0]) | uint64(q[1])<<8 | uint64(q[2])<<16 | uint64(q[3])<<24 | uint64(q[4])<<32 | uint64(q[5])<<40 | uint64(q[6])<<48 | uint64(q[7])<<56 18 | } 19 | 20 | func ReadUnaligned32(p unsafe.Pointer) uint64 { 21 | q := (*[4]byte)(p) 22 | return uint64(uint32(q[0]) | uint32(q[1])<<8 | uint32(q[2])<<16 | uint32(q[3])<<24) 23 | } 24 | 25 | func ReadUnaligned16(p unsafe.Pointer) uint64 { 26 | q := (*[2]byte)(p) 27 | return uint64(uint32(q[0]) | uint32(q[1])<<8) 28 | } 29 | -------------------------------------------------------------------------------- /parser/parse_go/transform.go: -------------------------------------------------------------------------------- 1 | package parse_go 2 | 3 | var GoToPBMapping = map[string]string{ 4 | "int": "int64", 5 | "float": "double", 6 | "int16": "int32", 7 | "float16": "double", 8 | "float64": "double", 9 | "float32": "float", 10 | "int32": "int32", 11 | "int64": "int64", 12 | "uint32": "uint32", 13 | "uint64": "uint64", 14 | "bool": "bool", 15 | "string": "string", 16 | "[]byte": "bytes", 17 | } 18 | 19 | // GoTypeToPB go type 转化成 pb type 20 | func GoTypeToPB(s string) string { 21 | if v, ok := GoToPBMapping[s]; ok { 22 | return v 23 | } 24 | return s 25 | } 26 | 27 | // IsMappingKey 判断是否是 pb map的key类型 28 | func IsMappingKey(key string) bool { 29 | // Map key cannot be float, double, bytes, message, or enum types 30 | switch key { 31 | case "int32", "int64", "uint32", "uint64", "sint32", "sint64", "fixed32", "fixed64", "sfixed32", "sfixed64", "string": 32 | return true 33 | default: 34 | return false 35 | } 36 | } 37 | 38 | func addOne(a int) int { 39 | return a + 1 40 | } 41 | -------------------------------------------------------------------------------- /internal/sys/mutex/recursive_mutex_token.go: -------------------------------------------------------------------------------- 1 | package mutex 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "sync/atomic" 7 | ) 8 | 9 | // TokenRecursiveMutex Token方式的递归锁 10 | type TokenRecursiveMutex struct { 11 | sync.Mutex 12 | token int64 13 | recursion int32 14 | } 15 | 16 | // Lock 请求锁,需要传入token 17 | func (m *TokenRecursiveMutex) Lock(token int64) { 18 | if atomic.LoadInt64(&m.token) == token { // 如果传入的token和持有锁的token一致,说明是递归调用 19 | m.recursion++ 20 | return 21 | } 22 | m.Mutex.Lock() // 传入的token不一致,说明不是递归调用 23 | // 抢到锁之后记录这个token 24 | atomic.StoreInt64(&m.token, token) 25 | m.recursion = 1 26 | } 27 | 28 | // Unlock 释放锁 29 | func (m *TokenRecursiveMutex) Unlock(token int64) { 30 | if atomic.LoadInt64(&m.token) != token { // 释放其它token持有的锁 31 | panic(fmt.Sprintf("wrong the owner(%d): %d!", m.token, token)) 32 | } 33 | m.recursion-- // 当前持有这个锁的token释放锁 34 | if m.recursion != 0 { // 还没有回退到最初的递归调用 35 | return 36 | } 37 | atomic.StoreInt64(&m.token, 0) // 没有递归调用了,释放锁 38 | m.Mutex.Unlock() 39 | } 40 | -------------------------------------------------------------------------------- /watching/example/slowlyleak/slowlyleak.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | 7 | "github.com/songzhibin97/gkit/watching" 8 | ) 9 | 10 | func init() { 11 | http.HandleFunc("/leak", leak) 12 | go http.ListenAndServe(":10003", nil) 13 | } 14 | 15 | func main() { 16 | w := watching.NewWatching( 17 | watching.WithCollectInterval("2s"), 18 | watching.WithCoolDown("1m"), 19 | watching.WithDumpPath("/tmp"), 20 | watching.WithTextDump(), 21 | watching.WithGoroutineDump(10, 25, 80, 1000), 22 | ) 23 | w.EnableGoroutineDump().Start() 24 | time.Sleep(time.Hour) 25 | } 26 | 27 | func leak(wr http.ResponseWriter, req *http.Request) { 28 | taskChan := make(chan int) 29 | consumer := func() { 30 | for task := range taskChan { 31 | _ = task // do some tasks 32 | } 33 | } 34 | 35 | producer := func() { 36 | for i := 0; i < 10; i++ { 37 | taskChan <- i // generate some tasks 38 | } 39 | // forget to close the taskChan here 40 | } 41 | 42 | go consumer() 43 | go producer() 44 | } 45 | -------------------------------------------------------------------------------- /internal/wyhash/wyhash_test.go: -------------------------------------------------------------------------------- 1 | package wyhash 2 | 3 | import ( 4 | "fmt" 5 | "runtime" 6 | "testing" 7 | ) 8 | 9 | func BenchmarkWyhash(b *testing.B) { 10 | sizes := []int{17, 21, 24, 29, 32, 11 | 33, 64, 69, 96, 97, 128, 129, 240, 241, 12 | 512, 1024, 100 * 1024, 13 | } 14 | 15 | for size := 0; size <= 16; size++ { 16 | b.Run(fmt.Sprintf("%d", size), func(b *testing.B) { 17 | b.SetBytes(int64(size)) 18 | var ( 19 | x uint64 20 | data = string(make([]byte, size)) 21 | ) 22 | b.ReportAllocs() 23 | b.ResetTimer() 24 | 25 | for i := 0; i < b.N; i++ { 26 | x = Sum64String(data) 27 | } 28 | runtime.KeepAlive(x) 29 | }) 30 | } 31 | 32 | for _, size := range sizes { 33 | b.Run(fmt.Sprintf("%d", size), func(b *testing.B) { 34 | b.SetBytes(int64(size)) 35 | var x uint64 36 | data := string(make([]byte, size)) 37 | b.ReportAllocs() 38 | b.ResetTimer() 39 | 40 | for i := 0; i < b.N; i++ { 41 | x = Sum64String(data) 42 | } 43 | runtime.KeepAlive(x) 44 | }) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /overload/bbr/middle.go: -------------------------------------------------------------------------------- 1 | package bbr 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/songzhibin97/gkit/middleware" 7 | "github.com/songzhibin97/gkit/options" 8 | "github.com/songzhibin97/gkit/overload" 9 | ) 10 | 11 | const ( 12 | LimitKey = "LimitKey" 13 | LimitOp = "LimitLoad" 14 | ) 15 | 16 | func NewLimiter(options ...options.Option) middleware.MiddleWare { 17 | g := NewGroup(options...) 18 | return func(next middleware.Endpoint) middleware.Endpoint { 19 | return func(ctx context.Context, i interface{}) (interface{}, error) { 20 | // 通过ctx 获取 g中的限制器 21 | defaultKey := "default" 22 | defaultOp := overload.Success 23 | if v := ctx.Value(LimitKey); v != nil { 24 | defaultKey = v.(string) 25 | } 26 | if v := ctx.Value(LimitOp); v != nil { 27 | defaultOp = v.(overload.Op) 28 | } 29 | limiter := g.Get(defaultKey) 30 | if f, err := limiter.Allow(ctx); err != nil { 31 | return nil, err 32 | } else { 33 | f(overload.DoneInfo{Op: defaultOp}) 34 | return next(ctx, i) 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /distributed/task/error.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | type errNonsupportType struct { 9 | valueType string 10 | } 11 | 12 | func NewErrNonsupportType(valueType string) error { 13 | return &errNonsupportType{valueType: valueType} 14 | } 15 | 16 | func (e *errNonsupportType) Error() string { 17 | return e.valueType + ":不是支持类型" 18 | } 19 | 20 | // ErrRetryTaskLater 重试错误 21 | type ErrRetryTaskLater struct { 22 | msg string 23 | retryIn time.Duration 24 | } 25 | 26 | // RetryIn 返回重试时间,从现在开始到执行的间隔 27 | func (e ErrRetryTaskLater) RetryIn() time.Duration { 28 | return e.retryIn 29 | } 30 | 31 | // Error 实现标准error接口 32 | func (e ErrRetryTaskLater) Error() string { 33 | return fmt.Sprintf("Task error: %s Will retry in: %s", e.msg, e.retryIn) 34 | } 35 | 36 | // NewErrRetryTaskLater 生成重试错误 37 | func NewErrRetryTaskLater(msg string, retryIn time.Duration) ErrRetryTaskLater { 38 | return ErrRetryTaskLater{msg: msg, retryIn: retryIn} 39 | } 40 | 41 | type Retrievable interface { 42 | RetryIn() time.Duration 43 | } 44 | -------------------------------------------------------------------------------- /distributed/option.go: -------------------------------------------------------------------------------- 1 | package distributed 2 | 3 | import "github.com/songzhibin97/gkit/options" 4 | 5 | // SetNoUnixSignals 设置是否优雅关闭 6 | func SetNoUnixSignals(noUnixSignals bool) options.Option { 7 | return func(o interface{}) { 8 | o.(*Config).NoUnixSignals = noUnixSignals 9 | } 10 | } 11 | 12 | // SetResultExpire 设置 backend result 过期时间 13 | func SetResultExpire(expire int64) options.Option { 14 | return func(o interface{}) { 15 | o.(*Config).ResultExpire = expire 16 | } 17 | } 18 | 19 | // SetConcurrency 设置并发量 20 | func SetConcurrency(concurrency int64) options.Option { 21 | return func(o interface{}) { 22 | o.(*Config).Concurrency = concurrency 23 | } 24 | } 25 | 26 | // SetConsumeQueue 设置消费队列 27 | func SetConsumeQueue(consumeQueue string) options.Option { 28 | return func(o interface{}) { 29 | o.(*Config).ConsumeQueue = consumeQueue 30 | } 31 | } 32 | 33 | // SetDelayedQueue 设置延时队列 34 | func SetDelayedQueue(delayedQueue string) options.Option { 35 | return func(o interface{}) { 36 | o.(*Config).DelayedQueue = delayedQueue 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /sys/stringx/is.go: -------------------------------------------------------------------------------- 1 | package stringx 2 | 3 | import ( 4 | "unicode" 5 | ) 6 | 7 | // IsAlpha checks if the string contains only unicode letters. 8 | func IsAlpha(s string) bool { 9 | if s == "" { 10 | return false 11 | } 12 | for _, v := range s { 13 | if !unicode.IsLetter(v) { 14 | return false 15 | } 16 | } 17 | return true 18 | } 19 | 20 | // IsAlphanumeric checks if the string contains only Unicode letters or digits. 21 | func IsAlphanumeric(s string) bool { 22 | if s == "" { 23 | return false 24 | } 25 | for _, v := range s { 26 | if !isAlphanumeric(v) { 27 | return false 28 | } 29 | } 30 | return true 31 | } 32 | 33 | // IsNumeric Checks if the string contains only digits. A decimal point is not a digit and returns false. 34 | func IsNumeric(s string) bool { 35 | if s == "" { 36 | return false 37 | } 38 | for _, v := range s { 39 | if !unicode.IsDigit(v) { 40 | return false 41 | } 42 | } 43 | return true 44 | } 45 | 46 | func isAlphanumeric(v rune) bool { 47 | return unicode.IsDigit(v) || unicode.IsLetter(v) 48 | } 49 | -------------------------------------------------------------------------------- /sys/mutex/recursive_mutex_token.go: -------------------------------------------------------------------------------- 1 | package mutex 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "sync/atomic" 7 | ) 8 | 9 | // TokenRecursiveMutex Token方式的递归锁 10 | type TokenRecursiveMutex struct { 11 | sync.Mutex 12 | token int64 13 | recursion int64 14 | } 15 | 16 | // Lock 请求锁,需要传入token 17 | func (m *TokenRecursiveMutex) Lock(token int64) { 18 | if atomic.LoadInt64(&m.token) == token { // 如果传入的token和持有锁的token一致,说明是递归调用 19 | atomic.AddInt64(&m.recursion, 1) 20 | return 21 | } 22 | m.Mutex.Lock() // 传入的token不一致,说明不是递归调用 23 | // 抢到锁之后记录这个token 24 | atomic.StoreInt64(&m.token, token) 25 | atomic.StoreInt64(&m.recursion, 1) 26 | } 27 | 28 | // Unlock 释放锁 29 | func (m *TokenRecursiveMutex) Unlock(token int64) { 30 | if atomic.LoadInt64(&m.token) != token { // 释放其它token持有的锁 31 | panic(fmt.Sprintf("wrong the owner(%d): %d!", m.token, token)) 32 | } 33 | recursion := atomic.AddInt64(&m.recursion, -1) 34 | if recursion != 0 { // 如果这个goroutine还没有完全释放,则直接返回 35 | return 36 | } 37 | atomic.StoreInt64(&m.token, 0) // 没有递归调用了,释放锁 38 | m.Mutex.Unlock() 39 | } 40 | -------------------------------------------------------------------------------- /distributed/schedule/constantdelay.go: -------------------------------------------------------------------------------- 1 | package schedule 2 | 3 | import "time" 4 | 5 | // ConstantDelaySchedule represents a simple recurring duty cycle, e.g. "Every 5 minutes". 6 | // It does not support jobs more frequent than once a second. 7 | type ConstantDelaySchedule struct { 8 | Delay time.Duration 9 | } 10 | 11 | // Every returns a crontab Schedule that activates once every duration. 12 | // Delays of less than a second are not supported (will round up to 1 second). 13 | // Any fields less than a Second are truncated. 14 | func Every(duration time.Duration) ConstantDelaySchedule { 15 | if duration < time.Second { 16 | duration = time.Second 17 | } 18 | return ConstantDelaySchedule{ 19 | Delay: duration - time.Duration(duration.Nanoseconds())%time.Second, 20 | } 21 | } 22 | 23 | // Next returns the next time this should be run. 24 | // This rounds so that the next activation time will be on the second. 25 | func (schedule ConstantDelaySchedule) Next(t time.Time) time.Time { 26 | return t.Add(schedule.Delay - time.Duration(t.Nanosecond())*time.Nanosecond) 27 | } 28 | -------------------------------------------------------------------------------- /downgrade/downgrade.go: -------------------------------------------------------------------------------- 1 | package downgrade 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/afex/hystrix-go/hystrix" 7 | ) 8 | 9 | // package downgrade: 熔断降级 10 | // 与 "github.com/afex/hystrix-go/hystrix" 使用方法一致,只是做了抽象封装,避免因为升级对服务造成影响" 11 | 12 | type ( 13 | RunFunc = func() error 14 | FallbackFunc = func(error) error 15 | RunFuncC = func(context.Context) error 16 | FallbackFuncC = func(context.Context, error) error 17 | ) 18 | 19 | // Fuse 熔断降级接口 20 | type Fuse interface { 21 | // Do 以同步的方式运行 RunFunc,直到成功为止 22 | // 如果返回错误,执行 FallbackFunc 函数 23 | Do(name string, run RunFunc, fallback FallbackFunc) error 24 | 25 | // DoC 同步方式处理 26 | DoC(ctx context.Context, name string, run RunFuncC, fallback FallbackFuncC) error 27 | 28 | // Go 异步调用返回 channel 29 | Go(name string, run RunFunc, fallback FallbackFunc) chan error 30 | 31 | // GoC 32 | // Do/Go 都调用GoC, Do中处理了异步过程 33 | GoC(ctx context.Context, name string, run RunFuncC, fallback FallbackFuncC) chan error 34 | 35 | // ConfigureCommand 配置参数 36 | ConfigureCommand(name string, config hystrix.CommandConfig) 37 | } 38 | -------------------------------------------------------------------------------- /concurrent/fan_out_test.go: -------------------------------------------------------------------------------- 1 | package concurrent 2 | 3 | import ( 4 | "sort" 5 | "sync" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | ) 10 | 11 | func production() <-chan interface{} { 12 | out := make(chan interface{}) 13 | go func() { 14 | defer close(out) 15 | for i := 0; i < 10; i++ { 16 | out <- i 17 | } 18 | }() 19 | return out 20 | } 21 | 22 | func consumer(ch <-chan interface{}) (ret []interface{}) { 23 | for v := range ch { 24 | ret = append(ret, v) 25 | } 26 | sort.Slice(ret, func(i, j int) bool { 27 | return ret[i].(int) < ret[j].(int) 28 | }) 29 | return ret 30 | } 31 | 32 | func TestFanOut(t *testing.T) { 33 | var outList []chan interface{} 34 | for i := 0; i < 10; i++ { 35 | outList = append(outList, make(chan interface{})) 36 | } 37 | FanOut(production(), outList, true) 38 | wg := sync.WaitGroup{} 39 | for _, c := range outList { 40 | wg.Add(1) 41 | go func(c chan interface{}) { 42 | defer wg.Done() 43 | assert.Equal(t, consumer(c), []interface{}{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}) 44 | }(c) 45 | } 46 | wg.Wait() 47 | } 48 | -------------------------------------------------------------------------------- /gctuner/finalizer.go: -------------------------------------------------------------------------------- 1 | package gctuner 2 | 3 | import ( 4 | "runtime" 5 | "sync/atomic" 6 | ) 7 | 8 | type finalizerCallback func() 9 | 10 | type finalizer struct { 11 | ref *finalizerRef 12 | callback finalizerCallback 13 | stopped int32 14 | } 15 | 16 | func (f *finalizer) stop() { 17 | atomic.StoreInt32(&f.stopped, 1) 18 | } 19 | 20 | type finalizerRef struct { 21 | parent *finalizer 22 | } 23 | 24 | func finalizerHandler(f *finalizerRef) { 25 | // stop calling callback 26 | 27 | if atomic.LoadInt32(&f.parent.stopped) == 1 { 28 | return 29 | } 30 | f.parent.callback() 31 | runtime.SetFinalizer(f, finalizerHandler) 32 | } 33 | 34 | // newFinalizer return a finalizer object and caller should save it to make sure it will not be gc. 35 | // the go runtime promise the callback function should be called every gc time. 36 | func newFinalizer(callback finalizerCallback) *finalizer { 37 | f := &finalizer{ 38 | callback: callback, 39 | } 40 | f.ref = &finalizerRef{parent: f} 41 | runtime.SetFinalizer(f.ref, finalizerHandler) 42 | f.ref = nil // trigger gc 43 | return f 44 | } 45 | -------------------------------------------------------------------------------- /goroutine/goroutine_test.go: -------------------------------------------------------------------------------- 1 | package goroutine 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "testing" 7 | "time" 8 | 9 | "github.com/songzhibin97/gkit/log" 10 | ) 11 | 12 | func TestNewGoroutine(t *testing.T) { 13 | g := NewGoroutine(context.Background(), SetMax(10), SetLogger(log.DefaultLogger)) 14 | for i := 0; i < 20; i++ { 15 | i := i 16 | fmt.Println(g.AddTask(func() { 17 | //if rand.Int31n(10) > 5 { 18 | // panic(i) 19 | //} 20 | fmt.Println("start:", i) 21 | time.Sleep(5 * time.Second) 22 | fmt.Println("end:", i) 23 | })) 24 | g.Trick() 25 | if i == 7 { 26 | g.ChangeMax(5) 27 | } 28 | } 29 | _ = g.Shutdown() 30 | } 31 | 32 | func TestSetIdle(t *testing.T) { 33 | g := NewGoroutine(context.Background(), SetMax(1000), SetIdle(10), SetCheckTime(time.Second), SetLogger(log.DefaultLogger)) 34 | for i := 0; i < 10000; i++ { 35 | g.AddTask(func() { 36 | func(i int) { 37 | time.Sleep(time.Second) 38 | // t.Log("close", i) 39 | }(i) 40 | }) 41 | } 42 | for i := 0; i < 20; i++ { 43 | g.Trick() 44 | time.Sleep(time.Second) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /sys/syncx/rwmutex.go: -------------------------------------------------------------------------------- 1 | package syncx 2 | 3 | import ( 4 | "github.com/songzhibin97/gkit/internal/runtimex" 5 | "runtime" 6 | "sync" 7 | "unsafe" 8 | 9 | "golang.org/x/sys/cpu" 10 | ) 11 | 12 | const ( 13 | cacheLineSize = unsafe.Sizeof(cpu.CacheLinePad{}) 14 | ) 15 | 16 | var ( 17 | shardsLen int 18 | ) 19 | 20 | // RWMutex is a p-shard mutex, which has better performance when there's much more read than write. 21 | type RWMutex []rwMutexShard 22 | 23 | type rwMutexShard struct { 24 | sync.RWMutex 25 | _pad [cacheLineSize - unsafe.Sizeof(sync.RWMutex{})]byte 26 | } 27 | 28 | func init() { 29 | shardsLen = runtime.GOMAXPROCS(0) 30 | } 31 | 32 | // NewRWMutex creates a new RWMutex. 33 | func NewRWMutex() RWMutex { 34 | return make([]rwMutexShard, shardsLen) 35 | } 36 | 37 | func (m RWMutex) Lock() { 38 | for shard := range m { 39 | m[shard].Lock() 40 | } 41 | } 42 | 43 | func (m RWMutex) Unlock() { 44 | for shard := range m { 45 | m[shard].Unlock() 46 | } 47 | } 48 | 49 | func (m RWMutex) RLocker() sync.Locker { 50 | return m[runtimex.Pid()%shardsLen].RWMutex.RLocker() 51 | } 52 | -------------------------------------------------------------------------------- /internal/sys/safe_map/map.go: -------------------------------------------------------------------------------- 1 | package safe_map 2 | 3 | import "sync" 4 | 5 | type RWMap struct { // 一个读写锁保护的线程安全的map 6 | sync.RWMutex // 读写锁保护下面的map字段 7 | m map[int]int 8 | } 9 | 10 | // NewRWMap 新建一个RWMap 11 | func NewRWMap(n int) *RWMap { 12 | return &RWMap{ 13 | m: make(map[int]int, n), 14 | } 15 | } 16 | 17 | // Get 从map中读取一个值 18 | func (m *RWMap) Get(k int) (int, bool) { 19 | m.RLock() 20 | defer m.RUnlock() 21 | v, existed := m.m[k] // 在锁的保护下从map中读取 22 | return v, existed 23 | } 24 | 25 | // Set 设置一个键值对 26 | func (m *RWMap) Set(k int, v int) { 27 | m.Lock() // 锁保护 28 | defer m.Unlock() 29 | m.m[k] = v 30 | } 31 | 32 | // Delete 删除一个键 33 | func (m *RWMap) Delete(k int) { 34 | m.Lock() // 锁保护 35 | defer m.Unlock() 36 | delete(m.m, k) 37 | } 38 | 39 | // Len map的长度 40 | func (m *RWMap) Len() int { 41 | m.RLock() // 锁保护 42 | defer m.RUnlock() 43 | return len(m.m) 44 | } 45 | 46 | // Each 遍历map 47 | func (m *RWMap) Each(f func(k, v int) bool) { 48 | m.RLock() // 遍历期间一直持有读锁 49 | defer m.RUnlock() 50 | 51 | for k, v := range m.m { 52 | if !f(k, v) { 53 | return 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /goroutine/example_test.go: -------------------------------------------------------------------------------- 1 | package goroutine 2 | 3 | import ( 4 | "context" 5 | "time" 6 | 7 | "github.com/songzhibin97/gkit/log" 8 | ) 9 | 10 | var gGroup GGroup 11 | 12 | func mockFunc() func() { 13 | return func() { 14 | } 15 | } 16 | 17 | func ExampleNewGoroutine() { 18 | // 默认配置 19 | // gGroup = NewGoroutine(context.TODO()) 20 | 21 | // 可供选择配置选项 22 | 23 | // 设置停止超时时间 24 | // SetStopTimeout(time.Second) 25 | 26 | // 设置日志对象 27 | // SetLogger(&testLogger{}) 28 | 29 | // 设置pool最大容量 30 | // SetMax(100) 31 | 32 | gGroup = NewGoroutine(context.TODO(), 33 | SetStopTimeout(time.Second), 34 | SetLogger(log.DefaultLogger), 35 | SetMax(100), 36 | ) 37 | } 38 | 39 | func ExampleGoroutine_AddTask() { 40 | if !gGroup.AddTask(mockFunc()) { 41 | // 添加任务失败 42 | } 43 | } 44 | 45 | func ExampleGoroutine_AddTaskN() { 46 | // 带有超时控制添加任务 47 | if !gGroup.AddTaskN(context.TODO(), mockFunc()) { 48 | // 添加任务失败 49 | } 50 | } 51 | 52 | func ExampleGoroutine_ChangeMax() { 53 | // 修改 pool最大容量 54 | gGroup.ChangeMax(1000) 55 | } 56 | 57 | func ExampleGoroutine_Shutdown() { 58 | // 回收资源 59 | _ = gGroup.Shutdown() 60 | } 61 | -------------------------------------------------------------------------------- /parser/parse_go/model_test.go: -------------------------------------------------------------------------------- 1 | package parse_go 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestGoParsePB_GeneratePB(t *testing.T) { 8 | rr, err := ParseGo( 9 | "/Users/songzhibin/go/src/Songzhibin/gkit/parse/demo/demo.api", AddParseFunc(parseDoc), AddParseStruct(parseTag)) 10 | if err != nil { 11 | panic(err) 12 | } 13 | r := rr.(*GoParsePB) 14 | for _, note := range r.Note { 15 | t.Log(note.Text, note.Pos(), note.End()) 16 | } 17 | t.Log(r.Generate()) 18 | } 19 | 20 | func TestGoParsePB_PileDriving(t *testing.T) { 21 | rr, err := ParseGo("/Users/songzhibin/go/src/Songzhibin/gkit/parse/demo/demo.api") 22 | if err != nil { 23 | panic(err) 24 | } 25 | r := rr.(*GoParsePB) 26 | t.Log(r.PileDismantle("var _ = 1")) 27 | } 28 | 29 | func Test_checkRepeat(t *testing.T) { 30 | test := `type Demo struct { 31 | MapField map[string]int 32 | SliceField []int 33 | StringField string 34 | Uint32Field uint32 35 | // 注释1 36 | // 注释1.1 37 | 38 | // 注释1.2 39 | InterfaceField interface{} 40 | InterField Inter 41 | EmptyField 42 | }` 43 | t.Log(checkRepeat("// 注释1", test)) 44 | } 45 | -------------------------------------------------------------------------------- /restrictor/limiter.go: -------------------------------------------------------------------------------- 1 | package restrictor 2 | 3 | import ( 4 | "context" 5 | "time" 6 | ) 7 | 8 | type Allow interface { 9 | // Allow AllowN(time.Now(),1) 10 | Allow() bool 11 | // AllowN 截止到某一时刻,目前桶中数目是否至少为 n 个,满足则返回 true,同时从桶中消费 n 个 token 12 | AllowN(now time.Time, n int) bool 13 | } 14 | 15 | type Wait interface { 16 | // Wait WaitN(ctx,1) 17 | Wait(ctx context.Context) error 18 | // WaitN 如果此时桶内 Token 数组不足 (小于 N),那么 Wait 方法将会阻塞一段时间,直至 Token 满足条件。如果充足则直接返回 19 | // 我们可以设置 context 的 Deadline 或者 Timeout,来决定此次 Wait 的最长时间。 20 | WaitN(ctx context.Context, n int) error 21 | } 22 | 23 | // AllowFunc 实现 Allow 接口 24 | type AllowFunc func(now time.Time, n int) bool 25 | 26 | func (a AllowFunc) Allow() bool { 27 | return a.AllowN(time.Now(), 1) 28 | } 29 | 30 | func (a AllowFunc) AllowN(now time.Time, n int) bool { 31 | return a(now, n) 32 | } 33 | 34 | // WaitFunc 实现 Wait 接口 35 | type WaitFunc func(ctx context.Context, n int) error 36 | 37 | func (w WaitFunc) Wait(ctx context.Context) error { 38 | return w.WaitN(ctx, 1) 39 | } 40 | 41 | func (w WaitFunc) WaitN(ctx context.Context, n int) error { 42 | return w(ctx, n) 43 | } 44 | -------------------------------------------------------------------------------- /internal/sys/mutex/recursive_mutex.go: -------------------------------------------------------------------------------- 1 | package mutex 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "sync/atomic" 7 | 8 | "github.com/songzhibin97/gkit/internal/sys/goid" 9 | ) 10 | 11 | // RecursiveMutex 包装一个Mutex,实现可重入 12 | type RecursiveMutex struct { 13 | sync.Mutex 14 | owner int64 // 当前持有锁的goroutine id 15 | recursion int32 // 这个goroutine 重入的次数 16 | } 17 | 18 | func (m *RecursiveMutex) Lock() { 19 | gid := goid.GetGID() 20 | // 如果当前持有锁的goroutine就是这次调用的goroutine,说明是重入 21 | if atomic.LoadInt64(&m.owner) == gid { 22 | m.recursion++ 23 | return 24 | } 25 | m.Mutex.Lock() 26 | // 获得锁的goroutine第一次调用,记录下它的goroutine id,调用次数加1 27 | atomic.StoreInt64(&m.owner, gid) 28 | m.recursion = 1 29 | } 30 | 31 | func (m *RecursiveMutex) Unlock() { 32 | gid := goid.GetGID() 33 | // 非持有锁的goroutine尝试释放锁,错误的使用 34 | if atomic.LoadInt64(&m.owner) != gid { 35 | panic(fmt.Sprintf("wrong the owner(%d): %d!", m.owner, gid)) 36 | } 37 | // 调用次数减1 38 | m.recursion-- 39 | if m.recursion != 0 { // 如果这个goroutine还没有完全释放,则直接返回 40 | return 41 | } 42 | // 此goroutine最后一次调用,需要释放锁 43 | atomic.StoreInt64(&m.owner, -1) 44 | m.Mutex.Unlock() 45 | } 46 | -------------------------------------------------------------------------------- /coding/coding.go: -------------------------------------------------------------------------------- 1 | package coding 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | "sync" 7 | ) 8 | 9 | // package encoding 各种格式编码解码 10 | 11 | var ErrorTypeCode = errors.New("coding: code type error") 12 | 13 | var registerCode = CodeStorage{storage: map[string]Code{}} 14 | 15 | type ( 16 | // Code coding 接口 17 | Code interface { 18 | // Marshal 将v序列化为[]byte 19 | Marshal(v interface{}) ([]byte, error) 20 | 21 | // Unmarshal 将[]byte 反序列化为v 22 | Unmarshal(data []byte, v interface{}) error 23 | 24 | // Name 返回实际调用编码器的类型, 例如 json、xml、yaml、proto 25 | Name() string 26 | } 27 | 28 | // CodeStorage 注册中心 29 | CodeStorage struct { 30 | storage map[string]Code 31 | sync.Mutex 32 | } 33 | ) 34 | 35 | func RegisterCode(code Code) error { 36 | if code == nil || len(code.Name()) == 0 { 37 | return ErrorTypeCode 38 | } 39 | registerCode.Lock() 40 | defer registerCode.Unlock() 41 | registerCode.storage[strings.ToLower(code.Name())] = code 42 | return nil 43 | } 44 | 45 | func GetCode(codeName string) Code { 46 | registerCode.Lock() 47 | defer registerCode.Unlock() 48 | return registerCode.storage[strings.ToLower(codeName)] 49 | } 50 | -------------------------------------------------------------------------------- /parser/parse_pb/parse_pb.go: -------------------------------------------------------------------------------- 1 | package parse_pb 2 | 3 | import ( 4 | "bytes" 5 | "io/ioutil" 6 | 7 | "github.com/emicklei/proto" 8 | "github.com/songzhibin97/gkit/options" 9 | "github.com/songzhibin97/gkit/parser" 10 | ) 11 | 12 | func ParsePb(filepath string, options ...options.Option) (parser.Parser, error) { 13 | reader, err := ioutil.ReadFile(filepath) 14 | if err != nil { 15 | return nil, err 16 | } 17 | parser := proto.NewParser(bytes.NewReader(reader)) 18 | definition, err := parser.Parse() 19 | if err != nil { 20 | return nil, err 21 | } 22 | 23 | ret := CreatePbParseGo() 24 | for _, v := range options { 25 | v(ret) 26 | } 27 | ret.FilePath = filepath 28 | for _, element := range definition.Elements { 29 | switch v := element.(type) { 30 | case *proto.Package: 31 | ret.PkgName = v.Name 32 | case *proto.Comment: 33 | // note 34 | ret.AddNode(&Note{Comment: v}) 35 | case *proto.Message: 36 | // message 37 | ret.parseMessage(v, "") 38 | case *proto.Service: 39 | // service 40 | ret.parseService(v) 41 | case *proto.Enum: 42 | ret.parseEnum(v, "") 43 | } 44 | } 45 | return ret, nil 46 | } 47 | -------------------------------------------------------------------------------- /window/init_test.go: -------------------------------------------------------------------------------- 1 | package window 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | "time" 7 | ) 8 | 9 | func TestWindow(t *testing.T) { 10 | w := NewWindow() 11 | slice := []Index{ 12 | {Name: "1", Score: 1}, 13 | {Name: "2", Score: 2}, 14 | {Name: "2", Score: 2}, 15 | {Name: "3", Score: 3}, 16 | {Name: "2", Score: 2}, 17 | {Name: "3", Score: 3}, 18 | {Name: "4", Score: 4}, 19 | {Name: "3", Score: 3}, 20 | {Name: "5", Score: 5}, 21 | {Name: "2", Score: 2}, 22 | {Name: "6", Score: 6}, 23 | {Name: "5", Score: 5}, 24 | } 25 | /* 26 | [{1 1} {2 2}] 27 | [{2 4} {3 3} {1 1}] 28 | [{1 1} {2 6} {3 6}] 29 | [{3 9} {4 4} {1 1} {2 6}] 30 | [{1 1} {2 8} {3 9} {4 4} {5 5}] 31 | [{5 10} {3 9} {2 6} {4 4} {6 6}] 32 | */ 33 | for i := 0; i < len(slice); i += 2 { 34 | w.AddIndex(slice[i].Name, slice[i].Score) 35 | w.AddIndex(slice[i+1].Name, slice[i+1].Score) 36 | time.Sleep(time.Second) 37 | t.Log(w.Show()) 38 | } 39 | } 40 | 41 | func BenchmarkWindow(b *testing.B) { 42 | w := NewWindow() 43 | for i := 0; i < b.N; i++ { 44 | w.AddIndex(strconv.Itoa(i), uint(i)) 45 | w.Show() 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /sys/syncx/pool_race.go: -------------------------------------------------------------------------------- 1 | //go:build race 2 | // +build race 3 | 4 | package syncx 5 | 6 | import ( 7 | "sync" 8 | ) 9 | 10 | type Pool struct { 11 | p sync.Pool 12 | once sync.Once 13 | // New optionally specifies a function to generate 14 | // a value when Get would otherwise return nil. 15 | // It may not be changed concurrently with calls to Get. 16 | New func() interface{} 17 | // NoGC any objects in this Pool. 18 | NoGC bool 19 | } 20 | 21 | func (p *Pool) init() { 22 | p.once.Do(func() { 23 | p.p = sync.Pool{ 24 | New: p.New, 25 | } 26 | }) 27 | } 28 | 29 | // Put adds x to the pool. 30 | func (p *Pool) Put(x interface{}) { 31 | p.init() 32 | p.p.Put(x) 33 | } 34 | 35 | // Get selects an arbitrary item from the Pool, removes it from the 36 | // Pool, and returns it to the caller. 37 | // Get may choose to ignore the pool and treat it as empty. 38 | // Callers should not assume any relation between values passed to Put and 39 | // the values returned by Get. 40 | // 41 | // If Get would otherwise return nil and p.New is non-nil, Get returns 42 | // the result of calling p.New. 43 | func (p *Pool) Get() (x interface{}) { 44 | p.init() 45 | return p.p.Get() 46 | } 47 | -------------------------------------------------------------------------------- /sys/mutex/recursive_mutex.go: -------------------------------------------------------------------------------- 1 | package mutex 2 | 3 | import ( 4 | "fmt" 5 | "sync" 6 | "sync/atomic" 7 | 8 | "github.com/songzhibin97/gkit/internal/sys/goid" 9 | ) 10 | 11 | // RecursiveMutex 包装一个Mutex,实现可重入 12 | type RecursiveMutex struct { 13 | sync.Mutex 14 | owner int64 // 当前持有锁的goroutine id 15 | recursion int64 // 这个goroutine 重入的次数 16 | } 17 | 18 | func (m *RecursiveMutex) Lock() { 19 | gid := goid.GetGID() 20 | // 如果当前持有锁的goroutine就是这次调用的goroutine,说明是重入 21 | if atomic.LoadInt64(&m.owner) == gid { 22 | atomic.AddInt64(&m.recursion, 1) 23 | return 24 | } 25 | m.Mutex.Lock() 26 | // 获得锁的goroutine第一次调用,记录下它的goroutine id,调用次数加1 27 | atomic.StoreInt64(&m.owner, gid) 28 | atomic.StoreInt64(&m.recursion, 1) 29 | } 30 | 31 | func (m *RecursiveMutex) Unlock() { 32 | gid := goid.GetGID() 33 | // 非持有锁的goroutine尝试释放锁,错误的使用 34 | if atomic.LoadInt64(&m.owner) != gid { 35 | panic(fmt.Sprintf("wrong the owner(%d): %d!", m.owner, gid)) 36 | } 37 | // 调用次数减1 38 | recursion := atomic.AddInt64(&m.recursion, -1) 39 | if recursion != 0 { // 如果这个goroutine还没有完全释放,则直接返回 40 | return 41 | } 42 | // 此goroutine最后一次调用,需要释放锁 43 | atomic.StoreInt64(&m.owner, -1) 44 | m.Mutex.Unlock() 45 | } 46 | -------------------------------------------------------------------------------- /trace/stats_handler.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "context" 5 | 6 | "go.opentelemetry.io/otel/trace" 7 | "google.golang.org/grpc/peer" 8 | "google.golang.org/grpc/stats" 9 | ) 10 | 11 | // ClientHandler 客户端追踪 12 | type ClientHandler struct{} 13 | 14 | // HandleConn exists to satisfy gRPC stats.Handler. 15 | func (c *ClientHandler) HandleConn(ctx context.Context, cs stats.ConnStats) {} 16 | 17 | // TagConn exists to satisfy gRPC stats.Handler. 18 | func (c *ClientHandler) TagConn(ctx context.Context, cti *stats.ConnTagInfo) context.Context { 19 | return ctx 20 | } 21 | 22 | // HandleRPC implements per-RPC tracing and stats instrumentation. 23 | func (c *ClientHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) { 24 | if _, ok := rs.(*stats.OutHeader); ok { 25 | if p, ok := peer.FromContext(ctx); ok { 26 | remoteAddr := p.Addr.String() 27 | if span := trace.SpanFromContext(ctx); span.SpanContext().IsValid() { 28 | span.SetAttributes(peerAttr(remoteAddr)...) 29 | } 30 | } 31 | } 32 | } 33 | 34 | // TagRPC implements per-RPC context management. 35 | func (c *ClientHandler) TagRPC(ctx context.Context, rti *stats.RPCTagInfo) context.Context { 36 | return ctx 37 | } 38 | -------------------------------------------------------------------------------- /distributed/task/workflow_test.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestNewChain(t *testing.T) { 10 | t.Parallel() 11 | 12 | task1 := Signature{ 13 | Name: "foo", 14 | Args: []Arg{ 15 | { 16 | Type: "float64", 17 | Value: interface{}(1), 18 | }, 19 | { 20 | Type: "float64", 21 | Value: interface{}(1), 22 | }, 23 | }, 24 | } 25 | 26 | task2 := Signature{ 27 | Name: "bar", 28 | Args: []Arg{ 29 | { 30 | Type: "float64", 31 | Value: interface{}(5), 32 | }, 33 | { 34 | Type: "float64", 35 | Value: interface{}(6), 36 | }, 37 | }, 38 | } 39 | 40 | task3 := Signature{ 41 | Name: "qux", 42 | Args: []Arg{ 43 | { 44 | Type: "float64", 45 | Value: interface{}(4), 46 | }, 47 | }, 48 | } 49 | 50 | chain, err := NewChain("", &task1, &task2, &task3) 51 | if err != nil { 52 | t.Fatal(err) 53 | } 54 | 55 | firstTask := chain.Tasks[0] 56 | 57 | assert.Equal(t, "foo", firstTask.Name) 58 | assert.Equal(t, "bar", firstTask.CallbackOnSuccess[0].Name) 59 | assert.Equal(t, "qux", firstTask.CallbackOnSuccess[0].CallbackOnSuccess[0].Name) 60 | } 61 | -------------------------------------------------------------------------------- /cache/mbuffer/utils_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ByteDance Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package mbuffer 16 | 17 | import ( 18 | "testing" 19 | 20 | "github.com/stretchr/testify/assert" 21 | ) 22 | 23 | func TestBsr(t *testing.T) { 24 | assert.Equal(t, bsr(4), 2) 25 | assert.Equal(t, bsr(24), 4) 26 | assert.Equal(t, bsr((1<<10)-1), 9) 27 | assert.Equal(t, bsr((1<<30)+(1<<19)+(1<<16)+(1<<1)), 30) 28 | } 29 | 30 | func BenchmarkBsr(b *testing.B) { 31 | num := (1 << 30) + (1 << 19) + (1 << 16) + (1 << 1) 32 | 33 | b.ReportAllocs() 34 | b.ResetTimer() 35 | for i := 0; i < b.N; i++ { 36 | _ = bsr(num + i) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /concurrent/fan_in.go: -------------------------------------------------------------------------------- 1 | package concurrent 2 | 3 | import "reflect" 4 | 5 | // FanInRec 扇入模式 6 | func FanInRec(channels ...<-chan interface{}) <-chan interface{} { 7 | out := make(chan interface{}, 1) 8 | go func() { 9 | defer close(out) 10 | var cases []reflect.SelectCase 11 | for _, channel := range channels { 12 | cases = append(cases, reflect.SelectCase{ 13 | Dir: reflect.SelectRecv, 14 | Chan: reflect.ValueOf(channel), 15 | }) 16 | } 17 | for len(cases) > 0 { 18 | i, v, ok := reflect.Select(cases) 19 | if !ok { 20 | // 监控的channel已经关闭 21 | cases = append(cases[:i], cases[i+1:]...) 22 | continue 23 | } 24 | out <- v.Interface() 25 | } 26 | }() 27 | return out 28 | } 29 | 30 | // MergeChannel 合并两个channel 31 | func MergeChannel(a, b <-chan interface{}) <-chan interface{} { 32 | c := make(chan interface{}) 33 | go func() { 34 | defer close(c) 35 | for a != nil || b != nil { 36 | select { 37 | case v, ok := <-a: 38 | if !ok { 39 | a = nil 40 | continue 41 | } 42 | c <- v 43 | case v, ok := <-b: 44 | if !ok { 45 | b = nil 46 | continue 47 | } 48 | c <- v 49 | } 50 | } 51 | }() 52 | return c 53 | } 54 | -------------------------------------------------------------------------------- /cache/buffer/iobuffer_pool.go: -------------------------------------------------------------------------------- 1 | package buffer 2 | 3 | import "sync" 4 | 5 | // localIOPool 6 | var localIOPool IoPool 7 | 8 | // IoPool 存储 IoBuffer Pool 9 | type IoPool struct { 10 | // IoBuffer 11 | pool sync.Pool 12 | } 13 | 14 | // get 从pool中 获取一个 IoBuffer 15 | func (i *IoPool) get(size int) IoBuffer { 16 | v := i.pool.Get() 17 | if v == nil { 18 | return newIoBuffer(size) 19 | } else { 20 | buf := v.(IoBuffer) 21 | buf.Alloc(size) 22 | buf.Count(1) 23 | return buf 24 | } 25 | } 26 | 27 | // put 向pool中回填一个 IoBuffer 28 | func (i *IoPool) put(buf IoBuffer) { 29 | buf.Free() 30 | i.pool.Put(buf) 31 | } 32 | 33 | // GetIoPool 从pool中 获取一个 IoBuffer 34 | func GetIoPool(size int) IoBuffer { 35 | return localIOPool.get(size) 36 | } 37 | 38 | // PutIoPool 向pool中回填一个 IoBuffer 39 | func PutIoPool(buf IoBuffer) error { 40 | count := buf.Count(-1) 41 | if count > 0 { 42 | // 还有其他引用 43 | return nil 44 | } else if count < 0 { 45 | return ErrDuplicate 46 | } 47 | if p, _ := buf.(*pipe); p != nil { 48 | buf = p.IoBuffer 49 | } 50 | localIOPool.put(buf) 51 | return nil 52 | } 53 | 54 | // NewIoBuffer GetIoPool 别名 55 | func NewIoBuffer(size int) IoBuffer { 56 | return GetIoPool(size) 57 | } 58 | -------------------------------------------------------------------------------- /container/group/group.go: -------------------------------------------------------------------------------- 1 | package group 2 | 3 | import "sync" 4 | 5 | // package group: 提供懒加载容器 6 | 7 | // Group 懒加载容器 8 | type Group struct { 9 | sync.RWMutex 10 | f func() interface{} 11 | objs map[string]interface{} 12 | } 13 | 14 | // Get 根据key 获取 value 15 | func (g *Group) Get(key string) interface{} { 16 | g.RLock() 17 | if obj, ok := g.objs[key]; ok { 18 | g.RUnlock() 19 | return obj 20 | } 21 | g.RUnlock() 22 | 23 | g.Lock() 24 | defer g.Unlock() 25 | // 再次判断 26 | if obj, ok := g.objs[key]; ok { 27 | return obj 28 | } 29 | 30 | obj := g.f() 31 | g.objs[key] = obj 32 | return obj 33 | } 34 | 35 | // ReSet 更换实例化函数 36 | func (g *Group) ReSet(nf func() interface{}) { 37 | if nf == nil { 38 | panic("container.group: 不能为新函数分配nil") 39 | } 40 | g.Lock() 41 | g.f = nf 42 | g.Unlock() 43 | g.Clear() 44 | } 45 | 46 | func (g *Group) Clear() { 47 | g.Lock() 48 | defer g.Unlock() 49 | g.objs = make(map[string]interface{}) 50 | } 51 | 52 | // NewGroup Group 实例化方法 53 | func NewGroup(f func() interface{}) LazyLoadGroup { 54 | if f == nil { 55 | panic("container.group: 不能为新函数分配nil") 56 | } 57 | return &Group{ 58 | f: f, 59 | objs: make(map[string]interface{}), 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /trace/metadata.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/songzhibin97/gkit/internal/metadata" 7 | "go.opentelemetry.io/otel/propagation" 8 | ) 9 | 10 | const serverMark = "x-meta-service-name" 11 | 12 | type Metadata struct { 13 | Name string 14 | } 15 | 16 | var _ propagation.TextMapPropagator = Metadata{} 17 | 18 | // Inject set cross-cutting concerns from the Context into the carrier. 19 | func (m Metadata) Inject(ctx context.Context, carrier propagation.TextMapCarrier) { 20 | carrier.Set(serverMark, m.Name) 21 | } 22 | 23 | // Extract reads cross-cutting concerns from the carrier into a Context. 24 | func (m Metadata) Extract(ctx context.Context, carrier propagation.TextMapCarrier) context.Context { 25 | name := carrier.Get(serverMark) 26 | if name != "" { 27 | if md, ok := metadata.FromServerContext(ctx); ok { 28 | md.Set(serverMark, name) 29 | } else { 30 | // 设置新的metadata 31 | md := metadata.NewMetadata() 32 | md.Set(serverMark, name) 33 | ctx = metadata.NewServerContext(ctx, md) 34 | } 35 | } 36 | return ctx 37 | } 38 | 39 | // Fields returns the keys who's values are set with Inject. 40 | func (m Metadata) Fields() []string { 41 | return []string{serverMark} 42 | } 43 | -------------------------------------------------------------------------------- /net/tcp/conn_func.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import "time" 4 | 5 | // Send 拨号并发送消息,关闭链接 6 | func Send(addr string, data []byte, retry *Retry) error { 7 | c, err := NewConn(addr, nil) 8 | if err != nil { 9 | return err 10 | } 11 | defer c.Close() 12 | return c.Send(data, retry) 13 | } 14 | 15 | // SendRecv 拨号发送并读取响应 16 | func SendRecv(addr string, data []byte, length int, retry *Retry) ([]byte, error) { 17 | conn, err := NewConn(addr, nil) 18 | if err != nil { 19 | return nil, err 20 | } 21 | defer conn.Close() 22 | return conn.SendRecv(data, length, retry) 23 | } 24 | 25 | // SendWithTimeout 创建并发送具有写入超时限制的连接 26 | func SendWithTimeout(addr string, data []byte, timeout time.Duration, retry *Retry) error { 27 | conn, err := NewConn(addr, nil) 28 | if err != nil { 29 | return err 30 | } 31 | defer conn.Close() 32 | return conn.SendWithTimeout(data, timeout, retry) 33 | } 34 | 35 | // SendRecvWithTimeout 创建链接发送读取有超时限制的连接 36 | func SendRecvWithTimeout(addr string, data []byte, receive int, timeout time.Duration, retry *Retry) ([]byte, error) { 37 | conn, err := NewConn(addr, nil) 38 | if err != nil { 39 | return nil, err 40 | } 41 | defer conn.Close() 42 | return conn.SendRecvWithTimeout(data, timeout, receive, retry) 43 | } 44 | -------------------------------------------------------------------------------- /middleware/middleware_test.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "testing" 7 | ) 8 | 9 | var ( 10 | ctx = context.Background() 11 | req = struct{}{} 12 | ) 13 | 14 | func TestChain(t *testing.T) { 15 | e := Chain( 16 | annotate("first"), 17 | annotate("second"), 18 | annotate("third"), 19 | )(myEndpoint) 20 | 21 | if _, err := e(ctx, req); err != nil { 22 | panic(err) 23 | } 24 | // Output: 25 | // first pre 26 | // second pre 27 | // third pre 28 | // my endpoint! 29 | // third post 30 | // second post 31 | // first post 32 | } 33 | 34 | func ExampleChain() { 35 | e := Chain( 36 | annotate("first"), 37 | annotate("second"), 38 | annotate("third"), 39 | )(myEndpoint) 40 | 41 | if _, err := e(ctx, req); err != nil { 42 | panic(err) 43 | } 44 | } 45 | 46 | func annotate(s string) MiddleWare { 47 | return func(next Endpoint) Endpoint { 48 | return func(ctx context.Context, request interface{}) (interface{}, error) { 49 | fmt.Println(s, "pre") 50 | defer fmt.Println(s, "post") 51 | return next(ctx, request) 52 | } 53 | } 54 | } 55 | 56 | func myEndpoint(context.Context, interface{}) (interface{}, error) { 57 | fmt.Println("my endpoint!") 58 | return struct{}{}, nil 59 | } 60 | -------------------------------------------------------------------------------- /sys/xxhash3/util.go: -------------------------------------------------------------------------------- 1 | // Package xxhash3 implements https://github.com/Cyan4973/xxHash/blob/dev/xxhash.h 2 | package xxhash3 3 | 4 | import ( 5 | "math/bits" 6 | "unsafe" 7 | 8 | "golang.org/x/sys/cpu" 9 | ) 10 | 11 | var ( 12 | avx2 = cpu.X86.HasAVX2 13 | sse2 = cpu.X86.HasSSE2 14 | hashfunc = [2]func(unsafe.Pointer, int) uint64{xxh3HashSmall, xxh3HashLarge} 15 | hashfunc128 = [2]func(unsafe.Pointer, int) [2]uint64{xxh3HashSmall128, xxh3HashLarge128} 16 | ) 17 | 18 | type funcUnsafe int 19 | 20 | const ( 21 | hashSmall funcUnsafe = iota 22 | hashLarge 23 | ) 24 | 25 | func mix(a, b uint64) uint64 { 26 | hi, lo := bits.Mul64(a, b) 27 | return hi ^ lo 28 | } 29 | func xxh3RRMXMX(h64 uint64, length uint64) uint64 { 30 | h64 ^= bits.RotateLeft64(h64, 49) ^ bits.RotateLeft64(h64, 24) 31 | h64 *= 0x9fb21c651e98df25 32 | h64 ^= (h64 >> 35) + length 33 | h64 *= 0x9fb21c651e98df25 34 | h64 ^= (h64 >> 28) 35 | return h64 36 | } 37 | 38 | func xxh64Avalanche(h64 uint64) uint64 { 39 | h64 *= prime64_2 40 | h64 ^= h64 >> 29 41 | h64 *= prime64_3 42 | h64 ^= h64 >> 32 43 | return h64 44 | } 45 | 46 | func xxh3Avalanche(x uint64) uint64 { 47 | x ^= x >> 37 48 | x *= 0x165667919e3779f9 49 | x ^= x >> 32 50 | return x 51 | } 52 | -------------------------------------------------------------------------------- /tools/rand_string/rand_string.go: -------------------------------------------------------------------------------- 1 | package rand_string 2 | 3 | import ( 4 | "math/rand" 5 | "time" 6 | ) 7 | 8 | const letterBytes = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" 9 | const numberBytes = "0123456789" 10 | 11 | const ( 12 | letterIdxBits = 6 // 6 bits to represent a letter index 13 | letterIdxMask = 1<= 0; { 34 | if remain == 0 { 35 | cache, remain = src.Int63(), letterIdxMax 36 | } 37 | if idx := int(cache & letterIdxMask); idx < len(bytes) { 38 | b[i] = bytes[idx] 39 | i-- 40 | } 41 | cache >>= letterIdxBits 42 | remain-- 43 | } 44 | 45 | return string(b) 46 | } 47 | -------------------------------------------------------------------------------- /window/init.go: -------------------------------------------------------------------------------- 1 | package window 2 | 3 | import ( 4 | "context" 5 | "sync/atomic" 6 | "time" 7 | 8 | "github.com/songzhibin97/gkit/options" 9 | ) 10 | 11 | // SetSize 设置大小 12 | func SetSize(size uint) options.Option { 13 | return func(c interface{}) { 14 | c.(*conf).size = size 15 | } 16 | } 17 | 18 | // SetInterval 设置间隔时间 19 | func SetInterval(interval time.Duration) options.Option { 20 | return func(c interface{}) { 21 | c.(*conf).interval = interval 22 | } 23 | } 24 | 25 | // SetContext 设置context 26 | func SetContext(context context.Context) options.Option { 27 | return func(c interface{}) { 28 | c.(*conf).ctx = context 29 | } 30 | } 31 | 32 | // NewWindow 实例化 33 | func NewWindow(options ...options.Option) SlidingWindow { 34 | w := Window{ 35 | // 默认值: 36 | conf: conf{ 37 | size: 5, 38 | interval: time.Second, 39 | ctx: context.Background(), 40 | }, 41 | } 42 | for _, option := range options { 43 | option(&w.conf) 44 | } 45 | w.buffer = make([]atomic.Value, w.size) 46 | for i := uint(0); i < w.size; i++ { 47 | w.buffer[i].Store(make(map[string]uint)) 48 | } 49 | w.communication = make(chan Index, w.size) 50 | w.ctx, w.cancel = context.WithCancel(w.ctx) 51 | // 开启哨兵 52 | go w.sentinel() 53 | return &w 54 | } 55 | -------------------------------------------------------------------------------- /tools/bind/form.go: -------------------------------------------------------------------------------- 1 | package bind 2 | 3 | import ( 4 | "net/http" 5 | ) 6 | 7 | const defaultMemory = 32 << 20 8 | 9 | type ( 10 | formBinding struct{} 11 | formPostBinding struct{} 12 | formMultipartBinding struct{} 13 | ) 14 | 15 | func (formBinding) Name() string { 16 | return "form" 17 | } 18 | 19 | func (formBinding) Bind(req *http.Request, obj interface{}) error { 20 | if err := req.ParseForm(); err != nil { 21 | return err 22 | } 23 | if err := req.ParseMultipartForm(defaultMemory); err != nil { 24 | if err != http.ErrNotMultipart { 25 | return err 26 | } 27 | } 28 | return mapForm(obj, req.Form) 29 | } 30 | 31 | func (formPostBinding) Name() string { 32 | return "form-urlencoded" 33 | } 34 | 35 | func (formPostBinding) Bind(req *http.Request, obj interface{}) error { 36 | if err := req.ParseForm(); err != nil { 37 | return err 38 | } 39 | return mapForm(obj, req.PostForm) 40 | } 41 | 42 | func (formMultipartBinding) Name() string { 43 | return "multipart/form-data" 44 | } 45 | 46 | func (formMultipartBinding) Bind(req *http.Request, obj interface{}) error { 47 | if err := req.ParseMultipartForm(defaultMemory); err != nil { 48 | return err 49 | } 50 | return mappingByPtr(obj, (*multipartRequest)(req), "form") 51 | } 52 | -------------------------------------------------------------------------------- /page_token/token_test.go: -------------------------------------------------------------------------------- 1 | package page_token 2 | 3 | import ( 4 | "testing" 5 | "time" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestNewToken(t *testing.T) { 11 | n := NewTokenGenerate("test") 12 | 13 | i, err := n.GetIndex(n.ForIndex(1)) 14 | assert.NoError(t, err) 15 | assert.Equal(t, i, 1) 16 | 17 | n2 := NewTokenGenerate("test", SetSalt("test1")) 18 | s2 := n2.ForIndex(1) 19 | _, err = n.GetIndex(s2) 20 | assert.Error(t, err) 21 | assert.Equal(t, err, ErrInvalidToken) 22 | 23 | n3 := NewTokenGenerate("test", SetMaxIndex(10)) 24 | 25 | _, err = n3.GetIndex(n3.ForIndex(11)) 26 | assert.Error(t, err) 27 | assert.Equal(t, err, ErrOverMaxPageSizeToken) 28 | 29 | n4 := NewTokenGenerate("test", SetTimeLimitation(time.Second*5)) 30 | s4 := n4.ForIndex(1) 31 | time.Sleep(6 * time.Second) 32 | _, err = n4.GetIndex(s4) 33 | assert.Error(t, err) 34 | assert.Equal(t, err, ErrOverdueToken) 35 | } 36 | 37 | func Test_token_ProcessPageTokens(t *testing.T) { 38 | n := NewTokenGenerate("test") 39 | s, e, tk, err := n.ProcessPageTokens(10, 1, "") 40 | t.Log(s, e, tk) 41 | assert.NoError(t, err) 42 | for tk != "" { 43 | s, e, tk, err = n.ProcessPageTokens(10, 3, tk) 44 | t.Log(s, e, tk) 45 | assert.NoError(t, err) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /watching/example/deadlock/deadlock.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "sync" 6 | "time" 7 | 8 | "github.com/songzhibin97/gkit/watching" 9 | ) 10 | 11 | func init() { 12 | http.HandleFunc("/lockorder1", lockorder1) 13 | http.HandleFunc("/lockorder2", lockorder2) 14 | http.HandleFunc("/req", req) 15 | go http.ListenAndServe(":10003", nil) 16 | } 17 | 18 | func main() { 19 | w := watching.NewWatching( 20 | watching.WithCollectInterval("5s"), 21 | watching.WithCoolDown("1m"), 22 | watching.WithDumpPath("/tmp"), 23 | watching.WithTextDump(), 24 | watching.WithGoroutineDump(10, 25, 2000, 10000), 25 | ) 26 | w.EnableGoroutineDump().Start() 27 | time.Sleep(time.Hour) 28 | } 29 | 30 | var ( 31 | l1 sync.Mutex 32 | l2 sync.Mutex 33 | ) 34 | 35 | func req(wr http.ResponseWriter, req *http.Request) { 36 | l1.Lock() 37 | defer l1.Unlock() 38 | } 39 | 40 | func lockorder1(wr http.ResponseWriter, req *http.Request) { 41 | l1.Lock() 42 | defer l1.Unlock() 43 | 44 | time.Sleep(time.Minute) 45 | 46 | l2.Lock() 47 | defer l2.Unlock() 48 | } 49 | 50 | func lockorder2(wr http.ResponseWriter, req *http.Request) { 51 | l2.Lock() 52 | defer l2.Unlock() 53 | 54 | time.Sleep(time.Minute) 55 | 56 | l1.Lock() 57 | defer l1.Unlock() 58 | } 59 | -------------------------------------------------------------------------------- /watching/example/run_in_docker/run_in_docker.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | 7 | "github.com/songzhibin97/gkit/watching" 8 | ) 9 | 10 | func init() { 11 | http.HandleFunc("/docker", dockermake1gb) 12 | http.HandleFunc("/docker/cpu", cpuex) 13 | http.HandleFunc("/docker/cpu_multi_core", cpuMulticore) 14 | go http.ListenAndServe(":10003", nil) 15 | } 16 | 17 | func main() { 18 | w := watching.NewWatching( 19 | watching.WithCollectInterval("2s"), 20 | watching.WithCoolDown("1m"), 21 | watching.WithDumpPath("/tmp"), 22 | watching.WithTextDump(), 23 | watching.WithLoggerLevel(watching.LogLevelDebug), 24 | watching.WithMemDump(3, 25, 80), 25 | watching.WithCPUDump(60, 10, 80), 26 | watching.WithCGroup(true), 27 | ) 28 | w.EnableCPUDump() 29 | w.EnableMemDump() 30 | w.Start() 31 | time.Sleep(time.Hour) 32 | } 33 | 34 | func cpuex(wr http.ResponseWriter, req *http.Request) { 35 | go func() { 36 | for { 37 | } 38 | }() 39 | } 40 | 41 | func cpuMulticore(wr http.ResponseWriter, req *http.Request) { 42 | for i := 1; i <= 100; i++ { 43 | go func() { 44 | for { 45 | } 46 | }() 47 | } 48 | } 49 | 50 | func dockermake1gb(wr http.ResponseWriter, req *http.Request) { 51 | a := make([]byte, 1073741824) 52 | _ = a 53 | } 54 | -------------------------------------------------------------------------------- /concurrent/fan_in_test.go: -------------------------------------------------------------------------------- 1 | package concurrent 2 | 3 | import ( 4 | "sort" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func fanIn(start, end int) <-chan interface{} { 11 | out := make(chan interface{}) 12 | go func() { 13 | defer close(out) 14 | for i := start; i < end; i++ { 15 | out <- i 16 | } 17 | }() 18 | return out 19 | } 20 | 21 | func TestFanInRec(t *testing.T) { 22 | out := FanInRec(fanIn(0, 6), fanIn(6, 11), fanIn(11, 20)) 23 | outSlice := make([]interface{}, 0) 24 | for v := range out { 25 | outSlice = append(outSlice, v) 26 | } 27 | assert.Len(t, outSlice, 20) 28 | sort.Slice(outSlice, func(i, j int) bool { 29 | return outSlice[i].(int) < outSlice[j].(int) 30 | }) 31 | assert.Equal(t, outSlice, []interface{}{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}) 32 | } 33 | 34 | func TestMergeChannel(t *testing.T) { 35 | out := MergeChannel(fanIn(0, 6), fanIn(6, 11)) 36 | outSlice := make([]interface{}, 0) 37 | for v := range out { 38 | outSlice = append(outSlice, v) 39 | } 40 | assert.Len(t, outSlice, 11) 41 | sort.Slice(outSlice, func(i, j int) bool { 42 | return outSlice[i].(int) < outSlice[j].(int) 43 | }) 44 | assert.Equal(t, outSlice, []interface{}{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) 45 | } 46 | -------------------------------------------------------------------------------- /watching/example/thread_trigger/thread_trigger.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | /* 4 | #include 5 | #include 6 | #include 7 | void output(char *str) { 8 | sleep(10000); 9 | printf("%s\n", str); 10 | } 11 | */ 12 | import "C" 13 | 14 | import ( 15 | "fmt" 16 | "net/http" 17 | "time" 18 | "unsafe" 19 | 20 | "github.com/songzhibin97/gkit/watching" 21 | 22 | _ "net/http/pprof" 23 | ) 24 | 25 | func init() { 26 | go func() { 27 | w := watching.NewWatching( 28 | watching.WithCollectInterval("2s"), 29 | watching.WithCoolDown("5s"), 30 | watching.WithDumpPath("/tmp"), 31 | watching.WithTextDump(), 32 | watching.WithThreadDump(10, 25, 100), 33 | ) 34 | w.EnableThreadDump().Start() 35 | time.Sleep(time.Hour) 36 | }() 37 | } 38 | 39 | func leak(wr http.ResponseWriter, req *http.Request) { 40 | go func() { 41 | for i := 0; i < 1000; i++ { 42 | go func() { 43 | str := "hello cgo" 44 | // change to char* 45 | cstr := C.CString(str) 46 | C.output(cstr) 47 | C.free(unsafe.Pointer(cstr)) 48 | }() 49 | } 50 | }() 51 | } 52 | 53 | func main() { 54 | http.HandleFunc("/leak", leak) 55 | err := http.ListenAndServe(":10003", nil) 56 | if err != nil { 57 | fmt.Println(err) 58 | return 59 | } 60 | select {} 61 | } 62 | -------------------------------------------------------------------------------- /tools/vto/default.go: -------------------------------------------------------------------------------- 1 | package vto 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/songzhibin97/gkit/tools" 7 | ) 8 | 9 | func CompletionDefault(dst interface{}) error { 10 | dstT := reflect.TypeOf(dst) 11 | if dstT.Kind() != reflect.Ptr { 12 | return tools.ErrorMustPtr 13 | } 14 | 15 | dstT = dstT.Elem() 16 | if dstT.Kind() != reflect.Struct { 17 | return tools.ErrorMustStructPtr 18 | } 19 | 20 | dstV := reflect.ValueOf(dst).Elem() 21 | for i := 0; i < dstT.NumField(); i++ { 22 | field := dstT.Field(i) 23 | if !field.IsExported() { 24 | continue 25 | } 26 | d := dstV.Field(i) 27 | 28 | if d.Kind() == reflect.Struct { 29 | err := CompletionDefault(d.Addr().Interface()) 30 | if err != nil { 31 | return err 32 | } 33 | continue 34 | } 35 | 36 | defaultTag := field.Tag.Get("default") 37 | if defaultTag == "" { 38 | continue 39 | } 40 | 41 | if d.IsZero() { 42 | if d.Kind() == reflect.Ptr { 43 | ss := reflect.New(d.Type().Elem()) 44 | err := bindDefault(ss.Elem(), defaultTag, field) 45 | if err != nil { 46 | return err 47 | } 48 | d.Set(ss) 49 | } else { 50 | err := bindDefault(d, defaultTag, field) 51 | if err != nil { 52 | return err 53 | } 54 | } 55 | } 56 | } 57 | 58 | return nil 59 | } 60 | -------------------------------------------------------------------------------- /restrictor/rate/rate_test.go: -------------------------------------------------------------------------------- 1 | package rate 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "golang.org/x/time/rate" 9 | ) 10 | 11 | func TestRate(t *testing.T) { 12 | // 第一个参数是 r Limit。代表每秒可以向 Token 桶中产生多少 token。Limit 实际上是 float64 的别名 13 | // 第二个参数是 b int。b 代表 Token 桶的容量大小。 14 | // limit := Every(100 * time.Millisecond); 15 | // limiter := rate.NewLimiter(limit, 4) 16 | // 以上就表示每 100ms 往桶中放一个 Token。本质上也就是一秒钟产生 10 个。 17 | limiter := rate.NewLimiter(2, 4) 18 | af, wf := NewRate(limiter) 19 | // 暂停3秒,等待桶满 20 | time.Sleep(3 * time.Second) 21 | for i := 0; i < 10; i++ { 22 | t.Log("i:", i, af.Allow()) 23 | } 24 | 25 | // 暂停3秒,等待桶满 26 | time.Sleep(3 * time.Second) 27 | for i := 0; i < 5; i++ { 28 | t.Log("i:", i, af.AllowN(time.Now(), 2)) 29 | } 30 | 31 | // 暂停3秒,等待桶满 32 | time.Sleep(3 * time.Second) 33 | for i := 0; i < 10; i++ { 34 | func(i int) { 35 | ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) 36 | defer cancel() 37 | t.Log("i:", i, wf.Wait(ctx)) 38 | }(i) 39 | } 40 | // 暂停3秒,等待桶满 41 | time.Sleep(3 * time.Second) 42 | for i := 0; i < 5; i++ { 43 | func(i int) { 44 | ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) 45 | defer cancel() 46 | t.Log("i:", i, wf.WaitN(ctx, 2)) 47 | }(i) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /timeout/date.go: -------------------------------------------------------------------------------- 1 | package timeout 2 | 3 | import ( 4 | "database/sql/driver" 5 | "fmt" 6 | "strings" 7 | "time" 8 | ) 9 | 10 | type Date time.Time 11 | 12 | const DateFormat = "2006-01-02" 13 | 14 | func (d *Date) UnmarshalJSON(src []byte) error { 15 | return d.UnmarshalText(strings.Replace(string(src), "\"", "", -1)) 16 | } 17 | 18 | func (d Date) MarshalJSON() ([]byte, error) { 19 | return []byte(`"` + d.String() + `"`), nil 20 | } 21 | 22 | func (d *Date) UnmarshalText(value string) error { 23 | dd, err := time.Parse(DateFormat, value) 24 | if err != nil { 25 | return err 26 | } 27 | *d = Date(dd) 28 | return nil 29 | } 30 | 31 | func (d Date) String() string { 32 | return (time.Time)(d).Format(DateFormat) 33 | } 34 | 35 | func (d *Date) Scan(value interface{}) error { 36 | switch v := value.(type) { 37 | case []byte: 38 | return d.UnmarshalText(string(v)) 39 | case string: 40 | return d.UnmarshalText(v) 41 | case time.Time: 42 | *d = Date(v) 43 | case nil: 44 | *d = Date{} 45 | default: 46 | return fmt.Errorf("cannot sql.Scan() DBDate from: %#v", v) 47 | } 48 | return nil 49 | } 50 | 51 | func (d Date) Value() (driver.Value, error) { 52 | return driver.Value(time.Time(d).Format(DateFormat)), nil 53 | } 54 | 55 | func (Date) GormDataType() string { 56 | return "DATE" 57 | } 58 | -------------------------------------------------------------------------------- /sys/mutex/mutex_test.go: -------------------------------------------------------------------------------- 1 | package mutex 2 | 3 | import ( 4 | "runtime" 5 | "sync" 6 | "testing" 7 | "time" 8 | 9 | "github.com/songzhibin97/gkit/internal/clock" 10 | ) 11 | 12 | func Test_Mutex_TryLock(t *testing.T) { 13 | var m Mutex 14 | m.Lock() 15 | clock.Sleep(time.Second) 16 | if m.TryLock() { 17 | t.Error("TryLock get lock error") 18 | } 19 | m.Unlock() 20 | if !m.TryLock() { 21 | t.Error("TryLock get lock error") 22 | } 23 | m.Unlock() 24 | } 25 | 26 | func utTriableMutexConcurrent(t *testing.T) { 27 | m := &Mutex{} 28 | cnt := int32(0) 29 | wg := &sync.WaitGroup{} 30 | wg.Add(1000) 31 | for i := 0; i < 1000; i++ { 32 | go func(tm *Mutex, wgi *sync.WaitGroup, cntPtr *int32, t *testing.T) { 33 | for { 34 | if tm.TryLock() { 35 | *cntPtr = *cntPtr + 1 36 | tm.Unlock() 37 | wgi.Done() 38 | break 39 | } else { 40 | runtime.Gosched() 41 | } 42 | } 43 | }(m, wg, &cnt, t) 44 | } 45 | wg.Wait() 46 | // fmt.Println("count=", cnt) 47 | if cnt != 1000 { 48 | t.Error("count error concurrency") 49 | } 50 | } 51 | 52 | func Test_Mutex_TryLock_Concurrent(t *testing.T) { 53 | utTriableMutexConcurrent(t) 54 | } 55 | 56 | func Benchmark_Mutex_TryLock(b *testing.B) { 57 | for n := 0; n < b.N; n++ { 58 | utTriableMutexConcurrent(nil) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /egroup/option.go: -------------------------------------------------------------------------------- 1 | package egroup 2 | 3 | import ( 4 | "os" 5 | "time" 6 | 7 | "github.com/songzhibin97/gkit/options" 8 | ) 9 | 10 | // config 11 | type config struct { 12 | // startTimeout: 启动超时时间 13 | // <=0 不启动超时时间,注意要在shutdown处理关闭通知 14 | startTimeout time.Duration 15 | 16 | // stopTimeout: 关闭超时时间 17 | // <=0 不启动超时时间 18 | stopTimeout time.Duration 19 | 20 | // signals: 信号集 21 | signals []os.Signal 22 | 23 | // handler: 捕捉信号后处理函数 24 | handler func(*LifeAdmin, os.Signal) 25 | 26 | // group: goroutine group 27 | g *Group 28 | } 29 | 30 | // SetStartTimeout 设置启动超时时间 31 | func SetStartTimeout(d time.Duration) options.Option { 32 | return func(c interface{}) { c.(*config).startTimeout = d } 33 | } 34 | 35 | // SetStopTimeout 设置停止超时时间 36 | func SetStopTimeout(d time.Duration) options.Option { 37 | return func(c interface{}) { c.(*config).stopTimeout = d } 38 | } 39 | 40 | // SetSignal 设置信号集合,和处理信号的函数 41 | func SetSignal(handler func(*LifeAdmin, os.Signal), signals ...os.Signal) options.Option { 42 | return func(c interface{}) { 43 | conf := c.(*config) 44 | conf.handler = handler 45 | conf.signals = signals 46 | } 47 | } 48 | 49 | // SetGroup 设置goroutine group 50 | func SetGroup(g *Group) options.Option { 51 | return func(c interface{}) { 52 | conf := c.(*config) 53 | conf.g = g 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /internal/sys/mutex/mutex_test.go: -------------------------------------------------------------------------------- 1 | package mutex 2 | 3 | import ( 4 | "runtime" 5 | "sync" 6 | "testing" 7 | "time" 8 | 9 | "github.com/songzhibin97/gkit/internal/clock" 10 | ) 11 | 12 | func Test_Mutex_TryLock(t *testing.T) { 13 | var m Mutex 14 | m.Lock() 15 | clock.Sleep(time.Second) 16 | if m.TryLock() { 17 | t.Error("TryLock get lock error") 18 | } 19 | m.Unlock() 20 | if !m.TryLock() { 21 | t.Error("TryLock get lock error") 22 | } 23 | m.Unlock() 24 | } 25 | 26 | func utTriableMutexConcurrent(t *testing.T) { 27 | m := &Mutex{} 28 | cnt := int32(0) 29 | wg := &sync.WaitGroup{} 30 | wg.Add(1000) 31 | for i := 0; i < 1000; i++ { 32 | go func(tm *Mutex, wgi *sync.WaitGroup, cntPtr *int32, t *testing.T) { 33 | for { 34 | if tm.TryLock() { 35 | *cntPtr = *cntPtr + 1 36 | tm.Unlock() 37 | wgi.Done() 38 | break 39 | } else { 40 | runtime.Gosched() 41 | } 42 | } 43 | }(m, wg, &cnt, t) 44 | } 45 | wg.Wait() 46 | // fmt.Println("count=", cnt) 47 | if cnt != 1000 { 48 | t.Error("count error concurrency") 49 | } 50 | } 51 | 52 | func Test_Mutex_TryLock_Concurrent(t *testing.T) { 53 | utTriableMutexConcurrent(t) 54 | } 55 | 56 | func Benchmark_Mutex_TryLock(b *testing.B) { 57 | for n := 0; n < b.N; n++ { 58 | utTriableMutexConcurrent(nil) 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /structure/skipmap/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ByteDance Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package skipmap 16 | 17 | import ( 18 | "github.com/songzhibin97/gkit/internal/wyhash" 19 | "github.com/songzhibin97/gkit/sys/fastrand" 20 | _ "unsafe" // for linkname 21 | ) 22 | 23 | const ( 24 | maxLevel = 16 25 | p = 0.25 26 | defaultHighestLevel = 3 27 | ) 28 | 29 | func hash(s string) uint64 { 30 | return wyhash.Sum64String(s) 31 | } 32 | 33 | //go:linkname cmpstring runtime.cmpstring 34 | func cmpstring(a, b string) int 35 | 36 | func randomLevel() int { 37 | level := 1 38 | for fastrand.Uint32n(1/p) == 0 { 39 | level++ 40 | } 41 | if level > maxLevel { 42 | return maxLevel 43 | } 44 | return level 45 | } 46 | -------------------------------------------------------------------------------- /structure/skipset/util.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ByteDance Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package skipset 16 | 17 | import ( 18 | "github.com/songzhibin97/gkit/internal/wyhash" 19 | "github.com/songzhibin97/gkit/sys/fastrand" 20 | _ "unsafe" // for linkname 21 | ) 22 | 23 | const ( 24 | maxLevel = 16 25 | p = 0.25 26 | defaultHighestLevel = 3 27 | ) 28 | 29 | func hash(s string) uint64 { 30 | return wyhash.Sum64String(s) 31 | } 32 | 33 | //go:linkname cmpstring runtime.cmpstring 34 | func cmpstring(a, b string) int 35 | 36 | func randomLevel() int { 37 | level := 1 38 | for fastrand.Uint32n(1/p) == 0 { 39 | level++ 40 | } 41 | if level > maxLevel { 42 | return maxLevel 43 | } 44 | return level 45 | } 46 | -------------------------------------------------------------------------------- /timeout/d_time.go: -------------------------------------------------------------------------------- 1 | package timeout 2 | 3 | import ( 4 | "database/sql/driver" 5 | "fmt" 6 | "strings" 7 | "time" 8 | ) 9 | 10 | type DTime time.Time 11 | 12 | const DTimeFormat = "15:04:05" 13 | 14 | func (d *DTime) UnmarshalJSON(src []byte) error { 15 | return d.UnmarshalText(strings.Replace(string(src), "\"", "", -1)) 16 | } 17 | 18 | func (d DTime) MarshalJSON() ([]byte, error) { 19 | return []byte(`"` + d.String() + `"`), nil 20 | } 21 | 22 | func (d *DTime) UnmarshalText(value string) error { 23 | dd, err := time.Parse(DTimeFormat, value) 24 | if err != nil { 25 | return err 26 | } 27 | *d = DTime(dd) 28 | return nil 29 | } 30 | 31 | func (d DTime) String() string { 32 | return (time.Time)(d).Format(DTimeFormat) 33 | } 34 | 35 | func (d *DTime) Scan(value interface{}) error { 36 | switch v := value.(type) { 37 | case []byte: 38 | return d.UnmarshalText(string(v)) 39 | case string: 40 | return d.UnmarshalText(v) 41 | case time.Time: 42 | *d = DTime(v) 43 | case nil: 44 | *d = DTime{} 45 | default: 46 | return fmt.Errorf("cannot sql.Scan() DBDate from: %#v", v) 47 | } 48 | return nil 49 | } 50 | 51 | func (d DTime) Value() (driver.Value, error) { 52 | return driver.Value(time.Time(d).Format(DTimeFormat)), nil 53 | } 54 | 55 | func (DTime) GormDataType() string { 56 | return "TIME" 57 | } 58 | -------------------------------------------------------------------------------- /goroutine/option.go: -------------------------------------------------------------------------------- 1 | package goroutine 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/songzhibin97/gkit/log" 7 | "github.com/songzhibin97/gkit/options" 8 | ) 9 | 10 | // config 11 | type config struct { 12 | 13 | // stopTimeout: 关闭超时时间 14 | // 控制shutdown关闭超时时间 15 | // <=0 不启动超时时间 16 | stopTimeout time.Duration 17 | 18 | // max 最大goroutine以及初始化channel大小,channel长度不可更改 19 | max int64 20 | 21 | // idle 闲置goroutine大小 22 | idle int64 23 | 24 | // checkTime 检查时间 25 | checkTime time.Duration 26 | 27 | // logger 日志输出对象 28 | logger log.Logger 29 | } 30 | 31 | // SetStopTimeout 设置停止超时时间 32 | func SetStopTimeout(d time.Duration) options.Option { 33 | return func(c interface{}) { c.(*config).stopTimeout = d } 34 | } 35 | 36 | // SetMax 设置pool最大容量 37 | func SetMax(max int64) options.Option { 38 | return func(c interface{}) { c.(*config).max = max } 39 | } 40 | 41 | // SetIdle 这是pool闲置goroutine数量 42 | func SetIdle(idle int64) options.Option { 43 | return func(c interface{}) { c.(*config).idle = idle } 44 | } 45 | 46 | // SetCheckTime 设置检查时间 47 | func SetCheckTime(d time.Duration) options.Option { 48 | return func(c interface{}) { c.(*config).checkTime = d } 49 | } 50 | 51 | // SetLogger 设置日志对象 52 | func SetLogger(logger log.Logger) options.Option { 53 | return func(c interface{}) { c.(*config).logger = logger } 54 | } 55 | -------------------------------------------------------------------------------- /sys/cpu/cpu.go: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import ( 4 | "fmt" 5 | "sync/atomic" 6 | "time" 7 | ) 8 | 9 | const ( 10 | interval time.Duration = time.Millisecond * 500 11 | ) 12 | 13 | var ( 14 | stats CPU 15 | usage uint64 16 | ) 17 | 18 | // CPU interface 定义CPU用法 19 | type CPU interface { 20 | Usage() (u uint64, e error) 21 | Info() Info 22 | } 23 | 24 | func init() { 25 | var err error 26 | // 判断操作系统使用的是cGroup,如果不是cGroup退化为Psutil 27 | stats, err = newCGroupCPU() 28 | if err != nil { 29 | stats, err = newPsutilCPU(interval) 30 | if err != nil { 31 | panic(fmt.Sprintf("cGroup cpu init failed!err:=%v", err)) 32 | } 33 | } 34 | // 开启定时任务 35 | go func() { 36 | ticker := time.NewTicker(interval) 37 | defer ticker.Stop() 38 | for { 39 | <-ticker.C 40 | u, err := stats.Usage() 41 | if err == nil && u != 0 { 42 | atomic.StoreUint64(&usage, u) 43 | } 44 | } 45 | }() 46 | } 47 | 48 | // Stat 状态信息 49 | type Stat struct { 50 | // Usage: CPU使用率 51 | Usage uint64 52 | } 53 | 54 | // Info 详细信息 55 | type Info struct { 56 | // Frequency: 频率 57 | Frequency uint64 58 | // Quota: 磁盘配额 59 | Quota float64 60 | } 61 | 62 | // ReadStat 读取状态 63 | func ReadStat(stat *Stat) { 64 | stat.Usage = atomic.LoadUint64(&usage) 65 | } 66 | 67 | // GetInfo 获取信息 68 | func GetInfo() Info { 69 | return stats.Info() 70 | } 71 | -------------------------------------------------------------------------------- /tools/pointer/type_gen.go: -------------------------------------------------------------------------------- 1 | //go:build ignore 2 | // +build ignore 3 | 4 | package main 5 | 6 | import ( 7 | "bytes" 8 | "go/format" 9 | "io/ioutil" 10 | "os" 11 | "strings" 12 | ) 13 | 14 | var packageName = "package pointer" 15 | 16 | func main() { 17 | f, err := os.Open("template.txt") 18 | if err != nil { 19 | panic(err) 20 | } 21 | fileData, err := ioutil.ReadAll(f) 22 | if err != nil { 23 | panic(err) 24 | } 25 | 26 | w := new(bytes.Buffer) 27 | start_pos := strings.Index(string(fileData), packageName) 28 | w.WriteString(string(fileData)[start_pos : start_pos+len(packageName)]) 29 | 30 | ts := []string{"Byte", "Complex64", "Complex128", "Float32", "Float64", "Int", "Int8", "Int16", "Int32", "Int64", "Rune", "Uint", "Uint8", "Uint16", "Uint32", "Uint64", "Uintptr"} 31 | 32 | for _, upper := range ts { 33 | lower := strings.ToLower(upper) 34 | data := string(fileData) 35 | 36 | data = data[start_pos+len(packageName):] 37 | 38 | data = strings.Replace(data, "{{upper}}", upper, -1) 39 | data = strings.Replace(data, "{{lower}}", lower, -1) 40 | 41 | w.WriteString(data) 42 | w.WriteString("\r\n") 43 | } 44 | 45 | out, err := format.Source(w.Bytes()) 46 | if err != nil { 47 | panic(err) 48 | } 49 | if err := ioutil.WriteFile("types.go", out, 0660); err != nil { 50 | panic(err) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /internal/sys/cpu/cpu.go: -------------------------------------------------------------------------------- 1 | package cpu 2 | 3 | import ( 4 | "fmt" 5 | "sync/atomic" 6 | "time" 7 | ) 8 | 9 | const ( 10 | interval time.Duration = time.Millisecond * 500 11 | ) 12 | 13 | var ( 14 | stats CPU 15 | usage uint64 16 | ) 17 | 18 | // CPU interface 定义CPU用法 19 | type CPU interface { 20 | Usage() (u uint64, e error) 21 | Info() Info 22 | } 23 | 24 | func init() { 25 | var err error 26 | // 判断操作系统使用的是cGroup,如果不是cGroup退化为Psutil 27 | stats, err = newCGroupCPU() 28 | if err != nil { 29 | stats, err = newPsutilCPU(interval) 30 | if err != nil { 31 | panic(fmt.Sprintf("cGroup cpu init failed!err:=%v", err)) 32 | } 33 | } 34 | // 开启定时任务 35 | go func() { 36 | ticker := time.NewTicker(interval) 37 | defer ticker.Stop() 38 | for { 39 | <-ticker.C 40 | u, err := stats.Usage() 41 | if err == nil && u != 0 { 42 | atomic.StoreUint64(&usage, u) 43 | } 44 | } 45 | }() 46 | } 47 | 48 | // Stat 状态信息 49 | type Stat struct { 50 | // Usage: CPU使用率 51 | Usage uint64 52 | } 53 | 54 | // Info 详细信息 55 | type Info struct { 56 | // Frequency: 频率 57 | Frequency uint64 58 | // Quota: 磁盘配额 59 | Quota float64 60 | } 61 | 62 | // ReadStat 读取状态 63 | func ReadStat(stat *Stat) { 64 | stat.Usage = atomic.LoadUint64(&usage) 65 | } 66 | 67 | // GetInfo 获取信息 68 | func GetInfo() Info { 69 | return stats.Info() 70 | } 71 | -------------------------------------------------------------------------------- /sys/xxhash3/internal/avo/build.sh: -------------------------------------------------------------------------------- 1 | go run sse.go avx.go gen.go -avx2 -out ./avx2.s 2 | go run sse.go avx.go gen.go -sse2 -out ./sse2.s 3 | sed -i '1i// Copyright 2021 ByteDance Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the "License");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an "AS IS" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n' ./avx2.s 4 | sed -i '1i// Copyright 2021 ByteDance Inc.\n//\n// Licensed under the Apache License, Version 2.0 (the "License");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an "AS IS" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n' ./sse2.s 5 | -------------------------------------------------------------------------------- /distributed/task/result.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | "strings" 7 | ) 8 | 9 | // Result 任务返回携带的kv键值对 10 | type Result struct { 11 | // Type 标注返回的类型 12 | Type string `json:"type" bson:"type"` 13 | // Value 根据type解压value 14 | Value interface{} `json:"value" bson:"value"` 15 | } 16 | 17 | // ConvertResult 将Result类型转换成reflect.Value 18 | func ConvertResult(result []*Result) ([]reflect.Value, error) { 19 | convertResult := make([]reflect.Value, 0, len(result)) 20 | for _, r := range result { 21 | _value, err := ReflectValue(r.Type, r.Value) 22 | if err != nil { 23 | return nil, err 24 | } 25 | convertResult = append(convertResult, _value) 26 | } 27 | return convertResult, nil 28 | } 29 | 30 | // FormatResult 将reflect.Value转换为可读答案 31 | func FormatResult(values []reflect.Value) string { 32 | ln := len(values) 33 | switch ln { 34 | case 0: 35 | return "" 36 | case 1: 37 | return fmt.Sprintf("[%v]", values[0].Interface()) 38 | default: 39 | builder := strings.Builder{} 40 | for i, value := range values { 41 | if i == 0 { 42 | builder.WriteString("[ ") 43 | } 44 | builder.WriteString(fmt.Sprintf("%v", value.Interface())) 45 | if i != ln-1 { 46 | builder.WriteString(", ") 47 | } else { 48 | builder.WriteString(" ]") 49 | } 50 | } 51 | return builder.String() 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /cache/local_cache/option.go: -------------------------------------------------------------------------------- 1 | package local_cache 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/songzhibin97/gkit/options" 7 | ) 8 | 9 | type Config struct { 10 | // defaultExpire 默认超时时间 11 | defaultExpire time.Duration 12 | 13 | // interval 间隔时间 14 | interval time.Duration 15 | // fn 哨兵周期执行的函数 16 | fn func() 17 | 18 | // capture 捕获删除对象时间 会返回kv值用于用户自定义处理 19 | capture func(k string, v interface{}) 20 | 21 | member map[string]Iterator 22 | } 23 | 24 | // SetInternal 设置间隔时间 25 | func SetInternal(interval time.Duration) options.Option { 26 | return func(c interface{}) { 27 | c.(*Config).interval = interval 28 | } 29 | } 30 | 31 | // SetDefaultExpire 设置默认的超时时间 32 | func SetDefaultExpire(expire time.Duration) options.Option { 33 | return func(c interface{}) { 34 | c.(*Config).defaultExpire = expire 35 | } 36 | } 37 | 38 | // SetFn 设置周期的执行函数,默认(不设置)是扫描全局清除过期的k 39 | func SetFn(fn func()) options.Option { 40 | return func(c interface{}) { 41 | c.(*Config).fn = fn 42 | } 43 | } 44 | 45 | // SetCapture 设置触发删除后的捕获函数, 数据删除后回调用设置的捕获函数 46 | func SetCapture(capture func(k string, v interface{})) options.Option { 47 | return func(c interface{}) { 48 | c.(*Config).capture = capture 49 | } 50 | } 51 | 52 | // SetMember 设置初始化存储的成员对象 53 | func SetMember(m map[string]Iterator) options.Option { 54 | return func(c interface{}) { 55 | c.(*Config).member = m 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /timeout/datetime.go: -------------------------------------------------------------------------------- 1 | package timeout 2 | 3 | import ( 4 | "database/sql/driver" 5 | "fmt" 6 | "strings" 7 | "time" 8 | ) 9 | 10 | type DateTime time.Time 11 | 12 | const DateTimeFormat = "2006-01-02 15:04:05" 13 | 14 | func (d *DateTime) UnmarshalJSON(src []byte) error { 15 | return d.UnmarshalText(strings.Replace(string(src), "\"", "", -1)) 16 | } 17 | 18 | func (d DateTime) MarshalJSON() ([]byte, error) { 19 | return []byte(`"` + d.String() + `"`), nil 20 | } 21 | 22 | func (d *DateTime) UnmarshalText(value string) error { 23 | dd, err := time.Parse(DateTimeFormat, value) 24 | if err != nil { 25 | return err 26 | } 27 | *d = DateTime(dd) 28 | return nil 29 | } 30 | 31 | func (d DateTime) String() string { 32 | return (time.Time)(d).Format(DateTimeFormat) 33 | } 34 | 35 | func (d *DateTime) Scan(value interface{}) error { 36 | switch v := value.(type) { 37 | case []byte: 38 | return d.UnmarshalText(string(v)) 39 | case string: 40 | return d.UnmarshalText(v) 41 | case time.Time: 42 | *d = DateTime(v) 43 | case nil: 44 | *d = DateTime{} 45 | default: 46 | return fmt.Errorf("cannot sql.Scan() DBDate from: %#v", v) 47 | } 48 | return nil 49 | } 50 | 51 | func (d DateTime) Value() (driver.Value, error) { 52 | return driver.Value(time.Time(d).Format(DateTimeFormat)), nil 53 | } 54 | 55 | func (DateTime) GormDataType() string { 56 | return "DATETIME" 57 | } 58 | -------------------------------------------------------------------------------- /coding/json/json.go: -------------------------------------------------------------------------------- 1 | package json 2 | 3 | import ( 4 | "reflect" 5 | 6 | json "github.com/json-iterator/go" 7 | 8 | "github.com/songzhibin97/gkit/coding" 9 | "google.golang.org/protobuf/encoding/protojson" 10 | "google.golang.org/protobuf/proto" 11 | ) 12 | 13 | const Name = "json" 14 | 15 | var ( 16 | MarshalOptions = protojson.MarshalOptions{ 17 | EmitUnpopulated: true, 18 | UseProtoNames: true, 19 | } 20 | 21 | UnmarshalOptions = protojson.UnmarshalOptions{ 22 | DiscardUnknown: true, 23 | } 24 | ) 25 | 26 | func init() { 27 | _ = coding.RegisterCode(code{}) 28 | } 29 | 30 | type code struct{} 31 | 32 | func (c code) Marshal(v interface{}) ([]byte, error) { 33 | if m, ok := v.(proto.Message); ok { 34 | return MarshalOptions.Marshal(m) 35 | } 36 | return json.Marshal(v) 37 | } 38 | 39 | func (c code) Unmarshal(data []byte, v interface{}) error { 40 | rv := reflect.ValueOf(v) 41 | for rv.Kind() == reflect.Ptr { 42 | if rv.IsNil() { 43 | rv.Set(reflect.New(rv.Type().Elem())) 44 | } 45 | rv = rv.Elem() 46 | } 47 | if m, ok := v.(proto.Message); ok { 48 | return UnmarshalOptions.Unmarshal(data, m) 49 | } else if m, ok := reflect.Indirect(reflect.ValueOf(v)).Interface().(proto.Message); ok { 50 | return UnmarshalOptions.Unmarshal(data, m) 51 | } 52 | return json.Unmarshal(data, v) 53 | } 54 | 55 | func (c code) Name() string { 56 | return Name 57 | } 58 | -------------------------------------------------------------------------------- /structure/skipset/flag_test.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ByteDance Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package skipset 16 | 17 | import ( 18 | "testing" 19 | ) 20 | 21 | func TestFlag(t *testing.T) { 22 | // Correctness. 23 | const ( 24 | f0 = 1 << iota 25 | f1 26 | f2 27 | f3 28 | f4 29 | f5 30 | f6 31 | f7 32 | ) 33 | x := &bitflag{} 34 | 35 | x.SetTrue(f1 | f3) 36 | if x.Get(f0) || !x.Get(f1) || x.Get(f2) || !x.Get(f3) || !x.MGet(f0|f1|f2|f3, f1|f3) { 37 | t.Fatal("invalid") 38 | } 39 | x.SetTrue(f1) 40 | x.SetTrue(f1 | f3) 41 | if x.data != f1+f3 { 42 | t.Fatal("invalid") 43 | } 44 | 45 | x.SetFalse(f1 | f2) 46 | if x.Get(f0) || x.Get(f1) || x.Get(f2) || !x.Get(f3) || !x.MGet(f0|f1|f2|f3, f3) { 47 | t.Fatal("invalid") 48 | } 49 | x.SetFalse(f1 | f2) 50 | if x.data != f3 { 51 | t.Fatal("invalid") 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /container/group/group_test.go: -------------------------------------------------------------------------------- 1 | package group 2 | 3 | import ( 4 | "strconv" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestGroupGet(t *testing.T) { 11 | count := 0 12 | g := NewGroup(func() interface{} { 13 | count++ 14 | return count 15 | }) 16 | v := g.Get("user") 17 | assert.Equal(t, 1, v.(int)) 18 | 19 | v = g.Get("avatar") 20 | assert.Equal(t, 2, v.(int)) 21 | 22 | v = g.Get("user") 23 | assert.Equal(t, 1, v.(int)) 24 | assert.Equal(t, 2, count) 25 | } 26 | 27 | func TestGroupReset(t *testing.T) { 28 | g := NewGroup(func() interface{} { 29 | return 1 30 | }) 31 | g.Get("user") 32 | call := false 33 | g.ReSet(func() interface{} { 34 | call = true 35 | return 1 36 | }) 37 | 38 | length := 0 39 | for range g.(*Group).objs { 40 | length++ 41 | } 42 | 43 | assert.Equal(t, 0, length) 44 | 45 | g.Get("user") 46 | assert.Equal(t, true, call) 47 | } 48 | 49 | func TestGroupClear(t *testing.T) { 50 | g := NewGroup(func() interface{} { 51 | return 1 52 | }) 53 | g.Get("user") 54 | length := 0 55 | for range g.(*Group).objs { 56 | length++ 57 | } 58 | assert.Equal(t, 1, length) 59 | 60 | g.Clear() 61 | length = 0 62 | for range g.(*Group).objs { 63 | length++ 64 | } 65 | assert.Equal(t, 0, length) 66 | } 67 | 68 | func BenchmarkGroupGet(b *testing.B) { 69 | g := NewGroup(func() interface{} { 70 | return 1 71 | }) 72 | for i := 0; i < b.N; i++ { 73 | g.Get(strconv.Itoa(i)) 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /restrictor/ratelimite/ratelimite_test.go: -------------------------------------------------------------------------------- 1 | package ratelimite 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | "time" 7 | 8 | "github.com/juju/ratelimit" 9 | ) 10 | 11 | func TestRateLimit(t *testing.T) { 12 | // 创建指定填充速率和容量大小的令牌桶 13 | // func NewBucket(fillInterval time.Duration, capacity int64) *Bucket 14 | // 创建指定填充速率、容量大小和每次填充的令牌数的令牌桶 15 | // func NewBucketWithQuantum(fillInterval time.Duration, capacity, quantum int64) *Bucket 16 | // 创建填充速度为指定速率和容量大小的令牌桶 17 | // NewBucketWithRate(0.1, 200) 表示每秒填充20个令牌 18 | // func NewBucketWithRate(rate float64, capacity int64) *Bucket 19 | 20 | bucket := ratelimit.NewBucket(time.Second/2, 4) 21 | af, wf := NewRateLimit(bucket) 22 | // 暂停3秒,等待桶满 23 | time.Sleep(3 * time.Second) 24 | for i := 0; i < 10; i++ { 25 | t.Log("i:", i, af.Allow()) 26 | } 27 | 28 | // 暂停3秒,等待桶满 29 | time.Sleep(3 * time.Second) 30 | for i := 0; i < 5; i++ { 31 | t.Log("i:", i, af.AllowN(time.Now(), 2)) 32 | } 33 | 34 | // 暂停3秒,等待桶满 35 | time.Sleep(3 * time.Second) 36 | for i := 0; i < 10; i++ { 37 | func(i int) { 38 | ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) 39 | defer cancel() 40 | t.Log("i:", i, wf.Wait(ctx)) 41 | }(i) 42 | } 43 | // 暂停3秒,等待桶满 44 | time.Sleep(3 * time.Second) 45 | for i := 0; i < 5; i++ { 46 | func(i int) { 47 | ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) 48 | defer cancel() 49 | t.Log("i:", i, wf.WaitN(ctx, 2)) 50 | }(i) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /distributed/schedule/option.go: -------------------------------------------------------------------------------- 1 | package schedule 2 | 3 | import ( 4 | "github.com/songzhibin97/gkit/options" 5 | ) 6 | 7 | type Interval int 8 | 9 | const ( 10 | Second Interval = 1 << iota // Seconds field, default 0 11 | SecondOptional // Optional seconds field, default 0 12 | Minute // Minutes field, default 0 13 | Hour // Hours field, default 0 14 | Dom // Day of month field, default * 15 | Month // Month field, default * 16 | Dow // Day of week field, default * 17 | DowOptional // Optional day of week field, default * 18 | Descriptor // Allow descriptors such as @monthly, @weekly, etc. 19 | ) 20 | 21 | var places = []Interval{ 22 | Second, 23 | Minute, 24 | Hour, 25 | Dom, 26 | Month, 27 | Dow, 28 | } 29 | 30 | var defaults = []string{ 31 | "0", 32 | "0", 33 | "0", 34 | "*", 35 | "*", 36 | "*", 37 | } 38 | 39 | type Config struct { 40 | interval Interval 41 | } 42 | 43 | func WithInterval(options Interval) options.Option { 44 | return func(o interface{}) { 45 | optionals := 0 46 | if options&DowOptional > 0 { 47 | optionals++ 48 | } 49 | if options&SecondOptional > 0 { 50 | optionals++ 51 | } 52 | if optionals > 1 { 53 | panic("multiple optionals may not be configured") 54 | } 55 | o.(*Config).interval = options 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /distributed/backend/backend.go: -------------------------------------------------------------------------------- 1 | package backend 2 | 3 | import "github.com/songzhibin97/gkit/distributed/task" 4 | 5 | type Backend interface { 6 | // GroupTakeOver 组接管任务详情 7 | GroupTakeOver(groupID string, name string, taskIDs ...string) error 8 | 9 | // GroupCompleted 组任务是否完成 10 | GroupCompleted(groupID string) (bool, error) 11 | 12 | // GroupTaskStatus 组任务状态 13 | GroupTaskStatus(groupID string) ([]*task.Status, error) 14 | 15 | // TriggerCompleted 任务全部完成后更改标记位 16 | // TriggerCompleted 是并发安全的,保证只能成功更改一次 17 | TriggerCompleted(groupID string) (bool, error) 18 | 19 | // 设置任务状态 20 | 21 | // SetStatePending 设置任务状态为等待 22 | SetStatePending(signature *task.Signature) error 23 | 24 | // SetStateReceived 设置任务状态为接受 25 | SetStateReceived(signature *task.Signature) error 26 | 27 | // SetStateStarted 设置任务状态为开始 28 | SetStateStarted(signature *task.Signature) error 29 | 30 | // SetStateRetry 设置任务状态为重试 31 | SetStateRetry(signature *task.Signature) error 32 | 33 | // SetStateSuccess 设置任务状态为成功 34 | SetStateSuccess(signature *task.Signature, results []*task.Result) error 35 | 36 | // SetStateFailure 设置任务状态为失败 37 | SetStateFailure(signature *task.Signature, err string) error 38 | 39 | // GetStatus 获取任务状态 40 | GetStatus(taskID string) (*task.Status, error) 41 | 42 | // ResetTask 重置任务状态 43 | ResetTask(taskIDs ...string) error 44 | 45 | // ResetGroup 重置组信息 46 | ResetGroup(groupIDs ...string) error 47 | 48 | // SetResultExpire 设置过期时间 49 | // 在使用controller中接管时候统一设置 50 | SetResultExpire(expire int64) 51 | } 52 | -------------------------------------------------------------------------------- /watching/example/gcheap/gcheap.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "math/rand" 6 | "net/http" 7 | "time" 8 | 9 | "github.com/songzhibin97/gkit/watching" 10 | ) 11 | 12 | func init() { 13 | http.HandleFunc("/rand", randAlloc) 14 | http.HandleFunc("/spike", spikeAlloc) 15 | go http.ListenAndServe(":10024", nil) 16 | } 17 | 18 | func main() { 19 | w := watching.NewWatching( 20 | watching.WithCoolDown("10s"), 21 | watching.WithDumpPath("./tmp"), 22 | watching.WithBinaryDump(), 23 | watching.WithMemoryLimit(100*1024*1024), // 100MB 24 | watching.WithGCHeapDump(10, 20, 40), 25 | ) 26 | w.EnableGCHeapDump().Start() 27 | time.Sleep(time.Hour) 28 | } 29 | 30 | var base = make([]byte, 1024*1024*10) // 10 MB long live memory. 31 | 32 | func randAlloc(wr http.ResponseWriter, req *http.Request) { 33 | s := make([][]byte, 0) // short live 34 | for i := 0; i < 1024; i++ { 35 | len := rand.Intn(1024) 36 | bytes := make([]byte, len) 37 | 38 | s = append(s, bytes) 39 | 40 | if len == 0 { 41 | s = make([][]byte, 0) 42 | } 43 | } 44 | time.Sleep(time.Millisecond * 10) 45 | fmt.Fprintf(wr, "slice current length: %v\n", len(s)) 46 | } 47 | 48 | func spikeAlloc(wr http.ResponseWriter, req *http.Request) { 49 | s := make([][]byte, 0, 1024) // spike, 10MB 50 | for i := 0; i < 10; i++ { 51 | bytes := make([]byte, 1024*1024) 52 | s = append(s, bytes) 53 | } 54 | // live for a while 55 | time.Sleep(time.Millisecond * 500) 56 | fmt.Fprintf(wr, "spike slice length: %v\n", len(s)) 57 | } 58 | -------------------------------------------------------------------------------- /container/pool/example_test.go: -------------------------------------------------------------------------------- 1 | package pool 2 | 3 | import ( 4 | "context" 5 | "time" 6 | ) 7 | 8 | var pool Pool 9 | 10 | type mock map[string]string 11 | 12 | func (m *mock) Shutdown() error { 13 | return nil 14 | } 15 | 16 | // getResources: 获取资源,返回的资源对象需要实现 IShutdown 接口,用于资源回收 17 | func getResources(c context.Context) (IShutdown, error) { 18 | return &mock{}, nil 19 | } 20 | 21 | func ExampleNewList() { 22 | // NewList(options ...) 23 | // 默认配置 24 | // pool = NewList() 25 | 26 | // 可供选择配置选项 27 | 28 | // 设置 Pool 连接数, 如果 == 0 则无限制 29 | // SetActive(100) 30 | 31 | // 设置最大空闲连接数 32 | // SetIdle(20) 33 | 34 | // 设置空闲等待时间 35 | // SetIdleTimeout(time.Second) 36 | 37 | // 设置期望等待 38 | // SetWait(false,time.Second) 39 | 40 | // 自定义配置 41 | pool = NewList( 42 | SetActive(100), 43 | SetIdle(20), 44 | SetIdleTimeout(time.Second), 45 | SetWait(false, time.Second)) 46 | 47 | // New需要实例化,否则在 pool.Get() 会无法获取到资源 48 | pool.New(getResources) 49 | } 50 | 51 | func ExampleList_Get() { 52 | v, err := pool.Get(context.TODO()) 53 | if err != nil { 54 | // 处理错误 55 | } 56 | // v 获取到的资源 57 | _ = v 58 | } 59 | 60 | func ExampleList_Put() { 61 | v, err := pool.Get(context.TODO()) 62 | if err != nil { 63 | // 处理错误 64 | } 65 | 66 | // Put: 资源回收 67 | // forceClose: true 内部帮你调用 Shutdown回收, 否则判断是否是可回收,挂载到list上 68 | err = pool.Put(context.TODO(), v, false) 69 | if err != nil { 70 | // 处理错误 71 | } 72 | } 73 | 74 | func ExampleList_Shutdown() { 75 | // Shutdown 回收资源,关闭所有资源 76 | _ = pool.Shutdown() 77 | } 78 | -------------------------------------------------------------------------------- /encrypt/aes/aes_test.go: -------------------------------------------------------------------------------- 1 | package aes 2 | 3 | import "testing" 4 | 5 | func TestPadKeyToLength(t *testing.T) { 6 | tests := []struct { 7 | name string 8 | input string 9 | targetLength int 10 | expected string 11 | }{ 12 | { 13 | name: "Empty input with defaultKey", 14 | input: "", 15 | targetLength: 8, 16 | expected: "gkitgkit", // Assuming defaultKey is defined globally 17 | }, 18 | { 19 | name: "Input shorter than target length", 20 | input: "abc", 21 | targetLength: 8, 22 | expected: "abcabcab", 23 | }, 24 | { 25 | name: "Input equal to target length", 26 | input: "abcdefgh", 27 | targetLength: 8, 28 | expected: "abcdefgh", 29 | }, 30 | { 31 | name: "Input longer than target length", 32 | input: "abcdefghijklmnopqrstuvwxyz", 33 | targetLength: 8, 34 | expected: "abcdefgh", 35 | }, 36 | { 37 | name: "Empty input with zero target length", 38 | input: "", 39 | targetLength: 0, 40 | expected: "", 41 | }, 42 | { 43 | name: "Non-empty input with zero target length", 44 | input: "abc", 45 | targetLength: 0, 46 | expected: "", 47 | }, 48 | } 49 | 50 | for _, tt := range tests { 51 | t.Run(tt.name, func(t *testing.T) { 52 | result := PadKeyToLength(tt.input, tt.targetLength) 53 | if result != tt.expected { 54 | t.Errorf("expected %q, got %q", tt.expected, result) 55 | } 56 | }) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /internal/benchmark/linkedq/linkedq.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ByteDance Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package linkedq 16 | 17 | import "sync" 18 | 19 | type LinkedQueue struct { 20 | head *linkedqueueNode 21 | tail *linkedqueueNode 22 | mu sync.Mutex 23 | } 24 | 25 | type linkedqueueNode struct { 26 | value uint64 27 | next *linkedqueueNode 28 | } 29 | 30 | func New() *LinkedQueue { 31 | node := new(linkedqueueNode) 32 | return &LinkedQueue{head: node, tail: node} 33 | } 34 | 35 | func (q *LinkedQueue) Enqueue(value uint64) bool { 36 | q.mu.Lock() 37 | q.tail.next = &linkedqueueNode{value: value} 38 | q.tail = q.tail.next 39 | q.mu.Unlock() 40 | return true 41 | } 42 | 43 | func (q *LinkedQueue) Dequeue() (uint64, bool) { 44 | q.mu.Lock() 45 | if q.head.next == nil { 46 | q.mu.Unlock() 47 | return 0, false 48 | } else { 49 | value := q.head.next.value 50 | q.head = q.head.next 51 | q.mu.Unlock() 52 | return value, true 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /internal/stat/rolling_counter.go: -------------------------------------------------------------------------------- 1 | package stat 2 | 3 | import ( 4 | "fmt" 5 | "time" 6 | ) 7 | 8 | // RollingCounter 滚动窗口接口 9 | type RollingCounter interface { 10 | Metric 11 | Aggregation 12 | Timespan() int 13 | // Reduce 将缩减功能应用于窗口内的所有存储桶。 14 | Reduce(func(Iterator) float64) float64 15 | } 16 | 17 | // rollingCounter: 实现接口 RollingCounter 18 | type rollingCounter struct { 19 | policy *RollingPolicy 20 | } 21 | 22 | func (r *rollingCounter) Add(val int64) { 23 | if val < 0 { 24 | panic(fmt.Errorf("stat/metric: cannot decrease in value. val: %d", val)) 25 | } 26 | r.policy.Add(float64(val)) 27 | } 28 | 29 | func (r *rollingCounter) Reduce(f func(Iterator) float64) float64 { 30 | return r.policy.Reduce(f) 31 | } 32 | 33 | func (r *rollingCounter) Avg() float64 { 34 | return r.policy.Reduce(Avg) 35 | } 36 | 37 | func (r *rollingCounter) Min() float64 { 38 | return r.policy.Reduce(Min) 39 | } 40 | 41 | func (r *rollingCounter) Max() float64 { 42 | return r.policy.Reduce(Max) 43 | } 44 | 45 | func (r *rollingCounter) Sum() float64 { 46 | return r.policy.Reduce(Sum) 47 | } 48 | 49 | func (r *rollingCounter) Value() int64 { 50 | return int64(r.Sum()) 51 | } 52 | 53 | func (r *rollingCounter) Timespan() int { 54 | return r.policy.timespan() 55 | } 56 | 57 | // NewRollingCounter 实例化 RollingCounter 方法 58 | func NewRollingCounter(size int, bucketDuration time.Duration) RollingCounter { 59 | window := NewWindow(size) 60 | policy := NewRollingPolicy(window, bucketDuration) 61 | return &rollingCounter{ 62 | policy: policy, 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /trace/middle.go: -------------------------------------------------------------------------------- 1 | package trace 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/songzhibin97/gkit/middleware" 7 | "github.com/songzhibin97/gkit/options" 8 | "go.opentelemetry.io/otel/trace" 9 | ) 10 | 11 | // WithServer returns a new server middleware for OpenTelemetry. 12 | func WithServer(opts ...options.Option) middleware.MiddleWare { 13 | tracer := NewTracer(trace.SpanKindServer, opts...) 14 | return func(handler middleware.Endpoint) middleware.Endpoint { 15 | return func(ctx context.Context, req interface{}) (reply interface{}, err error) { 16 | if tr, ok := FromServerTransportContext(ctx); ok { 17 | var span trace.Span 18 | ctx, span = tracer.Start(ctx, tr.Operation(), tr.RequestHeader()) 19 | setServerSpan(ctx, span, req) 20 | defer func() { tracer.End(ctx, span, reply, err) }() 21 | } 22 | return handler(ctx, req) 23 | } 24 | } 25 | } 26 | 27 | // WithClient returns a new client middleware for OpenTelemetry. 28 | func WithClient(opts ...options.Option) middleware.MiddleWare { 29 | tracer := NewTracer(trace.SpanKindClient, opts...) 30 | return func(handler middleware.Endpoint) middleware.Endpoint { 31 | return func(ctx context.Context, req interface{}) (reply interface{}, err error) { 32 | if tr, ok := FromClientTransportContext(ctx); ok { 33 | var span trace.Span 34 | ctx, span = tracer.Start(ctx, tr.Operation(), tr.RequestHeader()) 35 | setClientSpan(ctx, span, req) 36 | defer func() { tracer.End(ctx, span, reply, err) }() 37 | } 38 | return handler(ctx, req) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tools/bind/json.go: -------------------------------------------------------------------------------- 1 | package bind 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io" 7 | "net/http" 8 | 9 | "github.com/songzhibin97/gkit/tools/bind/internal/json" 10 | ) 11 | 12 | // EnableDecoderUseNumber is used to call the UseNumber method on the JSON 13 | // Decoder instance. UseNumber causes the Decoder to unmarshal a number into an 14 | // interface{} as a Number instead of as a float64. 15 | var EnableDecoderUseNumber = false 16 | 17 | // EnableDecoderDisallowUnknownFields is used to call the DisallowUnknownFields method 18 | // on the JSON Decoder instance. DisallowUnknownFields causes the Decoder to 19 | // return an error when the destination is a struct and the input contains object 20 | // keys which do not match any non-ignored, exported fields in the destination. 21 | var EnableDecoderDisallowUnknownFields = false 22 | 23 | type jsonBinding struct{} 24 | 25 | func (jsonBinding) Name() string { 26 | return "json" 27 | } 28 | 29 | func (jsonBinding) Bind(req *http.Request, obj interface{}) error { 30 | if req == nil || req.Body == nil { 31 | return fmt.Errorf("invalid request") 32 | } 33 | return decodeJSON(req.Body, obj) 34 | } 35 | 36 | func (jsonBinding) BindBody(body []byte, obj interface{}) error { 37 | return decodeJSON(bytes.NewReader(body), obj) 38 | } 39 | 40 | func decodeJSON(r io.Reader, obj interface{}) error { 41 | decoder := json.NewDecoder(r) 42 | if EnableDecoderUseNumber { 43 | decoder.UseNumber() 44 | } 45 | if EnableDecoderDisallowUnknownFields { 46 | decoder.DisallowUnknownFields() 47 | } 48 | return decoder.Decode(obj) 49 | } 50 | -------------------------------------------------------------------------------- /internal/stat/window_test.go: -------------------------------------------------------------------------------- 1 | package stat 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/stretchr/testify/assert" 7 | ) 8 | 9 | func TestWindowResetWindow(t *testing.T) { 10 | window := NewWindow(3) 11 | for i := 0; i < 3; i++ { 12 | window.Append(i, 1.0) 13 | } 14 | window.ResetWindow() 15 | for i := 0; i < 3; i++ { 16 | assert.Equal(t, len(window.Bucket(i).Points), 0) 17 | } 18 | } 19 | 20 | func TestWindowResetBucket(t *testing.T) { 21 | window := NewWindow(3) 22 | for i := 0; i < 3; i++ { 23 | window.Append(i, 1.0) 24 | } 25 | window.ResetBucket(1) 26 | assert.Equal(t, len(window.Bucket(1).Points), 0) 27 | assert.Equal(t, window.Bucket(0).Points[0], 1.0) 28 | assert.Equal(t, window.Bucket(2).Points[0], 1.0) 29 | } 30 | 31 | func TestWindowResetBuckets(t *testing.T) { 32 | window := NewWindow(3) 33 | for i := 0; i < 3; i++ { 34 | window.Append(i, 1.0) 35 | } 36 | window.ResetBuckets([]int{0, 1, 2}) 37 | for i := 0; i < 3; i++ { 38 | assert.Equal(t, len(window.Bucket(i).Points), 0) 39 | } 40 | } 41 | 42 | func TestWindowAppend(t *testing.T) { 43 | window := NewWindow(3) 44 | for i := 0; i < 3; i++ { 45 | window.Append(i, 1.0) 46 | } 47 | for i := 0; i < 3; i++ { 48 | assert.Equal(t, window.Bucket(i).Points[0], float64(1.0)) 49 | } 50 | } 51 | 52 | func TestWindowAdd(t *testing.T) { 53 | window := NewWindow(3) 54 | window.Append(0, 1.0) 55 | window.Add(0, 1.0) 56 | assert.Equal(t, window.Bucket(0).Points[0], 2.0) 57 | } 58 | 59 | func TestWindowSize(t *testing.T) { 60 | window := NewWindow(3) 61 | assert.Equal(t, window.Size(), 3) 62 | } 63 | -------------------------------------------------------------------------------- /distributed/task/result_test.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestConvertResult(t *testing.T) { 9 | type args struct { 10 | result []*Result 11 | } 12 | tests := []struct { 13 | name string 14 | args args 15 | want []reflect.Value 16 | wantErr bool 17 | }{ 18 | { 19 | name: "string", 20 | args: args{result: []*Result{{ 21 | Type: "string", 22 | Value: "gkit", 23 | }}}, 24 | want: []reflect.Value{reflect.ValueOf("gkit")}, 25 | wantErr: false, 26 | }, 27 | { 28 | name: "mix", 29 | args: args{result: []*Result{{ 30 | Type: "string", 31 | Value: "gkit", 32 | }, { 33 | Type: "int", 34 | Value: 1, 35 | }, { 36 | Type: "[]string", 37 | Value: []string{"6", "66", "666"}, 38 | }}}, 39 | want: []reflect.Value{reflect.ValueOf("gkit"), reflect.ValueOf(int(1)), reflect.ValueOf([]string{"6", "66", "666"})}, 40 | }, 41 | } 42 | for _, tt := range tests { 43 | t.Run(tt.name, func(t *testing.T) { 44 | got, err := ConvertResult(tt.args.result) 45 | if (err != nil) != tt.wantErr { 46 | t.Errorf("ConvertResult() error = %v, wantErr %v", err, tt.wantErr) 47 | return 48 | } 49 | if len(got) != len(tt.want) { 50 | t.Errorf("got len no equal want: got len = %d want len = %d", len(got), len(tt.want)) 51 | 52 | } 53 | for i := range got { 54 | if !reflect.DeepEqual(got[i].Interface(), tt.want[i].Interface()) { 55 | t.Errorf("ConvertResult() got = %v, want %v", got, tt.want) 56 | } 57 | } 58 | t.Log(FormatResult(got)) 59 | }) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /structure/skipset/oparry.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ByteDance Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package skipset 16 | 17 | import ( 18 | "sync/atomic" 19 | "unsafe" 20 | ) 21 | 22 | const ( 23 | op1 = 4 24 | op2 = maxLevel - op1 25 | ) 26 | 27 | type optionalArray struct { 28 | base [op1]unsafe.Pointer 29 | extra *([op2]unsafe.Pointer) 30 | } 31 | 32 | func (a *optionalArray) load(i int) unsafe.Pointer { 33 | if i < op1 { 34 | return a.base[i] 35 | } 36 | return a.extra[i-op1] 37 | } 38 | 39 | func (a *optionalArray) store(i int, p unsafe.Pointer) { 40 | if i < op1 { 41 | a.base[i] = p 42 | return 43 | } 44 | a.extra[i-op1] = p 45 | } 46 | 47 | func (a *optionalArray) atomicLoad(i int) unsafe.Pointer { 48 | if i < op1 { 49 | return atomic.LoadPointer(&a.base[i]) 50 | } 51 | return atomic.LoadPointer(&a.extra[i-op1]) 52 | } 53 | 54 | func (a *optionalArray) atomicStore(i int, p unsafe.Pointer) { 55 | if i < op1 { 56 | atomic.StorePointer(&a.base[i], p) 57 | return 58 | } 59 | atomic.StorePointer(&a.extra[i-op1], p) 60 | } 61 | -------------------------------------------------------------------------------- /structure/skipmap/oparray.go: -------------------------------------------------------------------------------- 1 | // Copyright 2021 ByteDance Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package skipmap 16 | 17 | import ( 18 | "sync/atomic" 19 | "unsafe" 20 | ) 21 | 22 | const ( 23 | op1 = 4 24 | op2 = maxLevel - op1 25 | ) 26 | 27 | type optionalArray struct { 28 | base [op1]unsafe.Pointer 29 | extra *([op2]unsafe.Pointer) 30 | } 31 | 32 | func (a *optionalArray) load(i int) unsafe.Pointer { 33 | if i < op1 { 34 | return a.base[i] 35 | } 36 | return a.extra[i-op1] 37 | } 38 | 39 | func (a *optionalArray) store(i int, p unsafe.Pointer) { 40 | if i < op1 { 41 | a.base[i] = p 42 | return 43 | } 44 | a.extra[i-op1] = p 45 | } 46 | 47 | func (a *optionalArray) atomicLoad(i int) unsafe.Pointer { 48 | if i < op1 { 49 | return atomic.LoadPointer(&a.base[i]) 50 | } 51 | return atomic.LoadPointer(&a.extra[i-op1]) 52 | } 53 | 54 | func (a *optionalArray) atomicStore(i int, p unsafe.Pointer) { 55 | if i < op1 { 56 | atomic.StorePointer(&a.base[i], p) 57 | return 58 | } 59 | atomic.StorePointer(&a.extra[i-op1], p) 60 | } 61 | -------------------------------------------------------------------------------- /internal/stat/reduce.go: -------------------------------------------------------------------------------- 1 | package stat 2 | 3 | // Sum 返回窗口所有值之和 4 | func Sum(iterator Iterator) float64 { 5 | result := 0.0 6 | for iterator.Next() { 7 | bucket := iterator.Bucket() 8 | for _, p := range bucket.Points { 9 | result = result + p 10 | } 11 | } 12 | return result 13 | } 14 | 15 | // Avg 返回窗口平均值 16 | func Avg(iterator Iterator) float64 { 17 | result := 0.0 18 | count := 0.0 19 | for iterator.Next() { 20 | bucket := iterator.Bucket() 21 | for _, p := range bucket.Points { 22 | result = result + p 23 | count = count + 1 24 | } 25 | } 26 | return result / count 27 | } 28 | 29 | // Min 返回窗口最小值 30 | func Min(iterator Iterator) float64 { 31 | result := 0.0 32 | started := false 33 | for iterator.Next() { 34 | bucket := iterator.Bucket() 35 | for _, p := range bucket.Points { 36 | if !started { 37 | result = p 38 | started = true 39 | continue 40 | } 41 | if p < result { 42 | result = p 43 | } 44 | } 45 | } 46 | return result 47 | } 48 | 49 | // Max 返回窗口最大值 50 | func Max(iterator Iterator) float64 { 51 | result := 0.0 52 | started := false 53 | for iterator.Next() { 54 | bucket := iterator.Bucket() 55 | for _, p := range bucket.Points { 56 | if !started { 57 | result = p 58 | started = true 59 | continue 60 | } 61 | if p > result { 62 | result = p 63 | } 64 | } 65 | } 66 | return result 67 | } 68 | 69 | // Count 返回窗口中的 key 70 | func Count(iterator Iterator) float64 { 71 | var result int64 72 | for iterator.Next() { 73 | bucket := iterator.Bucket() 74 | result += bucket.Count 75 | } 76 | return float64(result) 77 | } 78 | -------------------------------------------------------------------------------- /egroup/example_test.go: -------------------------------------------------------------------------------- 1 | package egroup 2 | 3 | import ( 4 | "context" 5 | "os" 6 | "syscall" 7 | "time" 8 | ) 9 | 10 | var admin *LifeAdmin 11 | 12 | func mockStart() func(ctx context.Context) error { 13 | return nil 14 | } 15 | 16 | func mockShutdown() func(ctx context.Context) error { 17 | return nil 18 | } 19 | 20 | type mockLifeAdminer struct{} 21 | 22 | func (m *mockLifeAdminer) Start(ctx context.Context) error { 23 | return nil 24 | } 25 | 26 | func (m *mockLifeAdminer) Shutdown(ctx context.Context) error { 27 | return nil 28 | } 29 | 30 | func ExampleNewLifeAdmin() { 31 | // 默认配置 32 | // admin = NewLifeAdmin() 33 | 34 | // 可供选择配置选项 35 | 36 | // 设置启动超时时间 37 | // <=0 不启动超时时间,注意要在shutdown处理关闭通知 38 | // SetStartTimeout(time.Second) 39 | 40 | // 设置关闭超时时间 41 | // <=0 不启动超时时间 42 | // SetStopTimeout(time.Second) 43 | 44 | // 设置信号集合,和处理信号的函数 45 | //SetSignal(func(lifeAdmin *LifeAdmin, signal os.Signal) { 46 | // return 47 | //}, signal...) 48 | 49 | admin = NewLifeAdmin(SetStartTimeout(time.Second), SetStopTimeout(time.Second), SetSignal(func(a *LifeAdmin, signal os.Signal) { 50 | switch signal { 51 | case syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT: 52 | a.shutdown() 53 | default: 54 | } 55 | })) 56 | } 57 | 58 | func ExampleLifeAdmin_Add() { 59 | // 通过struct添加 60 | admin.Add(Member{ 61 | Start: mockStart(), 62 | Shutdown: mockShutdown(), 63 | }) 64 | } 65 | 66 | func ExampleLifeAdmin_AddMember() { 67 | // 根据接口适配添加 68 | admin.AddMember(&mockLifeAdminer{}) 69 | } 70 | 71 | func ExampleLifeAdmin_Start() { 72 | defer admin.Shutdown() 73 | if err := admin.Start(); err != nil { 74 | // 处理错误 75 | // 正常启动会hold主 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /distributed/task/workflow.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | // Chain 创建链式调用的任务 4 | type Chain struct { 5 | Name string 6 | Tasks []*Signature 7 | } 8 | 9 | // Group 创建并行执行的任务组 10 | type Group struct { 11 | Name string 12 | GroupID string 13 | Tasks []*Signature 14 | } 15 | 16 | // GroupCallback 具有回调任务的任务组 17 | type GroupCallback struct { 18 | Name string 19 | Group *Group 20 | Callback *Signature 21 | } 22 | 23 | // GetTaskIDs 获取组任务的所有ID 24 | func (g *Group) GetTaskIDs() []string { 25 | ids := make([]string, 0, len(g.Tasks)) 26 | for _, task := range g.Tasks { 27 | ids = append(ids, task.ID) 28 | } 29 | return ids 30 | } 31 | 32 | // NewChain 创建链式调用任务 33 | func NewChain(name string, signatures ...*Signature) (*Chain, error) { 34 | for i := len(signatures) - 1; i > 0; i-- { 35 | if i > 0 { 36 | signatures[i-1].CallbackOnSuccess = []*Signature{signatures[i]} 37 | } 38 | } 39 | return &Chain{Name: name, Tasks: signatures}, nil 40 | } 41 | 42 | // NewGroup 创建并行执行的任务组 43 | func NewGroup(groupID string, name string, signatures ...*Signature) (*Group, error) { 44 | ln := len(signatures) 45 | for i := range signatures { 46 | signatures[i].GroupID = groupID 47 | signatures[i].GroupTaskCount = ln 48 | } 49 | return &Group{ 50 | GroupID: groupID, 51 | Name: name, 52 | Tasks: signatures, 53 | }, nil 54 | } 55 | 56 | // NewGroupCallback 创建具有回调任务的个任务组 57 | func NewGroupCallback(group *Group, name string, callback *Signature) (*GroupCallback, error) { 58 | for _, task := range group.Tasks { 59 | task.CallbackChord = callback 60 | } 61 | return &GroupCallback{ 62 | Group: group, 63 | Name: name, 64 | Callback: callback, 65 | }, nil 66 | } 67 | -------------------------------------------------------------------------------- /tools/stm/stm.go: -------------------------------------------------------------------------------- 1 | package stm 2 | 3 | import ( 4 | "reflect" 5 | "unsafe" 6 | ) 7 | 8 | // struct => map 9 | 10 | func StructToMap(val interface{}, tag string) map[string]interface{} { 11 | return structToMap(val, tag, false) 12 | } 13 | 14 | func StructToMapExtraExport(val interface{}, tag string) map[string]interface{} { 15 | return structToMap(val, tag, true) 16 | } 17 | 18 | func structToMap(val interface{}, tag string, extraExport bool) map[string]interface{} { 19 | t, v := reflect.TypeOf(val), reflect.ValueOf(val) 20 | if v.Kind() == reflect.Ptr { 21 | return structToMap(v.Elem().Interface(), tag, extraExport) 22 | } 23 | if v.Kind() != reflect.Struct { 24 | return nil 25 | } 26 | mp := make(map[string]interface{}) 27 | 28 | for i := 0; i < v.NumField(); i++ { 29 | vField := v.Field(i) 30 | name := t.Field(i).Name 31 | if tag != "" { 32 | ts := t.Field(i).Tag.Get(tag) 33 | if ts == "-" { 34 | continue 35 | } 36 | if ts != "" { 37 | name = ts 38 | } 39 | } 40 | if !vField.IsValid() { 41 | continue 42 | } 43 | st := (vField.Kind() == reflect.Struct) || (vField.Kind() == reflect.Ptr && vField.Elem().Kind() == reflect.Struct) 44 | if vField.CanInterface() { 45 | iv := vField.Interface() 46 | if st { 47 | iv = structToMap(iv, tag, extraExport) 48 | } 49 | mp[name] = iv 50 | } else if extraExport { 51 | // 未导出 52 | cp := reflect.New(v.Type()).Elem() 53 | cp.Set(v) 54 | value := cp.Field(i) 55 | iv := reflect.NewAt(value.Type(), unsafe.Pointer(value.UnsafeAddr())).Elem().Interface() 56 | if st { 57 | iv = structToMap(iv, tag, extraExport) 58 | } 59 | mp[name] = iv 60 | } 61 | } 62 | return mp 63 | } 64 | -------------------------------------------------------------------------------- /downgrade/example_test.go: -------------------------------------------------------------------------------- 1 | package downgrade 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/afex/hystrix-go/hystrix" 7 | ) 8 | 9 | var fuse Fuse 10 | 11 | // type RunFunc = func() error 12 | // type FallbackFunc = func(error) error 13 | // type RunFuncC = func(context.Context) error 14 | // type FallbackFuncC = func(context.Context, error) error 15 | 16 | func mockRunFunc() RunFunc { 17 | return func() error { 18 | return nil 19 | } 20 | } 21 | 22 | func mockFallbackFunc() FallbackFunc { 23 | return func(err error) error { 24 | return nil 25 | } 26 | } 27 | 28 | func mockRunFuncC() RunFuncC { 29 | return func(ctx context.Context) error { 30 | return nil 31 | } 32 | } 33 | 34 | func mockFallbackFuncC() FallbackFuncC { 35 | return func(ctx context.Context, err error) error { 36 | return nil 37 | } 38 | } 39 | 40 | func ExampleNewFuse() { 41 | // 拿到一个熔断器 42 | fuse = NewFuse() 43 | } 44 | 45 | func ExampleHystrix_ConfigureCommand() { 46 | // 不设置 ConfigureCommand 走默认配置 47 | // hystrix.CommandConfig{} 设置参数 48 | fuse.ConfigureCommand("test", hystrix.CommandConfig{}) 49 | } 50 | 51 | func ExampleHystrix_Do() { 52 | // Do: 同步执行 func() error, 没有超时控制 直到等到返回, 53 | // 如果返回 error != nil 则触发 FallbackFunc 进行降级 54 | err := fuse.Do("do", mockRunFunc(), mockFallbackFunc()) 55 | if err != nil { 56 | // 处理 error 57 | } 58 | } 59 | 60 | func ExampleHystrix_Go() { 61 | // Go: 异步执行 返回 channel 62 | ch := fuse.Go("go", mockRunFunc(), mockFallbackFunc()) 63 | if err := <-ch; err != nil { 64 | // 处理 error 65 | } 66 | } 67 | 68 | func ExampleHystrix_GoC() { 69 | // GoC: Do/Go 实际上最终调用的就是GoC, Do主处理了异步过程 70 | // GoC可以传入 context 保证链路超时控制 71 | fuse.GoC(context.TODO(), "goc", mockRunFuncC(), mockFallbackFuncC()) 72 | } 73 | --------------------------------------------------------------------------------