gkit
gkit copied to clipboard
A collection of basic usability component tools dedicated to providing micro-services and single services, drawing on some excellent open source project features such as kratos, go-kit, mosn, sentinel...
GKIT
_____/\\\\\\\\\\\\__/\\\________/\\\__/\\\\\\\\\\\__/\\\\\\\\\\\\\\\_
___/\\\//////////__\/\\\_____/\\\//__\/////\\\///__\///////\\\/////__
__/\\\_____________\/\\\__/\\\//_________\/\\\___________\/\\\_______
_\/\\\____/\\\\\\\_\/\\\\\\//\\\_________\/\\\___________\/\\\_______
_\/\\\___\/////\\\_\/\\\//_\//\\\________\/\\\___________\/\\\_______
_\/\\\_______\/\\\_\/\\\____\//\\\_______\/\\\___________\/\\\_______
_\/\\\_______\/\\\_\/\\\_____\//\\\______\/\\\___________\/\\\_______
_\//\\\\\\\\\\\\/__\/\\\______\//\\\__/\\\\\\\\\\\_______\/\\\_______
__\////////////____\///________\///__\///////////________\///________
项ç®ç®ä»
è´åäºæä¾å¾®æå¡ä»¥ååä½æå¡çå¯ç¨æ§åºç¡ç»ä»¶å·¥å
·éå,åé´äºä¸äºä¼ç§çå¼æºé¡¹ç®ä¾å¦:kratos
ãgo-kit
ãmosn
ãsentinel
ãgopkg
...
å¸æ大家å¤å¤æ¯æ
ç®å½ç»æ
âââ cache (æ建ç¼åç¸å
³ç»ä»¶)
âââ buffer (æä¾byteæ°ç»å¤ç¨ä»¥åio bufferå°è£
)
âââ mbuffer (buffer 类似å®ç°)
âââ local_cache (æä¾æ¬å°key-valueæ建æ¬å°ç¼åçå°è£
å®ç°)
âââ singlefight (æä¾é«å¹¶åæ
åµä¸é²æ¢éå¤ä»»å¡,ä¸è¬ç¨äºcache missåå¡«è¡¥cacheåºæ¯)
âââ coding (æä¾å¯¹è±¡åºåå/ååºååæ¥å£å, æä¾jsonãprotoãxmlãyaml å®ä¾æ¹æ³)
âââ concurrent (å¨å¹¶åä¸ä½¿ç¨channelçæä½³å®è·µ)
âââ fan_in (æå
¥æ¨¡å¼,常ç¨ä¸ç产è
æ¶è´¹è
模åä¸å¤ä¸ªç产è
,ä¸ä¸ªæ¶è´¹è
)
âââ fan_out (æåºæ¨¡å¼,常ç¨ä¸ç产çæ¶è´¹è
模åä¸ä¸ä¸ªç产è
,å¤ä¸ªæ¶è´¹è
)
âââ or_done (并ååºæ¯ä¸ä»»æä¸ä¸ªä»»å¡å®æåç«å³è¿å)
âââ orderly (å¨å¹¶ååºæ¯ä¸ä¹è½ä¿ææåºçå®æè¿å)
âââ map_reduce
âââ stream (æä¾æ°æ®ç产æµå°è£
,以åå¤çæµçå®ç°)
âââ pipeline (并åå为串è¡)
âââ container (容å¨åç»ä»¶,æä¾groupãpoolãqueue)
âââ group (æä¾äºå®¹å¨æå 载模å¼,类似sync.Pool,å¨ä½¿ç¨æ¶ä½¿ç¨keyè·å对åºå®¹å¨å®ä¾,å¦æä¸åå¨åè¿è¡çæ)
âââ pool (æä¾äºpoolçå°è£
æ½è±¡,以å使ç¨list对æ¥å£çå®ç°)
âââ queue
âââ codel (对åå®ç°å¯æ§å¶å»¶æ¶ç®æ³,对积åä»»å¡å®ç°å¶è£)
âââ delayed (延æ¶ä»»å¡-åæºç)
âââ distributed (åå¸å¼ä»»å¡,æä¾äºæ ååæ¥å£ä»¥åredisãmysqlãpgsqlãmongodb对åºçå®ç°)
âââ downgrade (çæé级ç¸å
³ç»ä»¶)
âââ egroup (errgroup,æ§å¶ç»ä»¶çå½å¨æ)
âââ errors (grpc errorå¤ç)
âââ gctuner (go1.19åä¼ågcå©å¨)
âââ generator (åå·å¨,snowflake)
âââ goroutine (æä¾goroutineæ± ,æ§å¶goroutineæ°éæ¿å¢)
âââ log (æ¥å£åæ¥å¿,使ç¨æ¥å¿ç»ä»¶æ¥å
¥)
âââ metrics (ææ æ¥å£å)
âââ middleware (ä¸é´ä»¶æ¥å£æ¨¡åå®ä¹)
âââ net (ç½ç»ç¸å
³å°è£
)
âââ tcp
âââ options (é项模å¼æ¥å£å)
âââ overload (æå¡å¨èªéåºä¿æ¤,æä¾bbræ¥å£,çæ§é¨ç½²æå¡å¨ç¶æéæ©æµéæ¾è¡,ä¿æ¤æå¡å¨å¯ç¨æ§)
âââ bbr (èªéåºéæµ)
âââ parser (æ件解æ,proto<->goç¸äºè§£æ)
âââ parseGo (解ægoçæpb)
âââ parsePb (解æpbçægo)
âââ registry (æå¡åç°æ¥å£åãgoogle sre subsetå®ç°)
âââ restrictor (éæµ,æä¾ä»¤ç桶åæ¼æ¡¶æ¥å£å°è£
)
âââ client_throttling (客æ·ç«¯èæµ)
âââ rate
âââ ratelimite
âââ structure (常ç¨æ°æ®ç»æ)
âââ hashset (åå¸è¡¨)
âââ lscq (æ éæ è¾¹çéå,æ¯æarm)
âââ skipmap (跳表)
âââ skipset
âââ zset
âââ sync
âââ cpu (è·åLinuxå¹³å°ä¸çç³»ç»ä¿¡æ¯,å
æ¬cpu主é¢ãcpu使ç¨çç)
âââ fastrand (éæºæ°)
âââ goid (è·ågoroutine id)
âââ mutex (æä¾trylockãéå
¥éåtokenéå
¥é)
âââ nanotime (æ¶é´æ³ä¼å)
âââ once (once æ´å¼ºå¤§çå®ç°,设置onceå½æ°å¢å è¿åerror,失败åå¯éè¯)
âââ queue (æ ééå)
âââ stringx (string å¢å¼ºç)
âââ syncx (sync å¢å¼ºç)
âââ xxhash3
âââ ternary (ä¸å
表达å¼)
âââ timeout (è¶
æ¶æ§å¶,å
¨é¾è·¯ä¿æ¤ãæä¾ä¸äºæ°æ®åºå¤çæ¶é´çå°è£
å®ç°)
âââ ctime (é¾è·¯è¶
æ¶æ§å¶)
âââ c_json (éé
æ°æ®åºjsonç±»å)
âââ d_time (éé
æ°æ®åº åªåå¨æ¶é´)
âââ date (éé
æ°æ®åº åªåå¨æ¥æ)
âââ date_struct (éé
æ°æ®åº åªåå¨æ¥æ)
âââ datetime (éé
æ°æ®åº åå¨datetime)
âââ datetime_struct (éé
æ°æ®åº åå¨datetime)
âââ stamp (éé
æ°æ®åº åå¨æ¶é´æ³)
âââ human (æä¾å¯è§åæ¶é´é´è·)
âââ tools
âââ bind (ç»å®å·¥å
·,常ç¨ä¸ginæ¡æ¶ä¸èªå®ä¹ç»å®æ°æ®,ä¾å¦åæ¶ç»å®queryåjson)
âââ deepcopy (æ·±æ·è´)
âââ float (æµ®ç¹æ°æªæå·¥å
·)
âââ match (åºç¡å¹é
å¨,æ ¹æ®éé
符å¹é
)
âââ pointer (æéå·¥å
·)
âââ pretty (æ ¼å¼åjson)
âââ reflect2value (åºç¡å段æ å°)
âââ rand_string (éæºå符串)
âââ vto (å
·æç¸åç±»åçå½æ°èµå¼,解æ¾åæ,é常ç¨äºvo->do对象转æ¢)
âââ æ°å¢plus æ¯æå段,tag以åé»è®¤å¼ç»å®
âââ trace (é¾è·¯è¿½è¸ª)
âââ watching (çæ§cpuãmumãgcãgoroutineçææ ä¿¡æ¯,å¨æ³¢å¨çæ
åµä¸èªå¨dump pprofææ )
âââ window (æ»å¨çªå£,æ¯æå¤æ°æ®ç±»åææ çªå£æ¶é)
ä¸è½½ä½¿ç¨
# go get github.com/songzhibin97/gkit@master
go get github.com/songzhibin97/gkit
ç»ä»¶ä½¿ç¨ä»ç»
cache
ç¼åç¸å ³ç»ä»¶
buffer pool
package main
import (
"fmt"
"github.com/songzhibin97/gkit/cache/buffer"
)
func main() {
// Byteå¤ç¨
// size 2^6 - 2^18
// è¿ååä¸åæ´ç 2çæ´æ°å cap, len == size
// å
¶ä»ç¹æ®çæè
å¨è¿è¡æé´æ©å®¹ç å°ä¼è¢«æ¸
空
slice := buffer.GetBytes(1024)
fmt.Println(len(*slice), cap(*slice)) // 1024 1024
// åæ¶
// 注æ: åæ¶ä»¥åä¸å¯å¨å¼ç¨
buffer.PutBytes(slice)
// IOByte å¤ç¨
// io buffer.IoBuffer interface
io := buffer.GetIoPool(1024)
// å¦æä¸ä¸ªå¯¹è±¡å·²ç»è¢«åæ¶äº,å次å¼ç¨è¢«åæ¶ç对象ä¼è§¦åé误
err := buffer.PutIoPool(io)
if err != nil {
// å¤çé误
}
}
local_cache
package local_cache
import (
"github.com/songzhibin97/gkit/cache/buffer"
"log"
)
var ch Cache
func ExampleNewCache() {
// é»è®¤é
ç½®
//ch = NewCache()
// å¯ä¾éæ©çé
ç½®é项
// 设置é´éæ¶é´
// SetInternal(interval time.Duration)
// 设置é»è®¤çè¶
æ¶æ¶é´
// SetDefaultExpire(expire time.Duration)
// 设置å¨æçæ§è¡å½æ°,é»è®¤(ä¸è®¾ç½®)æ¯æ«æå
¨å±æ¸
é¤è¿æçk
// SetFn(fn func())
// 设置触åå é¤åçæè·å½æ°, æ°æ®å é¤ååè°ç¨è®¾ç½®çæè·å½æ°
// SetCapture(capture func(k string, v interface{}))
// 设置åå§ååå¨çæå对象
// SetMember(m map[string]Iterator)
ch = NewCache(SetInternal(1000),
SetDefaultExpire(10000),
SetCapture(func(k string, v interface{}) {
log.Println(k, v)
}))
}
func ExampleCacheStorage() {
// Set æ·»å cache æ 论æ¯å¦åå¨é½ä¼è¦ç
ch.Set("k1", "v1", DefaultExpire)
// SetDefault æ 论æ¯å¦åå¨é½ä¼è¦ç
// åå½æ°æ¨¡å¼,é»è®¤ä¼ å
¥è¶
æ¶æ¶é´ä¸ºå建cacheçé»è®¤æ¶é´
ch.SetDefault("k1", 1)
// SetNoExpire
// åå½æ°æ¨¡å¼,é»è®¤ä¼ å
¥è¶
æ¶æ¶é´ä¸ºæ°¸ä¸è¿æ
ch.SetNoExpire("k1", 1.1)
// Add æ·»å cache å¦æåå¨çè¯ä¼æåºå¼å¸¸
err := ch.Add("k1", nil, DefaultExpire)
CacheErrExist(err) // true
// Replace å¦ææ就设置没æå°±æåºé误
err = ch.Replace("k2", make(chan struct{}), DefaultExpire)
CacheErrNoExist(err) // true
}
func ExampleGet() {
// Get æ ¹æ®keyè·å cache ä¿è¯æææå
çkv被ååº
v, ok := ch.Get("k1")
if !ok {
// v == nil
}
_ = v
// GetWithExpire æ ¹æ®keyè·å cache 并带åºè¶
æ¶æ¶é´
v, t, ok := ch.GetWithExpire("k1")
if !ok {
// v == nil
}
// å¦æè¶
æ¶æ¶é´æ¯ NoExpire t.IsZero() == true
if t.IsZero() {
// 没æ设置è¶
æ¶æ¶é´
}
// Iterator è¿å cache ä¸ææææç对象
mp := ch.Iterator()
for s, iterator := range mp {
log.Println(s, iterator)
}
// Count è¿åmemberæ°é
log.Println(ch.Count())
}
func ExampleIncrement() {
ch.Set("k3", 1, DefaultExpire)
ch.Set("k4", 1.1, DefaultExpire)
// Increment 为k对åºçvalueå¢å n nå¿
须为æ°åç±»å
err := ch.Increment("k3", 1)
if CacheErrExpire(err) || CacheErrExist(CacheTypeErr) {
// æªè®¾ç½®æå
}
_ = ch.IncrementFloat("k4", 1.1)
// å¦æä½ ç¥é设置çkçå
·ä½ç±»å è¿å¯ä»¥ä½¿ç¨ç±»åç¡®å®ç incrementå½æ°
// ch.IncrementInt(k string, v int)
// ...
// ch.IncrementFloat32(k string, v flot32)
// ...
// Decrement åç
}
func ExampleDelete() {
// Delete å¦æè®¾ç½®äº capture ä¼è§¦åä¸æå½æ°
ch.Delete("k1")
// DeleteExpire å é¤ææè¿æäºçkey, é»è®¤ç capture å°±æ¯æ§è¡ DeleteExpire()
ch.DeleteExpire()
}
func ExampleChangeCapture() {
// æä¾äºå¨è¿è¡ä¸æ¹åæè·å½æ°çæ¹æ³
// ChangeCapture
ch.ChangeCapture(func(k string, v interface{}) {
log.Println(k, v)
})
}
func ExampleSaveLoad() {
// åå
¥æ件éç¨goç¬æçgobåè®®
io := buffer.NewIoBuffer(1000)
// Save ä¼ å
¥ä¸ä¸ª w io.Writer åæ° å° cacheä¸ç member æååå
¥wä¸
_ = ch.Save(io)
// SaveFile ä¼ å
¥path åå°æ件ä¸
_ = ch.SaveFile("path")
// Load ä¼ å
¥ä¸ä¸ª r io.Reader对象 ä» rä¸è¯»åååå° memberä¸
_ = ch.Load(io)
// LoadFile ä¼ å
¥path 读åæ件å
容
_ = ch.LoadFile("path")
}
func ExampleFlush() {
// Flush éæ¾memberæå
ch.Flush()
}
func ExampleShutdown() {
// Shutdown éæ¾å¯¹è±¡
ch.Shutdown()
}
singleflight
å½å¹¶åæº
package main
import (
"github.com/songzhibin97/gkit/cache/singleflight"
)
// getResources: ä¸è¬ç¨äºå»æ°æ®åºå»è·åæ°æ®
func getResources() (interface{}, error) {
return "test", nil
}
// cache: å¡«å
å° ç¼åä¸çæ°æ®
func cache(v interface{}) {
return
}
func main() {
f := singleflight.NewSingleFlight()
// åæ¥:
v, err, _ := f.Do("test1", func() (interface{}, error) {
// è·åèµæº
return getResources()
})
if err != nil {
// å¤çé误
}
// åå¨å°buffer
// vå°±æ¯è·åå°çèµæº
cache(v)
// å¼æ¥
ch := f.DoChan("test2", func() (interface{}, error) {
// è·åèµæº
return getResources()
})
// çå¾
è·åèµæºå®æå,ä¼å°ç»æéè¿channelè¿å
result := <-ch
if result.Err != nil {
// å¤çé误
}
// åå¨å°buffer
// result.Valå°±æ¯è·åå°çèµæº
cache(result.Val)
// å°½ååæ¶
f.Forget("test2")
}
coding
对象åºååååºååæ¥å£ä»¥åå®ä¾
package main
import (
"fmt"
"github.com/songzhibin97/gkit/coding"
_ "github.com/songzhibin97/gkit/json" // ä¸å®è¦æå导å
¥!!!
)
func main() {
t := struct {
Gkit string
Lever int
}{"Gkit", 200}
fmt.Println(coding.GetCode("json").Name())
data, err := coding.GetCode("json").Marshal(t)
if err != nil {
fmt.Println(err)
return
}
fmt.Println(string(data)) // {"Gkit":"Gkit","Lever":200}
v := struct {
Gkit string
Lever int
}{}
coding.GetCode("json").Unmarshal(data,&v)
fmt.Println(v) // {Gkit 200}
}
concurrent
并åä¸channelæä½³å®è·µ
container
容å¨åç»ä»¶
group
æå 载容å¨
package main
import (
"fmt"
"github.com/songzhibin97/gkit/container/group"
)
func createResources() interface{} {
return map[int]int{1: 1, 2: 2}
}
func createResources2() interface{} {
return []int{1, 2, 3}
}
func main() {
// 类似 sync.Pool ä¸æ ·
// åå§åä¸ä¸ªgroup
g := group.NewGroup(createResources)
// å¦ækey ä¸åå¨ è°ç¨ NewGroup ä¼ å
¥ç function å建èµæº
// å¦æåå¨åè¿åå建çèµæºä¿¡æ¯
v := g.Get("test")
fmt.Println(v) // map[1:1 2:2]
v.(map[int]int)[1] = 3
fmt.Println(v) // map[1:3 2:2]
v2 := g.Get("test")
fmt.Println(v2) // map[1:3 2:2]
// ReSet éç½®åå§åå½æ°,åæ¶ä¼å¯¹ç¼åç keyè¿è¡æ¸
空
g.ReSet(createResources2)
v3 := g.Get("test")
fmt.Println(v3) // []int{1,2,3}
// æ¸
空ç¼åç buffer
g.Clear()
}
pool
类似èµæºæ±
package main
import (
"context"
"fmt"
"github.com/songzhibin97/gkit/container/pool"
"time"
)
var p pool.Pool
type mock map[string]string
func (m *mock) Shutdown() error {
return nil
}
// getResources: è·åèµæº,è¿åçèµæºå¯¹è±¡éè¦å®ç° IShutdown æ¥å£,ç¨äºèµæºåæ¶
func getResources(c context.Context) (pool.IShutdown, error) {
return &mock{"mockKey": "mockValue"}, nil
}
func main() {
// pool.NewList(options ...)
// é»è®¤é
ç½®
// p = pool.NewList()
// å¯ä¾éæ©é
ç½®é项
// 设置 Pool è¿æ¥æ°, å¦æ == 0 åæ éå¶
// pool.SetActive(100)
// 设置æ大空é²è¿æ¥æ°
// pool.SetIdle(20)
// 设置空é²çå¾
æ¶é´
// pool.SetIdleTimeout(time.Second)
// 设置ææçå¾
// pool.SetWait(false,time.Second)
// èªå®ä¹é
ç½®
p = pool.NewList(
pool.SetActive(100),
pool.SetIdle(20),
pool.SetIdleTimeout(time.Second),
pool.SetWait(false, time.Second))
// Newéè¦å®ä¾å,å¦åå¨ pool.Get() ä¼æ æ³è·åå°èµæº
p.NewQueue(getResources)
v, err := p.Get(context.TODO())
if err != nil {
// å¤çé误
}
fmt.Println(v) // &map[mockKey:mockValue]
// Put: èµæºåæ¶
// forceClose: true å
é¨å¸®ä½ è°ç¨ Shutdownåæ¶, å¦åå¤ææ¯å¦æ¯å¯åæ¶,æè½½å°listä¸
err = p.Put(context.TODO(), v, false)
if err != nil {
// å¤çé误
}
// Shutdown åæ¶èµæº,å
³éææèµæº
_ = p.Shutdown()
}
queue
codelå®ç°
package main
import (
"context"
"fmt"
"github.com/songzhibin97/gkit/container/queue/codel"
"github.com/songzhibin97/gkit/overload/bbr"
)
func main() {
queue := codel.NewQueue(codel.SetTarget(40), codel.SetInternal(1000))
// start ä½ç° CoDel ç¶æä¿¡æ¯
start := queue.Stat()
fmt.Println(start)
go func() {
// å®é
æ¶è´¹çå°æ¹
queue.Pop()
}()
if err := queue.Push(context.TODO()); err != nil {
if err == bbr.LimitExceed {
// todo å¤çè¿è½½ä¿æ¤é误
} else {
// todo å¤çå
¶ä»é误
}
}
}
delayed
延æ¶ä»»å¡(åæºç)
package main
import "github.com/songzhibin97/gkit/delayed"
type mockDelayed struct {
exec int64
}
func (m mockDelayed) Do() {
}
func (m mockDelayed) ExecTime() int64 {
return m.exec
}
func (m mockDelayed) Identify() string {
return "mock"
}
func main() {
// å建延æ¶å¯¹è±¡
// delayed.SetSingle() 设置çæ§ä¿¡å·
// delayed.SetSingleCallback() 设置信å·åè°
// delayed.SetWorkerNumber() 设置工ä½åç¨
// delayed.SetCheckTime() 设置çæ§æ¶é´
n := delayed.NewDispatchingDelayed()
// æ·»å 延æ¶ä»»å¡
n.AddDelayed(mockDelayed{exec: 1})
// 强å¶å·æ°
n.Refresh()
// å
³é
n.Close()
}
distributed
åå¸å¼ä»»å¡(详ç»ä½¿ç¨çæµè¯ç¨ä¾)
downgrade
çæé级
// ä¸ github.com/afex/hystrix-go 使ç¨æ¹æ³ä¸è´,åªæ¯åäºæ½è±¡å°è£
,é¿å
å 为å级对æå¡é æå½±å
package main
import (
"context"
"github.com/afex/hystrix-go/hystrix"
"github.com/songzhibin97/gkit/downgrade"
)
var fuse downgrade.Fuse
type RunFunc = func() error
type FallbackFunc = func(error) error
type RunFuncC = func(context.Context) error
type FallbackFuncC = func(context.Context, error) error
var outCH = make(chan struct{}, 1)
func mockRunFunc() RunFunc {
return func() error {
outCH <- struct{}{}
return nil
}
}
func mockFallbackFunc() FallbackFunc {
return func(err error) error {
return nil
}
}
func mockRunFuncC() RunFuncC {
return func(ctx context.Context) error {
return nil
}
}
func mockFallbackFuncC() FallbackFuncC {
return func(ctx context.Context, err error) error {
return nil
}
}
func main() {
// æ¿å°ä¸ä¸ªçæå¨
fuse = downgrade.NewFuse()
// ä¸è®¾ç½® ConfigureCommand èµ°é»è®¤é
ç½®
// hystrix.CommandConfig{} 设置åæ°
fuse.ConfigureCommand("test", hystrix.CommandConfig{})
// Do: åæ¥æ§è¡ func() error, 没æè¶
æ¶æ§å¶ ç´å°çå°è¿å,
// å¦æè¿å error != nil å触å FallbackFunc è¿è¡é级
err := fuse.Do("do", mockRunFunc(), mockFallbackFunc())
if err != nil {
// å¤ç error
}
// Go: å¼æ¥æ§è¡ è¿å channel
ch := fuse.Go("go", mockRunFunc(), mockFallbackFunc())
select {
case err = <-ch:
// å¤çé误
case <-outCH:
break
}
// GoC: Do/Go å®é
ä¸æç»è°ç¨çå°±æ¯GoC, Do主å¤çäºå¼æ¥è¿ç¨
// GoCå¯ä»¥ä¼ å
¥ context ä¿è¯é¾è·¯è¶
æ¶æ§å¶
fuse.GoC(context.TODO(), "goc", mockRunFuncC(), mockFallbackFuncC())
}
egroup
ç»ä»¶çå½å¨æ管ç
// errorGroup
// 级èæ§å¶,å¦ææç»ä»¶åçé误,ä¼éç¥groupææç»ä»¶éåº
// 声æçå½å¨æ管ç
package main
import (
"context"
"fmt"
"github.com/songzhibin97/gkit/egroup"
"github.com/songzhibin97/gkit/goroutine"
"net/http"
"os"
"syscall"
"time"
)
var admin *egroup.LifeAdmin
func mockStart() func(ctx context.Context) error {
return nil
}
func mockShutdown() func(ctx context.Context) error {
return nil
}
type mockLifeAdminer struct{}
func (m *mockLifeAdminer) Start(ctx context.Context) error {
return nil
}
func (m *mockLifeAdminer) Shutdown(ctx context.Context) error {
return nil
}
func main() {
// é»è®¤é
ç½®
//admin = egroup.NewLifeAdmin()
// å¯ä¾éæ©é
ç½®é项
// 设置å¯å¨è¶
æ¶æ¶é´
// <=0 ä¸å¯å¨è¶
æ¶æ¶é´,注æè¦å¨shutdownå¤çå
³ééç¥
// egroup.SetStartTimeout(time.Second)
// 设置å
³éè¶
æ¶æ¶é´
// <=0 ä¸å¯å¨è¶
æ¶æ¶é´
// egroup.SetStopTimeout(time.Second)
// 设置信å·éå,åå¤çä¿¡å·çå½æ°
// egroup.SetSignal(func(lifeAdmin *LifeAdmin, signal os.Signal) {
// return
// }, signal...)
admin = egroup.NewLifeAdmin(egroup.SetStartTimeout(time.Second), egroup.SetStopTimeout(time.Second),
egroup.SetSignal(func(a *egroup.LifeAdmin, signal os.Signal) {
switch signal {
case syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT:
a.Shutdown()
default:
}
}))
// éè¿structæ·»å
admin.Add(egroup.Member{
Start: mockStart(),
Shutdown: mockShutdown(),
})
// éè¿æ¥å£éé
admin.AddMember(&mockLifeAdminer{})
// å¯å¨
defer admin.Shutdown()
if err := admin.Start(); err != nil {
// å¤çé误
// æ£å¸¸å¯å¨ä¼hold主
}
}
func Demo() {
// å®æ´demo
var _admin = egroup.NewLifeAdmin()
srv := &http.Server{
Addr: ":8080",
}
// å¢å ä»»å¡
_admin.Add(egroup.Member{
Start: func(ctx context.Context) error {
fmt.Println("http start")
return goroutine.Delegate(ctx, -1, func(ctx context.Context) error {
return srv.ListenAndServe()
})
},
Shutdown: func(ctx context.Context) error {
fmt.Println("http shutdown")
return srv.Shutdown(context.Background())
},
})
// _admin.Start() å¯å¨
fmt.Println("error", _admin.Start())
defer _admin.Shutdown()
}
errors
å°è£ ä¸äºerrorå¤ç
package main
import (
"fmt"
"net/http"
"time"
"github.com/songzhibin97/gkit/errors"
)
func main() {
err := errors.Errorf(http.StatusBadRequest, "åå ", "æºå¸¦ä¿¡æ¯%s", "æµè¯")
err2 := err.AddMetadata(map[string]string{"time": time.Now().String()}) // æºå¸¦å
ä¿¡æ¯
// err æ¯åæ¥çé误 err2 æ¯å¸¦æå
ä¿¡æ¯çé误
fmt.Println(errors.Is(err,err2)) // ture
// å¯ä»¥è§£æerr2 æ¥è·åæ´å¤çä¿¡æ¯
fmt.Println(err2.Metadata["time"]) // meta
}
gctuner
// Get mem limit from the host machine or cgroup file.
limit := 4 * 1024 * 1024 * 1024
threshold := limit * 0.7
gctuner.Tuning(threshold)
// Friendly input
gctuner.TuningWithFromHuman("1g")
// Auto
// There may be problems with multiple services in one pod.
gctuner.TuningWithAuto(false) // Is it a container? Incoming Boolean
generator
åå·å¨
snowflake
éªè±ç®æ³
package main
import (
"fmt"
"github.com/songzhibin97/gkit/generator"
"time"
)
func main() {
ids := generator.NewSnowflake(time.Now(), 1)
nid, err := ids.NextID()
if err != nil {
// å¤çé误
}
fmt.Println(nid)
}
goroutine
æ± å,æ§å¶éçgoroutine
package main
import (
"context"
"fmt"
"github.com/songzhibin97/gkit/goroutine"
"time"
)
var gGroup goroutine.GGroup
func mockFunc() func() {
return func() {
fmt.Println("ok")
}
}
func main() {
// é»è®¤é
ç½®
//gGroup = goroutine.NewGoroutine(context.TODO())
// å¯ä¾éæ©é
ç½®é项
// 设置åæ¢è¶
æ¶æ¶é´
// goroutine.SetStopTimeout(time.Second)
// 设置æ¥å¿å¯¹è±¡
// goroutine.SetLogger(&testLogger{})
// 设置poolæ大容é
// goroutine.SetMax(100)
gGroup = goroutine.NewGoroutine(context.TODO(),
goroutine.SetStopTimeout(time.Second),
goroutine.SetMax(100),
)
// æ·»å ä»»å¡
if !gGroup.AddTask(mockFunc()) {
// æ·»å ä»»å¡å¤±è´¥
}
// 带æè¶
æ¶æ§å¶æ·»å ä»»å¡
if !gGroup.AddTaskN(context.TODO(), mockFunc()) {
// æ·»å ä»»å¡å¤±è´¥
}
// ä¿®æ¹ poolæ大容é
gGroup.ChangeMax(1000)
// åæ¶èµæº
_ = gGroup.Shutdown()
}
log
æ¥å¿ç¸å ³
package main
import (
"fmt"
"github.com/songzhibin97/gkit/log"
)
type testLogger struct{}
func (l *testLogger) Print(kv ...interface{}) {
fmt.Println(kv...)
}
func main() {
logs := log.NewHelper(log.DefaultLogger)
logs.Debug("debug", "v")
logs.Debugf("%s,%s", "debugf", "v")
logs.Info("Info", "v")
logs.Infof("%s,%s", "infof", "v")
logs.Warn("Warn", "v")
logs.Warnf("%s,%s", "warnf", "v")
logs.Error("Error", "v")
logs.Errorf("%s,%s", "errorf", "v")
/*
[debug] message=debugv
[debug] message=debugf,v
[Info] message=Infov
[Info] message=infof,v
[Warn] message=Warnv
[Warn] message=warnf,v
[Error] message=Errorv
[Error] message=errorf,v
*/
logger := log.DefaultLogger
logger = log.With(logger, "ts", log.DefaultTimestamp, "caller", log.DefaultCaller)
logger.Log(log.LevelInfo, "msg", "helloworld")
// [Info] ts=2021-06-10T13:41:35+08:00 caller=main.go:8 msg=helloworld
}
metrics
æä¾ææ æ¥å£,ç¨äºå®ç°çæ§é ç½®
type Counter interface {
With(lvs ...string) Counter
Inc()
Add(delta float64)
}
// Gauge is metrics gauge.
type Gauge interface {
With(lvs ...string) Gauge
Set(value float64)
Add(delta float64)
Sub(delta float64)
}
// Observer is metrics observer.
type Observer interface {
With(lvs ...string) Observer
Observe(float64)
}
middleware
ä¸é´ä»¶æ¥å£æ¨¡åå®ä¹
package main
import (
"context"
"fmt"
"github.com/songzhibin97/gkit/middleware"
)
func annotate(s string) middleware.MiddleWare {
return func(next middleware.Endpoint) middleware.Endpoint {
return func(ctx context.Context, request interface{}) (interface{}, error) {
fmt.Println(s, "pre")
defer fmt.Println(s, "post")
return next(ctx, request)
}
}
}
func myEndpoint(context.Context, interface{}) (interface{}, error) {
fmt.Println("my endpoint!")
return struct{}{}, nil
}
var (
ctx = context.Background()
req = struct{}{}
)
func main() {
e := middleware.Chain(
annotate("first"),
annotate("second"),
annotate("third"),
)(myEndpoint)
if _, err := e(ctx, req); err != nil {
panic(err)
}
// Output:
// first pre
// second pre
// third pre
// my endpoint!
// third post
// second post
// first post
}
net
ç½ç»ç¸å ³å°è£
tcp
// åéæ°æ®è³å¯¹ç«¯,æéè¯æºå¶
Send(data []byte, retry *Retry) error
// æ¥åæ°æ®
// length == 0 ä» Connä¸æ¬¡è¯»åç«å³è¿å
// length < 0 ä» Conn æ¥æ¶æææ°æ®ï¼å¹¶å°å
¶è¿åï¼ç´å°æ²¡ææ°æ®
// length > 0 ä» Conn æ¥æ¶å°å¯¹åºçæ°æ®è¿å
Recv(length int, retry *Retry) ([]byte, error)
// 读åä¸è¡ '\n'
RecvLine(retry *Retry) ([]byte, error)
// 读åå·²ç»è¶
æ¶çé¾æ¥
RecvWithTimeout(length int, timeout time.Duration, retry *Retry) ([]byte, error)
// åå
¥æ°æ®ç»å·²ç»è¶
æ¶çé¾æ¥
SendWithTimeout(data []byte, timeout time.Duration, retry *Retry) error
// åå
¥æ°æ®å¹¶è¯»åè¿å
SendRecv(data []byte, length int, retry *Retry) ([]byte, error)
// å°æ°æ®åå
¥å¹¶è¯»åºå·²ç»è¶
æ¶çé¾æ¥
SendRecvWithTimeout(data []byte, timeout time.Duration, length int, retry *Retry) ([]byte, error)
options
é项模å¼æ¥å£
overload
è¿è½½ä¿æ¤
æ®é使ç¨
package main
import (
"context"
"github.com/songzhibin97/gkit/overload"
"github.com/songzhibin97/gkit/overload/bbr"
)
func main() {
// æ®é使ç¨
// å
建ç«Group
group := bbr.NewGroup()
// å¦æ没æå°±ä¼å建
limiter := group.Get("key")
f, err := limiter.Allow(context.TODO())
if err != nil {
// 代表已ç»è¿è½½äº,æå¡ä¸å
许æ¥å
¥
return
}
// Op:æµéå®é
çæä½ç±»åååè®°å½ææ
f(overload.DoneInfo{Op: overload.Success})
}
ä¸é´ä»¶å¥ç¨
package main
import (
"context"
"github.com/songzhibin97/gkit/overload"
"github.com/songzhibin97/gkit/overload/bbr"
)
func main() {
// æ®é使ç¨
// å
建ç«Group
group := bbr.NewGroup()
// å¦æ没æå°±ä¼å建
limiter := group.Get("key")
f, err := limiter.Allow(context.TODO())
if err != nil {
// 代表已ç»è¿è½½äº,æå¡ä¸å
许æ¥å
¥
return
}
// Op:æµéå®é
çæä½ç±»åååè®°å½ææ
f(overload.DoneInfo{Op: overload.Success})
// 建ç«Group ä¸é´ä»¶
middle := bbr.NewLimiter()
// å¨middlewareä¸
// ctxä¸æºå¸¦è¿ä¸¤ä¸ªå¯é
ç½®çæææ°æ®
// å¯ä»¥éè¿ ctx.Set
// é
ç½®è·åéå¶å¨ç±»å,å¯ä»¥æ ¹æ®ä¸åapiè·åä¸åçéå¶å¨
ctx := context.WithValue(context.TODO(), bbr.LimitKey, "key")
// å¯é
ç½®æåæ¯å¦ä¸æ¥
// å¿
é¡»æ¯ overload.Op ç±»å
ctx = context.WithValue(ctx, bbr.LimitOp, overload.Success)
_ = middle
}
parser
æä¾ .go
æ件转.pb
以å .pb
转.go
.go
æ件转.pb
åè½æ´ä¸ºä¸°å¯,ä¾å¦æä¾å®ç¹æ桩代ç 注å
¥ä»¥åå»éè¯å«
package main
import (
"fmt"
"github.com/songzhibin97/gkit/parse/parseGo"
"github.com/songzhibin97/gkit/parse/parsePb"
)
func main() {
pgo, err := parseGo.ParseGo("gkit/parse/demo/demo.api")
if err != nil {
panic(err)
}
r := pgo.(*parseGo.GoParsePB)
for _, note := range r.Note {
fmt.Println(note.Text, note.Pos(), note.End())
}
// è¾åº å符串,å¦æéè¦èªè¡å¯¼å
¥æ件
fmt.Println(r.Generate())
// æ桩注å
¥
_ = r.PileDriving("", "start", "end", "var _ = 1")
// æè£
_ = r.PileDismantle("var _ = 1")
ppb, err := parsePb.ParsePb("GKit/parse/demo/test.proto")
if err != nil {
panic(err)
}
// è¾åº å符串,å¦æéè¦èªè¡å¯¼å
¥æ件
fmt.Println(ppb.Generate())
}
registry
æä¾æ³¨ååç°éç¨æ¥å£,使ç¨éç¨æ¥å£å¤æä¾èµ
// Registrar: 注åæ½è±¡
type Registrar interface {
// Register: 注å
Register(ctx context.Context, service *ServiceInstance) error
// Deregister: 注é
Deregister(ctx context.Context, service *ServiceInstance) error
}
// Discovery: æå¡åç°æ½è±¡
type Discovery interface {
// GetService: è¿åæå¡åç¸å
³çæå¡å®ä¾
GetService(ctx context.Context, serviceName string) ([]*ServiceInstance, error)
// Watch: æ ¹æ®æå¡åå建çæ§
Watch(ctx context.Context, serviceName string) (Watcher, error)
}
// Watcher: æå¡çæ§
type Watcher interface {
// Watchéè¦æ»¡è¶³ä»¥ä¸æ¡ä»¶
// 1. 第ä¸æ¬¡ GetService çå表ä¸ä¸ºç©º
// 2. åç°ä»»ä½æå¡å®ä¾æ´æ¹
// ä¸æ»¡è¶³ä»¥ä¸ä¸¤ç§æ¡ä»¶,Nextåä¼æ éçå¾
ç´å°ä¸ä¸ææªæ¢
Next() ([]*ServiceInstance, error)
// Stop: åæ¢çæ§è¡ä¸º
Stop() error
}
restrictor
éæµå¨
rate
æ¼æ¡¶
package main
import (
"context"
rate2 "github.com/songzhibin97/gkit/restrictor/rate"
"golang.org/x/time/rate"
"time"
)
func main() {
// 第ä¸ä¸ªåæ°æ¯ r Limitã代表æ¯ç§å¯ä»¥å Token 桶ä¸äº§çå¤å° tokenãLimit å®é
ä¸æ¯ float64 çå«å
// 第äºä¸ªåæ°æ¯ b intãb 代表 Token 桶ç容é大å°ã
// limit := Every(100 * time.Millisecond);
// limiter := rate.NewLimiter(limit, 4)
// 以ä¸å°±è¡¨ç¤ºæ¯ 100ms å¾æ¡¶ä¸æ¾ä¸ä¸ª Tokenãæ¬è´¨ä¸ä¹å°±æ¯ä¸ç§é产ç 10 个ã
// rate: golang.org/x/time/rate
limiter := rate.NewLimiter(2, 4)
af, wf := rate2.NewRate(limiter)
// af.Allow()bool: é»è®¤å1个token
// af.Allow() == af.AllowN(time.Now(), 1)
af.Allow()
// af.AllowN(ctx,n)bool: å¯ä»¥åN个token
af.AllowN(time.Now(), 5)
// wf.Wait(ctx) err: çå¾
ctxè¶
æ¶,é»è®¤å1个token
// wf.Wait(ctx) == wf.WaitN(ctx, 1)
_ = wf.Wait(context.TODO())
// wf.WaitN(ctx, n) err: çå¾
ctxè¶
æ¶,å¯ä»¥åN个token
_ = wf.WaitN(context.TODO(), 5)
}
ratelimite
令ç桶
package main
import (
"context"
"github.com/juju/ratelimit"
ratelimit2 "github.com/songzhibin97/gkit/restrictor/ratelimite"
"time"
)
func main() {
// ratelimit:github.com/juju/ratelimit
bucket := ratelimit.NewBucket(time.Second/2, 4)
af, wf := ratelimit2.NewRateLimit(bucket)
// af.Allow()bool: é»è®¤å1个token
// af.Allow() == af.AllowN(time.Now(), 1)
af.Allow()
// af.AllowN(ctx,n)bool: å¯ä»¥åN个token
af.AllowN(time.Now(), 5)
// wf.Wait(ctx) err: çå¾
ctxè¶
æ¶,é»è®¤å1个token
// wf.Wait(ctx) == wf.WaitN(ctx, 1)
_ = wf.Wait(context.TODO())
// wf.WaitN(ctx, n) err: çå¾
ctxè¶
æ¶,å¯ä»¥åN个token
_ = wf.WaitN(context.TODO(), 5)
}
structure (常ç¨æ°æ®ç»æ)
hashset
l := hashset.NewInt()
for _, v := range []int{10, 12, 15} {
l.Add(v)
}
if l.Contains(10) {
fmt.Println("hashset contains 10")
}
l.Range(func(value int) bool {
fmt.Println("hashset range found ", value)
return true
})
l.Remove(15)
fmt.Printf("hashset contains %d items\r\n", l.Len())
lscq
l := lscq.NewUint64()
ok := l.Enqueue(1)
if !ok {
panic("enqueue failed")
}
v, err := l.Dequeue()
if err != nil {
panic("dequeue failed")
}
fmt.Println("lscq dequeue value:", v)}
skipmap
m := skipmap.NewInt()
// Correctness.
m.Store(123, "123")
m.Load(123)
m.Delete(123)
m.LoadOrStore(123)
m.LoadAndDelete(123)
skipset
func Example() {
l := skipset.NewInt()
for _, v := range []int{10, 12, 15} {
if l.Add(v) {
fmt.Println("skipset add", v)
}
}
if l.Contains(10) {
fmt.Println("skipset contains 10")
}
l.Range(func(value int) bool {
fmt.Println("skipset range found ", value)
return true
})
l.Remove(15)
fmt.Printf("skipset contains %d items\r\n", l.Len())
}
zset æ¥ç对åºreadme
sys
mutex
éç¸å
³å°è£
ï¼å®ç°äºtrylockãéå
¥éçãéå
¥tokené,è¿å¯ä»¥è·åéææ æ°æ®ï¼
// è·åé
lk := mutex.NewMutex()
// å°è¯è·åé
if lk.TryLock() {
// è·åå°é
defer lk.Unlock()
}
// è·å失败æ§è¡å
¶ä»é»è¾
lk.Count() // è·åçå¾
éçæ°é
lk.IsLocked() // éæ¯å¦è¢«ææ
lk.IsWoken() // å
é¨æ¯å¦æçå¾
è
被å¤é
lk.IsStarving() // æ¯å¦å¤äºé¥¥é¥¿æ¨¡å¼
// éå
¥é
// å¨åä¸ä¸ªgoroutineå¯ä»¥å¤æ¬¡è·å
rvlk := mutex.NewRecursiveMutex()
rvlk.Lock()
defer rvlk.Unlock()
// tokenéå
¥é
// ä¼ å
¥ç¸åtoken å¯ä»¥å®ç°éå
¥åè½
tklk := mutex.NewTokenRecursiveMutex()
tklk.Lock(token)
defer tklk.Unlock(token)
ternary
ternary.ReturnInt(true, 1, 2)
timeout
å个æå¡é´çè¶ æ¶æ§å¶(以åå¤çæ¶é´æ ¼å¼çç»æä½)
package main
import (
"context"
"github.com/songzhibin97/gkit/timeout"
"time"
)
func main() {
// timeout.Shrink æ¹æ³æä¾å
¨é¾è·¯çè¶
æ¶æ§å¶
// åªéè¦ä¼ å
¥ä¸ä¸ªç¶èç¹çctx åéè¦è®¾ç½®çè¶
æ¶æ¶é´,ä»ä¼å¸®ä½ 确认è¿ä¸ªctxæ¯å¦ä¹å设置è¿è¶
æ¶æ¶é´,
// å¦æ设置è¿è¶
æ¶æ¶é´çè¯ä¼åä½ å½å设置çè¶
æ¶æ¶é´è¿è¡æ¯è¾,éæ©ä¸ä¸ªæå°çè¿è¡è®¾ç½®,ä¿è¯é¾è·¯è¶
æ¶æ¶é´ä¸ä¼è¢«ä¸æ¸¸å½±å
// d: 代表å©ä½çè¶
æ¶æ¶é´
// nCtx: æ°çcontext对象
// cancel: å¦ææ¯æåçæ£è®¾ç½®äºè¶
æ¶æ¶é´ä¼è¿åä¸ä¸ªcancel()æ¹æ³,æªè®¾ç½®æåä¼è¿åä¸ä¸ªæ æçcancel,ä¸è¿å«æ
å¿,è¿æ¯å¯ä»¥æ£å¸¸è°ç¨ç
d, nCtx, cancel := timeout.Shrink(context.Background(), 5*time.Second)
// d æ ¹æ®éè¦å¤æ
// ä¸è¬å¤æ该æå¡çä¸æ¸¸è¶
æ¶æ¶é´,å¦ædè¿äºå°,å¯ä»¥ç´æ¥æ¾å¼
select {
case <-nCtx.Done():
cancel()
default:
// ...
}
_ = d
}
å ¶ä»
timeout.DbJSON // æä¾db jsonæ ¼å¼çä¸äºåè½
timeout.DTime // æä¾db 15:04:05 æ ¼å¼çä¸äºåè½
timeout.DateStruct // æä¾db 15:04:05 æ ¼å¼çä¸äºåè½ åµå
¥æ¨¡å¼ä¸ºstruct
timeout.Date // æä¾db 2006-01-02 æ ¼å¼çä¸äºåè½
timeout.DateTime // æä¾db 2006-01-02 15:04:05 æ ¼å¼çä¸äºåè½
timeout.DateTimeStruct // æä¾db 2006-01-02 15:04:05 æ ¼å¼çä¸äºåè½ åµå
¥æ¨¡å¼ä¸ºstruct
timeout.Stamp // æä¾db æ¶é´æ³æ ¼å¼çä¸äºåè½
package main
import (
"github.com/songzhibin97/gkit/timeout"
"fmt"
"gorm.io/driver/mysql"
"gorm.io/gorm"
"time"
)
type GoStruct struct {
DateTime timeout.DateTime
DTime timeout.DTime
Date timeout.Date
}
func main() {
// åè https://github.com/go-sql-driver/mysql#dsn-data-source-name è·å详æ
dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{})
if err != nil {
panic(err)
}
db.AutoMigrate(&GoStruct{})
db.Create(&GoStruct{
DateTime: timeout.DateTime(time.Now()),
DTime: timeout.DTime(time.Now()),
Date: timeout.Date(time.Now()),
})
v := &GoStruct{}
db.Find(v) // æåæ¥åº
}
tools
bind
package main
// 为 ginæä¾ä¸ä¸ªå
¨è½bindå·¥å
·
import (
"github.com/songzhibin97/gkit/tools/bind"
"github.com/gin-gonic/gin"
)
type Test struct {
Json string `json:"json" form:"json,default=jjjson"`
Query string `json:"query" form:"query"`
}
func main() {
r := gin.Default()
r.POST("test", func(c *gin.Context) {
t := Test{}
// url : 127.0.0.1:8080/test?query=query
// {
// "json":"json",
// "query":"query"
// }
// err := c.ShouldBindWith(&t, bind.CreateBindAll(c.ContentType()),bind.)
// èªå®ä¹binding对象
// err := c.ShouldBindWith(&t, bind.CreateBindAll(c.ContentType(),bind.SetSelectorParse([]bind.Binding{})))
if err != nil {
c.JSON(200, err)
return
}
c.JSON(200, t)
})
r.Run(":8080")
}
votodo
package main
import "github.com/songzhibin97/gkit/tools/vto"
type CP struct {
Z1 int `default:"1"`
Z2 string `default:"z2"`
}
func main() {
c1 := CP{
Z1: 22,
Z2: "33",
}
c2 := CP{}
c3 := CP{}
_ = vto.VoToDo(&c2,&c1)
// c2 CP{ Z1: 22, Z2: "33"}
// ç¸åå称ç¸åç±»åçæ§è¡å¤å¶
// ä¸å®è¦dstãsrc å¿
é¡»ä¼ æéç±»å
// v1.1.2 æ°å¢defaultæ ç¾
_ = vto.VoToDo(&c2,&c3)
// c2 CP{ Z1: 1, Z2: "z2"}
// ç¸åå称ç¸åç±»åçæ§è¡å¤å¶
// ä¸å®è¦dstãsrc å¿
é¡»ä¼ æéç±»å
}
votodoPlus
å¢å äºå段&tag&é»è®¤å¼
window
æä¾ææ çªå£
package main
import (
"fmt"
"github.com/songzhibin97/gkit/window"
"time"
)
func main() {
w := window.NewWindow()
slice := []window.Index{
{Name: "1", Score: 1}, {Name: "2", Score: 2},
{Name: "2", Score: 2}, {Name: "3", Score: 3},
{Name: "2", Score: 2}, {Name: "3", Score: 3},
{Name: "4", Score: 4}, {Name: "3", Score: 3},
{Name: "5", Score: 5}, {Name: "2", Score: 2},
{Name: "6", Score: 6}, {Name: "5", Score: 5},
}
/*
[{1 1} {2 2}]
[{2 4} {3 3} {1 1}]
[{1 1} {2 6} {3 6}]
[{3 9} {4 4} {1 1} {2 6}]
[{1 1} {2 8} {3 9} {4 4} {5 5}]
[{5 10} {3 9} {2 6} {4 4} {6 6}]
*/
for i := 0; i < len(slice); i += 2 {
w.AddIndex(slice[i].Name, slice[i].Score)
w.AddIndex(slice[i+1].Name, slice[i+1].Score)
time.Sleep(time.Second)
fmt.Println(w.Show())
}
}
trace
é¾è·¯è¿½è¸ª
package main
import (
"context"
"fmt"
gtrace "github.com/songzhibin97/gkit/trace"
"go.opentelemetry.io/otel/trace"
)
type _Transport struct {
}
func (tr *_Transport) Get(key string) string {
panic("implement me")
}
func (tr *_Transport) Set(key string, value string) {
panic("implement me")
}
func (tr *_Transport) Keys() []string {
panic("implement me")
}
func main() {
// trace.WithServer() æå¡ç«¯ä½¿ç¨ä¸é´ä»¶
// trace.WithClient() 客æ·ç«¯ä½¿ç¨ä¸é´ä»¶
tracer := gtrace.NewTracer(trace.SpanKindServer)
ctx, span := tracer.Start(context.Background(), "使ç¨gkit", &_Transport{})
fmt.Println(span)
defer tracer.End(ctx, span, "replay", nil)
}
watching
ç³»ç»çæ§(å å«cpuãmumãgcãgoroutineççªå£çæ§,å¨é¢è®¾æ³¢å¨å¼ådump pprof)