github.com/songzhibin97/gkit@v1.2.13/README_zh.md (about) 1 # GKIT 2 3 ``` 4 _____/\\\\\\\\\\\\__/\\\________/\\\__/\\\\\\\\\\\__/\\\\\\\\\\\\\\\_ 5 ___/\\\//////////__\/\\\_____/\\\//__\/////\\\///__\///////\\\/////__ 6 __/\\\_____________\/\\\__/\\\//_________\/\\\___________\/\\\_______ 7 _\/\\\____/\\\\\\\_\/\\\\\\//\\\_________\/\\\___________\/\\\_______ 8 _\/\\\___\/////\\\_\/\\\//_\//\\\________\/\\\___________\/\\\_______ 9 _\/\\\_______\/\\\_\/\\\____\//\\\_______\/\\\___________\/\\\_______ 10 _\/\\\_______\/\\\_\/\\\_____\//\\\______\/\\\___________\/\\\_______ 11 _\//\\\\\\\\\\\\/__\/\\\______\//\\\__/\\\\\\\\\\\_______\/\\\_______ 12 __\////////////____\///________\///__\///////////________\///________ 13 ``` 14 15 16 # 项目简介 17 致力于提供微服务以及单体服务的可用性基础组件工具集合,借鉴了一些优秀的开源项目例如:`kratos`、`go-kit`、`mosn`、`sentinel`、`gopkg`... 希望大家多多支持 18 19 # 目录结构 20 ```shell 21 ├── cache (构建缓存相关组件) 22 ├── buffer (提供byte数组复用以及io buffer封装) 23 ├── mbuffer (buffer 类似实现) 24 ├── local_cache (提供本地key-value构建本地缓存的封装实现) 25 ├── singleflight (提供高并发情况下防止重复任务,一般用于cache miss后填补cache场景) 26 ├── coding (提供对象序列化/反序列化接口化, 提供json、proto、xml、yaml 实例方法) 27 ├── concurrent (在并发中使用channel的最佳实践) 28 ├── fan_in (扇入模式,常用与生产者消费者模型中多个生产者,一个消费者) 29 ├── fan_out (扇出模式,常用与生产着消费者模型中一个生产者,多个消费者) 30 ├── or_done (并发场景下任意一个任务完成后立即返回) 31 ├── orderly (在并发场景下也能保持有序的完成返回) 32 ├── map_reduce 33 ├── stream (提供数据生产流封装,以及处理流的实现) 34 ├── pipeline (并发变为串行) 35 ├── container (容器化组件,提供group、pool、queue) 36 ├── group (提供了容器懒加载模式,类似sync.Pool,在使用时使用key获取对应容器实例,如果不存在则进行生成) 37 ├── pool (提供了pool的封装抽象,以及使用list对接口的实现) 38 ├── queue 39 ├── codel (对列实现可控制延时算法,对积压任务实现制裁) 40 ├── delayed (延时任务-单机版) 41 ├── distributed (分布式任务,提供了标准化接口以及redis、mysql、pgsql、mongodb对应的实现) 42 ├── downgrade (熔断降级相关组件) 43 ├── egroup (errgroup,控制组件生命周期) 44 ├── encrypt (加密封装,保护padkey补全) 45 ├── errors (grpc error处理) 46 ├── gctuner (go1.19前优化gc利器) 47 ├── generator (发号器,snowflake) 48 ├── goroutine (提供goroutine池,控制goroutine数量激增) 49 ├── log (接口化日志,使用日志组件接入) 50 ├── metrics (指标接口化) 51 ├── middleware (中间件接口模型定义) 52 ├── net (网络相关封装) 53 ├── tcp 54 ├── options (选项模式接口化) 55 ├── overload (服务器自适应保护,提供bbr接口,监控部署服务器状态选择流量放行,保护服务器可用性) 56 ├── bbr (自适应限流) 57 ├── page_token (google aip next token 实现) 58 ├── parser (文件解析,proto<->go相互解析) 59 ├── parseGo (解析go生成pb) 60 ├── parsePb (解析pb生成go) 61 ├── registry (服务发现接口化、google sre subset实现) 62 ├── restrictor (限流,提供令牌桶和漏桶接口封装) 63 ├── client_throttling (客户端节流) 64 ├── rate 65 ├── ratelimite 66 ├── structure (常用数据结构) 67 ├── hashset (哈希表) 68 ├── lscq (无锁无边界队列,支持arm) 69 ├── skipmap 70 ├── skipset 71 ├── zset 72 ├── sync 73 ├── cpu (获取Linux平台下的系统信息,包括cpu主频、cpu使用率等) 74 ├── fastrand (随机数) 75 ├── goid (获取goroutine id) 76 ├── mutex (提供trylock、重入锁和token重入锁) 77 ├── nanotime (时间戳优化) 78 ├── once (once 更强大的实现,设置once函数增加返回error,失败后可重试) 79 ├── queue (无锁队列) 80 ├── safe (底层string,slice 结构) 81 ├── stringx (string 增强版) 82 ├── syncx (sync 增强版) 83 ├── xxhash3 84 ├── ternary (三元表达式) 85 ├── timeout (超时控制,全链路保护、提供一些数据库处理时间的封装实现) 86 ├── ctime (链路超时控制) 87 ├── c_json (适配数据库json类型) 88 ├── d_time (适配数据库 只存储时间) 89 ├── date (适配数据库 只存储日期) 90 ├── date_struct (适配数据库 只存储日期) 91 ├── datetime (适配数据库 存储datetime) 92 ├── datetime_struct (适配数据库 存储datetime) 93 ├── stamp (适配数据库 存储时间戳) 94 ├── human (提供可视化时间间距) 95 ├── tools 96 ├── bind (绑定工具,常用与gin框架中自定义绑定数据,例如同时绑定query和json) 97 ├── deepcopy (深拷贝) 98 ├── float (浮点数截断工具) 99 ├── match (基础匹配器,根据通配符匹配) 100 ├── pointer (指针工具) 101 ├── pretty (格式化json) 102 ├── reflect2value (基础字段映射) 103 ├── rand_string (随机字符串) 104 ├── vto (具有相同类型的函数赋值,解放双手,通常用于vo->do对象转换) 105 ├── vtoPlus (新增plus 支持字段,tag以及默认值绑定) 106 ├── trace (链路追踪) 107 ├── watching (监控cpu、mum、gc、goroutine等指标信息,在波动的情况下自动dump pprof指标) 108 └── window (滑动窗口,支持多数据类型指标窗口收集) 109 110 ``` 111 112 # 下载使用 113 ```shell 114 # go get github.com/songzhibin97/gkit@master 115 go get github.com/songzhibin97/gkit 116 ``` 117 118 # 组件使用介绍 119 ## cache 120 121 缓存相关组件 122 > buffer&mbuffer 提供的功能类似,buffer多了一些封装,以及实现了io方面的一些接口,而mbuffer仅仅是一个memory的缓存;在生命周期较短且频繁的情况下更适用; 123 > local_cache 提供了本地的数据缓存,也有一些失效机制,可以设置过期时间,以及定时清理过期数据,但是他现在比较旧了,如果需要的话有泛型版本 https://github.com/songzhibin97/go-baseutils/blob/main/app/bcache 124 > singleflight 封装了 golang.org/x/sync/singleflight,防止变更带来的影响. 125 126 ### buffer pool 127 ```go 128 package main 129 130 import ( 131 "fmt" 132 "github.com/songzhibin97/gkit/cache/buffer" 133 ) 134 135 func main() { 136 // Byte复用 137 138 // size 2^6 - 2^18 139 // 返回向上取整的 2的整数倍 cap, len == size 140 // 其他特殊的或者在运行期间扩容的 将会被清空 141 slice := buffer.GetBytes(1024) 142 fmt.Println(len(*slice), cap(*slice)) // 1024 1024 143 144 // 回收 145 // 注意: 回收以后不可在引用 146 buffer.PutBytes(slice) 147 148 // IOByte 复用 149 150 // io buffer.IoBuffer interface 151 io := buffer.GetIoPool(1024) 152 153 // 如果一个对象已经被回收了,再次引用被回收的对象会触发错误 154 err := buffer.PutIoPool(io) 155 if err != nil { 156 // 处理错误 157 } 158 } 159 ``` 160 161 ### local_cache 162 ```go 163 package local_cache 164 165 import ( 166 "github.com/songzhibin97/gkit/cache/buffer" 167 "log" 168 ) 169 170 var ch Cache 171 172 func ExampleNewCache() { 173 // 默认配置 174 //ch = NewCache() 175 176 // 可供选择的配置选项 177 178 // 设置间隔时间 179 // SetInternal(interval time.Duration) 180 181 // 设置默认的超时时间 182 // SetDefaultExpire(expire time.Duration) 183 184 // 设置周期的执行函数,默认(不设置)是扫描全局清除过期的k 185 // SetFn(fn func()) 186 187 // 设置触发删除后的捕获函数, 数据删除后回调用设置的捕获函数 188 // SetCapture(capture func(k string, v interface{})) 189 190 // 设置初始化存储的成员对象 191 // SetMember(m map[string]Iterator) 192 193 ch = NewCache(SetInternal(1000), 194 SetDefaultExpire(10000), 195 SetCapture(func(k string, v interface{}) { 196 log.Println(k, v) 197 })) 198 } 199 200 func ExampleCacheStorage() { 201 // Set 添加cache 无论是否存在都会覆盖 202 ch.Set("k1", "v1", DefaultExpire) 203 204 // SetDefault 无论是否存在都会覆盖 205 // 偏函数模式,默认传入超时时间为创建cache的默认时间 206 ch.SetDefault("k1", 1) 207 208 // SetNoExpire 209 // 偏函数模式,默认传入超时时间为永不过期 210 ch.SetNoExpire("k1", 1.1) 211 212 // Add 添加cache 如果存在的话会抛出异常 213 err := ch.Add("k1", nil, DefaultExpire) 214 CacheErrExist(err) // true 215 216 // Replace 如果有就设置没有就抛出错误 217 err = ch.Replace("k2", make(chan struct{}), DefaultExpire) 218 CacheErrNoExist(err) // true 219 } 220 221 func ExampleGet() { 222 // Get 根据key获取 cache 保证有效期内的kv被取出 223 v, ok := ch.Get("k1") 224 if !ok { 225 // v == nil 226 } 227 _ = v 228 229 // GetWithExpire 根据key获取 cache 并带出超时时间 230 v, t, ok := ch.GetWithExpire("k1") 231 if !ok { 232 // v == nil 233 } 234 // 如果超时时间是 NoExpire t.IsZero() == true 235 if t.IsZero() { 236 // 没有设置超时时间 237 } 238 239 // Iterator 返回 cache 中所有有效的对象 240 mp := ch.Iterator() 241 for s, iterator := range mp { 242 log.Println(s, iterator) 243 } 244 245 // Count 返回member数量 246 log.Println(ch.Count()) 247 } 248 249 func ExampleIncrement() { 250 ch.Set("k3", 1, DefaultExpire) 251 ch.Set("k4", 1.1, DefaultExpire) 252 // Increment 为k对应的value增加n n必须为数字类型 253 err := ch.Increment("k3", 1) 254 if CacheErrExpire(err) || CacheErrExist(CacheTypeErr) { 255 // 未设置成功 256 } 257 _ = ch.IncrementFloat("k4", 1.1) 258 259 // 如果你知道设置的k的具体类型 还可以使用类型确定的 increment函数 260 // ch.IncrementInt(k string, v int) 261 // ... 262 // ch.IncrementFloat32(k string, v flot32) 263 // ... 264 265 // Decrement 同理 266 } 267 268 func ExampleDelete() { 269 // Delete 如果设置了 capture 会触发不或函数 270 ch.Delete("k1") 271 272 // DeleteExpire 删除所有过期了的key, 默认的 capture 就是执行 DeleteExpire() 273 ch.DeleteExpire() 274 } 275 276 func ExampleChangeCapture() { 277 // 提供了在运行中改变捕获函数的方法 278 // ChangeCapture 279 ch.ChangeCapture(func(k string, v interface{}) { 280 log.Println(k, v) 281 }) 282 } 283 284 func ExampleSaveLoad() { 285 // 写入文件采用go独有的gob协议 286 287 io := buffer.NewIoBuffer(1000) 288 289 // Save 传入一个 w io.Writer 参数 将 cache中的 member 成员写入w中 290 _ = ch.Save(io) 291 292 // SaveFile 传入path 写到文件中 293 _ = ch.SaveFile("path") 294 295 // Load 传入一个 r io.Reader对象 从 r中读取写回到 member中 296 _ = ch.Load(io) 297 298 // LoadFile 传入path 读取文件内容 299 _ = ch.LoadFile("path") 300 } 301 302 func ExampleFlush() { 303 // Flush 释放member成员 304 ch.Flush() 305 } 306 307 func ExampleShutdown() { 308 // Shutdown 释放对象 309 ch.Shutdown() 310 } 311 ``` 312 313 ### singleflight 314 315 归并回源 316 ```go 317 package main 318 319 import ( 320 "github.com/songzhibin97/gkit/cache/singleflight" 321 ) 322 323 // getResources: 一般用于去数据库去获取数据 324 func getResources() (interface{}, error) { 325 return "test", nil 326 } 327 328 // cache: 填充到 缓存中的数据 329 func cache(v interface{}) { 330 return 331 } 332 333 func main() { 334 f := singleflight.NewSingleFlight() 335 336 // 同步: 337 v, err, _ := f.Do("test1", func() (interface{}, error) { 338 // 获取资源 339 return getResources() 340 }) 341 if err != nil { 342 // 处理错误 343 } 344 // 存储到buffer 345 // v就是获取到的资源 346 cache(v) 347 348 // 异步 349 ch := f.DoChan("test2", func() (interface{}, error) { 350 // 获取资源 351 return getResources() 352 }) 353 354 // 等待获取资源完成后,会将结果通过channel返回 355 result := <-ch 356 if result.Err != nil { 357 // 处理错误 358 } 359 360 // 存储到buffer 361 // result.Val就是获取到的资源 362 cache(result.Val) 363 364 // 尽力取消 365 f.Forget("test2") 366 } 367 ``` 368 369 370 ## coding 371 > 对象序列化反序列化接口以及实例封装,只需要导入匿名,例如json `_ "github.com/songzhibin97/gkit/coding/json"` 也可以实现对应接口后,进行注册,好处是可以控制全局序列化对象以及使用库的统一 372 373 ```go 374 package main 375 376 import ( 377 "fmt" 378 "github.com/songzhibin97/gkit/coding" 379 _ "github.com/songzhibin97/gkit/coding/json" // 一定要提前导入!!! 380 ) 381 382 func main() { 383 t := struct { 384 Gkit string 385 Lever int 386 }{"Gkit", 200} 387 fmt.Println(coding.GetCode("json").Name()) 388 data, err := coding.GetCode("json").Marshal(t) 389 if err != nil { 390 fmt.Println(err) 391 return 392 } 393 fmt.Println(string(data)) // {"Gkit":"Gkit","Lever":200} 394 v := struct { 395 Gkit string 396 Lever int 397 }{} 398 coding.GetCode("json").Unmarshal(data,&v) 399 fmt.Println(v) // {Gkit 200} 400 } 401 ``` 402 403 ## concurrent 404 405 > 并发中channel最佳实践,包含 fan_in,fan_out,map_reduce,or_done,orderly,pipeline,stream,泛型版本可以参考 https://github.com/songzhibin97/go-baseutils/tree/main/app/bconcurrent 406 407 408 ## container 409 410 > group 适用于生命周期较长对象的懒加载,类似于 sync.Pool,但是可以自定义创建函数,以及更换初始化函数 411 > pool 池化对象,通过配置可以设置最大连接数以及等待连接数,同步异步获取连接,以及连接的生命周期管理,可以自定义创建函数,以及更换初始化函数 412 > codel 实现codel算法,可以用于限流,以及熔断 413 414 ### group 415 416 懒加载容器 417 ```go 418 package main 419 420 import ( 421 "fmt" 422 "github.com/songzhibin97/gkit/container/group" 423 ) 424 425 func createResources() interface{} { 426 return map[int]int{1: 1, 2: 2} 427 } 428 429 func createResources2() interface{} { 430 return []int{1, 2, 3} 431 } 432 433 func main() { 434 // 类似 sync.Pool 一样 435 // 初始化一个group 436 g := group.NewGroup(createResources) 437 438 // 如果key 不存在 调用 NewGroup 传入的 function 创建资源 439 // 如果存在则返回创建的资源信息 440 v := g.Get("test") 441 fmt.Println(v) // map[1:1 2:2] 442 v.(map[int]int)[1] = 3 443 fmt.Println(v) // map[1:3 2:2] 444 v2 := g.Get("test") 445 fmt.Println(v2) // map[1:3 2:2] 446 447 // ReSet 重置初始化函数,同时会对缓存的 key进行清空 448 g.ReSet(createResources2) 449 v3 := g.Get("test") 450 fmt.Println(v3) // []int{1,2,3} 451 452 // 清空缓存的 buffer 453 g.Clear() 454 } 455 ``` 456 457 ### pool 458 459 类似资源池 460 ```go 461 package main 462 463 import ( 464 "context" 465 "fmt" 466 "github.com/songzhibin97/gkit/container/pool" 467 "time" 468 ) 469 470 var p pool.Pool 471 472 type mock map[string]string 473 474 func (m *mock) Shutdown() error { 475 return nil 476 } 477 478 // getResources: 获取资源,返回的资源对象需要实现 IShutdown 接口,用于资源回收 479 func getResources(c context.Context) (pool.IShutdown, error) { 480 return &mock{"mockKey": "mockValue"}, nil 481 } 482 483 func main() { 484 // pool.NewList(options ...) 485 // 默认配置 486 // p = pool.NewList() 487 488 // 可供选择配置选项 489 490 // 设置 Pool 连接数, 如果 == 0 则无限制 491 // pool.SetActive(100) 492 493 // 设置最大空闲连接数 494 // pool.SetIdle(20) 495 496 // 设置空闲等待时间 497 // pool.SetIdleTimeout(time.Second) 498 499 // 设置期望等待 500 // pool.SetWait(false,time.Second) 501 502 // 自定义配置 503 p = pool.NewList( 504 pool.SetActive(100), 505 pool.SetIdle(20), 506 pool.SetIdleTimeout(time.Second), 507 pool.SetWait(false, time.Second)) 508 509 // New需要实例化,否则在 pool.Get() 会无法获取到资源 510 p.NewQueue(getResources) 511 512 v, err := p.Get(context.TODO()) 513 if err != nil { 514 // 处理错误 515 } 516 fmt.Println(v) // &map[mockKey:mockValue] 517 518 // Put: 资源回收 519 // forceClose: true 内部帮你调用 Shutdown回收, 否则判断是否是可回收,挂载到list上 520 err = p.Put(context.TODO(), v, false) 521 if err != nil { 522 // 处理错误 523 } 524 525 // Shutdown 回收资源,关闭所有资源 526 _ = p.Shutdown() 527 } 528 ``` 529 530 ### queue 531 codel实现 532 ```go 533 package main 534 535 import ( 536 "context" 537 "fmt" 538 "github.com/songzhibin97/gkit/container/queue/codel" 539 "github.com/songzhibin97/gkit/overload/bbr" 540 ) 541 542 func main() { 543 queue := codel.NewQueue(codel.SetTarget(40), codel.SetInternal(1000)) 544 545 // start 体现 CoDel 状态信息 546 start := queue.Stat() 547 fmt.Println(start) 548 549 go func() { 550 // 实际消费的地方 551 queue.Pop() 552 }() 553 if err := queue.Push(context.TODO()); err != nil { 554 if err == bbr.LimitExceed { 555 // todo 处理过载保护错误 556 } else { 557 // todo 处理其他错误 558 } 559 } 560 } 561 ``` 562 563 ## delayed 564 565 > 单机版本的延时任务,利用四叉堆进行实现,可以自定义监控信号,以及监控时间,以及工作协程数量 566 567 ```go 568 package main 569 570 import "github.com/songzhibin97/gkit/delayed" 571 572 type mockDelayed struct { 573 exec int64 574 } 575 576 func (m mockDelayed) Do() { 577 } 578 579 func (m mockDelayed) ExecTime() int64 { 580 return m.exec 581 } 582 583 func (m mockDelayed) Identify() string { 584 return "mock" 585 } 586 587 func main() { 588 // 创建延时对象 589 // delayed.SetSingle() 设置监控信号 590 // delayed.SetSingleCallback() 设置信号回调 591 // delayed.SetWorkerNumber() 设置工作协程 592 // delayed.SetCheckTime() 设置监控时间 593 n := delayed.NewDispatchingDelayed() 594 595 // 添加延时任务 596 n.AddDelayed(mockDelayed{exec: 1}) 597 598 // 强制刷新 599 n.Refresh() 600 601 // 关闭 602 n.Close() 603 } 604 605 ``` 606 607 ## distributed 608 609 > 分布式任务, 目前后端支持db(gorm),mongodb,redis,调度端支持redis,分布式锁支持redis,包括重试机制,以及延时任务 610 611 612 ## downgrade 613 614 > 熔断降级 615 616 ```go 617 // 与 github.com/afex/hystrix-go 使用方法一致,只是做了抽象封装,避免因为升级对服务造成影响 618 package main 619 620 import ( 621 "context" 622 "github.com/afex/hystrix-go/hystrix" 623 "github.com/songzhibin97/gkit/downgrade" 624 ) 625 626 var fuse downgrade.Fuse 627 628 type RunFunc = func() error 629 type FallbackFunc = func(error) error 630 type RunFuncC = func(context.Context) error 631 type FallbackFuncC = func(context.Context, error) error 632 633 var outCH = make(chan struct{}, 1) 634 635 func mockRunFunc() RunFunc { 636 return func() error { 637 outCH <- struct{}{} 638 return nil 639 } 640 } 641 642 func mockFallbackFunc() FallbackFunc { 643 return func(err error) error { 644 return nil 645 } 646 } 647 648 func mockRunFuncC() RunFuncC { 649 return func(ctx context.Context) error { 650 return nil 651 } 652 } 653 654 func mockFallbackFuncC() FallbackFuncC { 655 return func(ctx context.Context, err error) error { 656 return nil 657 } 658 } 659 660 func main() { 661 // 拿到一个熔断器 662 fuse = downgrade.NewFuse() 663 664 // 不设置 ConfigureCommand 走默认配置 665 // hystrix.CommandConfig{} 设置参数 666 fuse.ConfigureCommand("test", hystrix.CommandConfig{}) 667 668 // Do: 同步执行 func() error, 没有超时控制 直到等到返回, 669 // 如果返回 error != nil 则触发 FallbackFunc 进行降级 670 err := fuse.Do("do", mockRunFunc(), mockFallbackFunc()) 671 if err != nil { 672 // 处理 error 673 } 674 675 // Go: 异步执行 返回 channel 676 ch := fuse.Go("go", mockRunFunc(), mockFallbackFunc()) 677 select { 678 case err = <-ch: 679 // 处理错误 680 case <-outCH: 681 break 682 } 683 684 // GoC: Do/Go 实际上最终调用的就是GoC, Do主处理了异步过程 685 // GoC可以传入 context 保证链路超时控制 686 fuse.GoC(context.TODO(), "goc", mockRunFuncC(), mockFallbackFuncC()) 687 } 688 ``` 689 690 ## egroup 691 692 > 组件生命周期管理,与sync.ErrorGroup相比,增加了容错机制,防止野生goroutine panic导致系统异常退出 693 694 695 ```go 696 // errorGroup 697 // 级联控制,如果有组件发生错误,会通知group所有组件退出 698 // 声明生命周期管理 699 700 package main 701 702 import ( 703 "context" 704 "fmt" 705 "github.com/songzhibin97/gkit/egroup" 706 "github.com/songzhibin97/gkit/goroutine" 707 "net/http" 708 "os" 709 "syscall" 710 "time" 711 ) 712 713 var admin *egroup.LifeAdmin 714 715 func mockStart() func(ctx context.Context) error { 716 return nil 717 } 718 719 func mockShutdown() func(ctx context.Context) error { 720 return nil 721 } 722 723 type mockLifeAdminer struct{} 724 725 func (m *mockLifeAdminer) Start(ctx context.Context) error { 726 return nil 727 } 728 729 func (m *mockLifeAdminer) Shutdown(ctx context.Context) error { 730 return nil 731 } 732 733 func main() { 734 // 默认配置 735 //admin = egroup.NewLifeAdmin() 736 737 // 可供选择配置选项 738 739 // 设置启动超时时间 740 // <=0 不启动超时时间,注意要在shutdown处理关闭通知 741 // egroup.SetStartTimeout(time.Second) 742 743 // 设置关闭超时时间 744 // <=0 不启动超时时间 745 // egroup.SetStopTimeout(time.Second) 746 747 // 设置信号集合,和处理信号的函数 748 // egroup.SetSignal(func(lifeAdmin *LifeAdmin, signal os.Signal) { 749 // return 750 // }, signal...) 751 752 admin = egroup.NewLifeAdmin(egroup.SetStartTimeout(time.Second), egroup.SetStopTimeout(time.Second), 753 egroup.SetSignal(func(a *egroup.LifeAdmin, signal os.Signal) { 754 switch signal { 755 case syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGINT: 756 a.Shutdown() 757 default: 758 } 759 })) 760 761 // 通过struct添加 762 admin.Add(egroup.Member{ 763 Start: mockStart(), 764 Shutdown: mockShutdown(), 765 }) 766 767 // 通过接口适配 768 admin.AddMember(&mockLifeAdminer{}) 769 770 // 启动 771 defer admin.Shutdown() 772 if err := admin.Start(); err != nil { 773 // 处理错误 774 // 正常启动会hold主 775 } 776 } 777 778 func Demo() { 779 // 完整demo 780 var _admin = egroup.NewLifeAdmin() 781 782 srv := &http.Server{ 783 Addr: ":8080", 784 } 785 // 增加任务 786 _admin.Add(egroup.Member{ 787 Start: func(ctx context.Context) error { 788 fmt.Println("http start") 789 return goroutine.Delegate(ctx, -1, func(ctx context.Context) error { 790 return srv.ListenAndServe() 791 }) 792 }, 793 Shutdown: func(ctx context.Context) error { 794 fmt.Println("http shutdown") 795 return srv.Shutdown(context.Background()) 796 }, 797 }) 798 // _admin.Start() 启动 799 fmt.Println("error", _admin.Start()) 800 defer _admin.Shutdown() 801 } 802 803 ``` 804 805 ## encrypt 806 807 > aes加密解密常用方法,包括 padkey填充 808 809 ## errors 810 811 封装一些error处理 812 813 ```go 814 package main 815 816 import ( 817 "fmt" 818 "net/http" 819 "time" 820 821 "github.com/songzhibin97/gkit/errors" 822 ) 823 func main() { 824 err := errors.Errorf(http.StatusBadRequest, "原因", "携带信息%s", "测试") 825 err2 := err.AddMetadata(map[string]string{"time": time.Now().String()}) // 携带元信息 826 // err 是原来的错误 err2 是带有元信息的错误 827 fmt.Println(errors.Is(err,err2)) // ture 828 // 可以解析err2 来获取更多的信息 829 fmt.Println(err2.Metadata["time"]) // meta 830 } 831 ``` 832 833 ## gctuner 834 835 > go 1.19前优化gc利器 836 837 ```go 838 package main 839 840 import "github.com/songzhibin97/gkit/gctuner" 841 842 func main() { 843 // Get mem limit from the host machine or cgroup file. 844 limit := 4 * 1024 * 1024 * 1024 845 threshold := uint64(float64(limit) * 0.7) 846 847 gctuner.Tuning(threshold) 848 849 // Friendly input 850 gctuner.TuningWithFromHuman("1g") 851 852 // Auto 853 // There may be problems with multiple services in one pod. 854 gctuner.TuningWithAuto(false) // Is it a container? Incoming Boolean 855 } 856 ``` 857 858 ## generator 859 860 发号器 861 862 ### snowflake 863 864 雪花算法 865 866 ```go 867 package main 868 869 import ( 870 "fmt" 871 "github.com/songzhibin97/gkit/generator" 872 "time" 873 ) 874 875 func main() { 876 ids := generator.NewSnowflake(time.Now(), 1) 877 nid, err := ids.NextID() 878 if err != nil { 879 // 处理错误 880 } 881 fmt.Println(nid) 882 } 883 884 ``` 885 886 ## goroutine 887 888 > 池化,控制野生goroutine panic导致的异常情况, 可以设置最大容量,闲置时间,巡检时间,停止超时时间,日志对象 889 890 ```go 891 package main 892 893 import ( 894 "context" 895 "fmt" 896 "github.com/songzhibin97/gkit/goroutine" 897 "time" 898 ) 899 900 var gGroup goroutine.GGroup 901 902 func mockFunc() func() { 903 return func() { 904 fmt.Println("ok") 905 } 906 } 907 908 func main() { 909 // 默认配置 910 //gGroup = goroutine.NewGoroutine(context.TODO()) 911 912 // 可供选择配置选项 913 914 // 设置停止超时时间 915 // goroutine.SetStopTimeout(time.Second) 916 917 // 设置日志对象 918 // goroutine.SetLogger(&testLogger{}) 919 920 // 设置pool最大容量 921 // goroutine.SetMax(100) 922 923 gGroup = goroutine.NewGoroutine(context.TODO(), 924 goroutine.SetStopTimeout(time.Second), 925 goroutine.SetMax(100), 926 ) 927 928 // 添加任务 929 if !gGroup.AddTask(mockFunc()) { 930 // 添加任务失败 931 } 932 933 // 带有超时控制添加任务 934 if !gGroup.AddTaskN(context.TODO(), mockFunc()) { 935 // 添加任务失败 936 } 937 938 // 修改 pool最大容量 939 gGroup.ChangeMax(1000) 940 941 // 回收资源 942 _ = gGroup.Shutdown() 943 } 944 ``` 945 946 ## log 947 948 日志相关 949 950 ```go 951 package main 952 953 import ( 954 "fmt" 955 "github.com/songzhibin97/gkit/log" 956 ) 957 958 type testLogger struct{} 959 960 func (l *testLogger) Print(kv ...interface{}) { 961 fmt.Println(kv...) 962 } 963 964 func main() { 965 logs := log.NewHelper(log.DefaultLogger) 966 logs.Debug("debug", "v") 967 logs.Debugf("%s,%s", "debugf", "v") 968 logs.Info("Info", "v") 969 logs.Infof("%s,%s", "infof", "v") 970 logs.Warn("Warn", "v") 971 logs.Warnf("%s,%s", "warnf", "v") 972 logs.Error("Error", "v") 973 logs.Errorf("%s,%s", "errorf", "v") 974 /* 975 [debug] message=debugv 976 [debug] message=debugf,v 977 [Info] message=Infov 978 [Info] message=infof,v 979 [Warn] message=Warnv 980 [Warn] message=warnf,v 981 [Error] message=Errorv 982 [Error] message=errorf,v 983 */ 984 985 logger := log.DefaultLogger 986 logger = log.With(logger, "ts", log.DefaultTimestamp, "caller", log.DefaultCaller) 987 logger.Log(log.LevelInfo, "msg", "helloworld") 988 // [Info] ts=2021-06-10T13:41:35+08:00 caller=main.go:8 msg=helloworld 989 } 990 ``` 991 992 ## metrics 993 994 提供指标接口,用于实现监控配置 995 ```go 996 package main 997 998 type Counter interface { 999 With(lvs ...string) Counter 1000 Inc() 1001 Add(delta float64) 1002 } 1003 1004 // Gauge is metrics gauge. 1005 type Gauge interface { 1006 With(lvs ...string) Gauge 1007 Set(value float64) 1008 Add(delta float64) 1009 Sub(delta float64) 1010 } 1011 1012 // Observer is metrics observer. 1013 type Observer interface { 1014 With(lvs ...string) Observer 1015 Observe(float64) 1016 } 1017 ``` 1018 1019 ## middleware 1020 1021 中间件接口模型定义 1022 ```go 1023 package main 1024 1025 import ( 1026 "context" 1027 "fmt" 1028 "github.com/songzhibin97/gkit/middleware" 1029 ) 1030 1031 func annotate(s string) middleware.MiddleWare { 1032 return func(next middleware.Endpoint) middleware.Endpoint { 1033 return func(ctx context.Context, request interface{}) (interface{}, error) { 1034 fmt.Println(s, "pre") 1035 defer fmt.Println(s, "post") 1036 return next(ctx, request) 1037 } 1038 } 1039 } 1040 1041 func myEndpoint(context.Context, interface{}) (interface{}, error) { 1042 fmt.Println("my endpoint!") 1043 return struct{}{}, nil 1044 } 1045 1046 var ( 1047 ctx = context.Background() 1048 req = struct{}{} 1049 ) 1050 1051 func main() { 1052 e := middleware.Chain( 1053 annotate("first"), 1054 annotate("second"), 1055 annotate("third"), 1056 )(myEndpoint) 1057 1058 if _, err := e(ctx, req); err != nil { 1059 panic(err) 1060 } 1061 // Output: 1062 // first pre 1063 // second pre 1064 // third pre 1065 // my endpoint! 1066 // third post 1067 // second post 1068 // first post 1069 } 1070 ``` 1071 1072 1073 ## net 1074 1075 网络相关封装 1076 1077 ### tcp 1078 ```go 1079 // 发送数据至对端,有重试机制 1080 Send(data []byte, retry *Retry) error 1081 1082 // 接受数据 1083 // length == 0 从 Conn一次读取立即返回 1084 // length < 0 从 Conn 接收所有数据,并将其返回,直到没有数据 1085 // length > 0 从 Conn 接收到对应的数据返回 1086 Recv(length int, retry *Retry) ([]byte, error) 1087 1088 // 读取一行 '\n' 1089 RecvLine(retry *Retry) ([]byte, error) 1090 1091 // 读取已经超时的链接 1092 RecvWithTimeout(length int, timeout time.Duration, retry *Retry) ([]byte, error) 1093 1094 // 写入数据给已经超时的链接 1095 SendWithTimeout(data []byte, timeout time.Duration, retry *Retry) error 1096 1097 // 写入数据并读取返回 1098 SendRecv(data []byte, length int, retry *Retry) ([]byte, error) 1099 1100 // 将数据写入并读出已经超时的链接 1101 SendRecvWithTimeout(data []byte, timeout time.Duration, length int, retry *Retry) ([]byte, error) 1102 ``` 1103 1104 1105 ## options 1106 1107 选项模式接口 1108 1109 1110 ## overload 1111 1112 > 提供过载保护,内部使用bbr算法进行限流 1113 1114 **普通使用** 1115 1116 ```go 1117 package main 1118 1119 import ( 1120 "context" 1121 "github.com/songzhibin97/gkit/overload" 1122 "github.com/songzhibin97/gkit/overload/bbr" 1123 ) 1124 1125 func main() { 1126 // 普通使用 1127 1128 // 先建立Group 1129 group := bbr.NewGroup() 1130 // 如果没有就会创建 1131 limiter := group.Get("key") 1132 f, err := limiter.Allow(context.TODO()) 1133 if err != nil { 1134 // 代表已经过载了,服务不允许接入 1135 return 1136 } 1137 // Op:流量实际的操作类型回写记录指标 1138 f(overload.DoneInfo{Op: overload.Success}) 1139 } 1140 ``` 1141 1142 **中间件套用** 1143 1144 ```go 1145 package main 1146 1147 import ( 1148 "context" 1149 "github.com/songzhibin97/gkit/overload" 1150 "github.com/songzhibin97/gkit/overload/bbr" 1151 ) 1152 1153 func main() { 1154 // 普通使用 1155 1156 // 先建立Group 1157 group := bbr.NewGroup() 1158 // 如果没有就会创建 1159 limiter := group.Get("key") 1160 f, err := limiter.Allow(context.TODO()) 1161 if err != nil { 1162 // 代表已经过载了,服务不允许接入 1163 return 1164 } 1165 // Op:流量实际的操作类型回写记录指标 1166 f(overload.DoneInfo{Op: overload.Success}) 1167 1168 // 建立Group 中间件 1169 middle := bbr.NewLimiter() 1170 1171 // 在middleware中 1172 // ctx中携带这两个可配置的有效数据 1173 // 可以通过 ctx.Set 1174 1175 // 配置获取限制器类型,可以根据不同api获取不同的限制器 1176 ctx := context.WithValue(context.TODO(), bbr.LimitKey, "key") 1177 1178 // 可配置成功是否上报 1179 // 必须是 overload.Op 类型 1180 ctx = context.WithValue(ctx, bbr.LimitOp, overload.Success) 1181 1182 _ = middle 1183 } 1184 ``` 1185 ## page_token 1186 > https://google.aip.dev/158 Google Pagination 1187 > 通过选项模式可以配置自定义最大页值,每一页最大元素数,设置加密秘钥,以及过期时间, page_token 可以有效的防止爬虫,以及翻页攻击,也可以限制接口并发量 1188 1189 1190 ```go 1191 package main 1192 1193 import "github.com/songzhibin97/gkit/page_token" 1194 1195 func main() { 1196 n := NewTokenGenerate("test") // 资源唯一标识,以及可传上述选项 1197 // 10000 为总数 1198 // 生成page_token 1199 // start - end 起止地址, tk为next_token 1200 start, end, tk, err := n.ProcessPageTokens(10000, 100, "") // 0,100, tk 1201 start, end, ntk, err := n.ProcessPageTokens(10000, 100, tk) // 100,200,ntk 1202 n.GetIndex(ntk) // 200 1203 } 1204 ``` 1205 1206 ## parser 1207 1208 提供 `.go`文件转`.pb` 以及 `.pb`转`.go` 1209 `.go`文件转`.pb` 功能更为丰富,例如提供定点打桩代码注入以及去重识别 1210 ```go 1211 package main 1212 1213 import ( 1214 "fmt" 1215 "github.com/songzhibin97/gkit/parse/parseGo" 1216 "github.com/songzhibin97/gkit/parse/parsePb" 1217 ) 1218 1219 func main() { 1220 pgo, err := parseGo.ParseGo("gkit/parse/demo/demo.api") 1221 if err != nil { 1222 panic(err) 1223 } 1224 r := pgo.(*parseGo.GoParsePB) 1225 for _, note := range r.Note { 1226 fmt.Println(note.Text, note.Pos(), note.End()) 1227 } 1228 // 输出 字符串,如果需要自行导入文件 1229 fmt.Println(r.Generate()) 1230 1231 // 打桩注入 1232 _ = r.PileDriving("", "start", "end", "var _ = 1") 1233 1234 // 拆装 1235 _ = r.PileDismantle("var _ = 1") 1236 1237 ppb, err := parsePb.ParsePb("GKit/parse/demo/test.proto") 1238 if err != nil { 1239 panic(err) 1240 } 1241 // 输出 字符串,如果需要自行导入文件 1242 fmt.Println(ppb.Generate()) 1243 } 1244 ``` 1245 1246 1247 ## registry 1248 1249 > 提供注册发现通用接口,使用通用接口外挂依赖,以及固定的实例结构 1250 > 服务发现提供了多种算法,常见的subset算法,以及最新的rock_steadier_subset 1251 1252 1253 ## restrictor 1254 1255 限流器 1256 1257 ### rate 1258 1259 漏桶 1260 1261 ```go 1262 package main 1263 1264 import ( 1265 "context" 1266 rate2 "github.com/songzhibin97/gkit/restrictor/rate" 1267 "golang.org/x/time/rate" 1268 "time" 1269 ) 1270 1271 func main() { 1272 // 第一个参数是 r Limit。代表每秒可以向 Token 桶中产生多少 token。Limit 实际上是 float64 的别名 1273 // 第二个参数是 b int。b 代表 Token 桶的容量大小。 1274 // limit := Every(100 * time.Millisecond); 1275 // limiter := rate.NewLimiter(limit, 4) 1276 // 以上就表示每 100ms 往桶中放一个 Token。本质上也就是一秒钟产生 10 个。 1277 1278 // rate: golang.org/x/time/rate 1279 limiter := rate.NewLimiter(2, 4) 1280 1281 af, wf := rate2.NewRate(limiter) 1282 1283 // af.Allow()bool: 默认取1个token 1284 // af.Allow() == af.AllowN(time.Now(), 1) 1285 af.Allow() 1286 1287 // af.AllowN(ctx,n)bool: 可以取N个token 1288 af.AllowN(time.Now(), 5) 1289 1290 // wf.Wait(ctx) err: 等待ctx超时,默认取1个token 1291 // wf.Wait(ctx) == wf.WaitN(ctx, 1) 1292 _ = wf.Wait(context.TODO()) 1293 1294 // wf.WaitN(ctx, n) err: 等待ctx超时,可以取N个token 1295 _ = wf.WaitN(context.TODO(), 5) 1296 } 1297 ``` 1298 ### ratelimite 1299 1300 令牌桶 1301 1302 ```go 1303 package main 1304 1305 import ( 1306 "context" 1307 "github.com/juju/ratelimit" 1308 ratelimit2 "github.com/songzhibin97/gkit/restrictor/ratelimite" 1309 "time" 1310 ) 1311 1312 func main() { 1313 // ratelimit:github.com/juju/ratelimit 1314 bucket := ratelimit.NewBucket(time.Second/2, 4) 1315 1316 af, wf := ratelimit2.NewRateLimit(bucket) 1317 // af.Allow()bool: 默认取1个token 1318 // af.Allow() == af.AllowN(time.Now(), 1) 1319 af.Allow() 1320 1321 // af.AllowN(ctx,n)bool: 可以取N个token 1322 af.AllowN(time.Now(), 5) 1323 1324 // wf.Wait(ctx) err: 等待ctx超时,默认取1个token 1325 // wf.Wait(ctx) == wf.WaitN(ctx, 1) 1326 _ = wf.Wait(context.TODO()) 1327 1328 // wf.WaitN(ctx, n) err: 等待ctx超时,可以取N个token 1329 _ = wf.WaitN(context.TODO(), 5) 1330 } 1331 ``` 1332 1333 ## structure (常用数据结构) 1334 1335 ### hashset 1336 1337 ```go 1338 package main 1339 1340 func main() { 1341 l := hashset.NewInt() 1342 1343 for _, v := range []int{10, 12, 15} { 1344 l.Add(v) 1345 } 1346 1347 if l.Contains(10) { 1348 fmt.Println("hashset contains 10") 1349 } 1350 1351 l.Range(func(value int) bool { 1352 fmt.Println("hashset range found ", value) 1353 return true 1354 }) 1355 1356 l.Remove(15) 1357 fmt.Printf("hashset contains %d items\r\n", l.Len()) 1358 } 1359 ``` 1360 1361 ### lscq 1362 1363 ```go 1364 package main 1365 1366 func main() { 1367 l := lscq.NewUint64() 1368 1369 ok := l.Enqueue(1) 1370 if !ok { 1371 panic("enqueue failed") 1372 } 1373 v, err := l.Dequeue() 1374 if err != nil { 1375 panic("dequeue failed") 1376 } 1377 fmt.Println("lscq dequeue value:", v) 1378 } 1379 ``` 1380 1381 ### skipmap 1382 1383 ```go 1384 package main 1385 1386 func main() { 1387 m := skipmap.NewInt() 1388 1389 // Correctness. 1390 m.Store(123, "123") 1391 m.Load(123) 1392 m.Delete(123) 1393 m.LoadOrStore(123) 1394 m.LoadAndDelete(123) 1395 } 1396 ``` 1397 1398 ### skipset 1399 1400 ```go 1401 package main 1402 func Example() { 1403 l := skipset.NewInt() 1404 1405 for _, v := range []int{10, 12, 15} { 1406 if l.Add(v) { 1407 fmt.Println("skipset add", v) 1408 } 1409 } 1410 1411 if l.Contains(10) { 1412 fmt.Println("skipset contains 10") 1413 } 1414 1415 l.Range(func(value int) bool { 1416 fmt.Println("skipset range found ", value) 1417 return true 1418 }) 1419 1420 l.Remove(15) 1421 fmt.Printf("skipset contains %d items\r\n", l.Len()) 1422 } 1423 ``` 1424 1425 ### zset 查看对应readme 1426 1427 ## sys 1428 ### mutex 1429 锁相关封装(实现了trylock、重入锁等、重入token锁,还可以获取锁指标数据) 1430 ```go 1431 package main 1432 1433 func main() { 1434 // 获取锁 1435 lk := mutex.NewMutex() 1436 // 尝试获取锁 1437 if lk.TryLock() { 1438 // 获取到锁 1439 defer lk.Unlock() 1440 } 1441 // 获取失败执行其他逻辑 1442 1443 lk.Count() // 获取等待锁的数量 1444 1445 lk.IsLocked() // 锁是否被持有 1446 1447 lk.IsWoken() // 内部是否有等待者被唤醒 1448 1449 lk.IsStarving() // 是否处于饥饿模式 1450 1451 // 重入锁 1452 // 在同一个goroutine可以多次获取 1453 rvlk := mutex.NewRecursiveMutex() 1454 rvlk.Lock() 1455 defer rvlk.Unlock() 1456 1457 // token重入锁 1458 // 传入相同token 可以实现重入功能 1459 tklk := mutex.NewTokenRecursiveMutex() 1460 tklk.Lock(token) 1461 defer tklk.Unlock(token) 1462 } 1463 1464 ``` 1465 1466 ## ternary 1467 > 三元表达式的简单实现 1468 > 可以使用通用版本 **[go-baseutils](https://github.com/songzhibin97/go-baseutils/tree/main/base/bternaryexpr)** 1469 > 注意不要直接使用空指针进行点运算,因为需要先调用它作为入口,解决办法可以是关闭进入的函数 1470 1471 1472 ```go 1473 package main 1474 import "github.com/songzhibin97/gkit/ternary" 1475 1476 func main() { 1477 ternary.ReturnInt(true, 1, 2) // 1 1478 } 1479 ``` 1480 1481 ## timeout 1482 1483 > 各个服务间的超时控制(以及处理时间格式的结构体) 1484 1485 ```go 1486 package main 1487 1488 import ( 1489 "context" 1490 "github.com/songzhibin97/gkit/timeout" 1491 "time" 1492 ) 1493 1494 func main() { 1495 // timeout.Shrink 方法提供全链路的超时控制 1496 // 只需要传入一个父节点的ctx 和需要设置的超时时间,他会帮你确认这个ctx是否之前设置过超时时间, 1497 // 如果设置过超时时间的话会和你当前设置的超时时间进行比较,选择一个最小的进行设置,保证链路超时时间不会被下游影响 1498 // d: 代表剩余的超时时间 1499 // nCtx: 新的context对象 1500 // cancel: 如果是成功真正设置了超时时间会返回一个cancel()方法,未设置成功会返回一个无效的cancel,不过别担心,还是可以正常调用的 1501 d, nCtx, cancel := timeout.Shrink(context.Background(), 5*time.Second) 1502 // d 根据需要判断 1503 // 一般判断该服务的下游超时时间,如果d过于小,可以直接放弃 1504 select { 1505 case <-nCtx.Done(): 1506 cancel() 1507 default: 1508 // ... 1509 } 1510 _ = d 1511 } 1512 ``` 1513 其他(目前,GORM还有一个受支持的相关类型.) 1514 > timeout.DbJSON // provides some functionality in db json format 1515 > timeout.DTime // provides some functionality in db 15:04:05 format 1516 > DateStruct // provides some functionality in db 15:04:05 format Embedded in struct mode 1517 > Date // provides some functionality in db 2006-01-02 format 1518 > DateTime // provides some functions in db 2006-01-02 15:04:05 format 1519 > DateTimeStruct // provides some functions in db 2006-01-02 15:04:05 format Embed mode as struct 1520 > timeout.Stamp // provides some functionality in db timestamp format 1521 1522 1523 1524 ```go 1525 package main 1526 1527 import ( 1528 "github.com/songzhibin97/gkit/timeout" 1529 "gorm.io/driver/mysql" 1530 "gorm.io/gorm" 1531 "time" 1532 ) 1533 1534 type GoStruct struct { 1535 DateTime timeout.DateTime 1536 DTime timeout.DTime 1537 Date timeout.Date 1538 } 1539 1540 func main() { 1541 // 参考 https://github.com/go-sql-driver/mysql#dsn-data-source-name 获取详情 1542 dsn := "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local" 1543 db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{}) 1544 if err != nil { 1545 panic(err) 1546 } 1547 db.AutoMigrate(&GoStruct{}) 1548 db.Create(&GoStruct{ 1549 DateTime: timeout.DateTime(time.Now()), 1550 DTime: timeout.DTime(time.Now()), 1551 Date: timeout.Date(time.Now()), 1552 }) 1553 v := &GoStruct{} 1554 db.Find(v) // 成功查出 1555 } 1556 1557 ``` 1558 1559 1560 ## tools 1561 1562 ### bind 1563 ```go 1564 1565 package main 1566 1567 // 为 gin提供一个全能bind工具 1568 import ( 1569 "github.com/songzhibin97/gkit/tools/bind" 1570 1571 "github.com/gin-gonic/gin" 1572 ) 1573 1574 type Test struct { 1575 Json string `json:"json" form:"json,default=jjjson"` 1576 Query string `json:"query" form:"query"` 1577 } 1578 1579 func main() { 1580 r := gin.Default() 1581 r.POST("test", func(c *gin.Context) { 1582 t := Test{} 1583 // url : 127.0.0.1:8080/test?query=query 1584 // { 1585 // "json":"json", 1586 // "query":"query" 1587 // } 1588 // err := c.ShouldBindWith(&t, bind.CreateBindAll(c.ContentType()),bind.) 1589 // 自定义binding对象 1590 // err := c.ShouldBindWith(&t, bind.CreateBindAll(c.ContentType(),bind.SetSelectorParse([]bind.Binding{}))) 1591 if err != nil { 1592 c.JSON(200, err) 1593 return 1594 } 1595 c.JSON(200, t) 1596 }) 1597 r.Run(":8080") 1598 } 1599 ``` 1600 1601 ### votodo 1602 ```go 1603 package main 1604 1605 import "github.com/songzhibin97/gkit/tools/vto" 1606 1607 type CP struct { 1608 Z1 int `default:"1"` 1609 Z2 string `default:"z2"` 1610 } 1611 1612 func main() { 1613 c1 := CP{ 1614 Z1: 22, 1615 Z2: "33", 1616 } 1617 c2 := CP{} 1618 c3 := CP{} 1619 _ = vto.VoToDo(&c2,&c1) 1620 // c2 CP{ Z1: 22, Z2: "33"} 1621 // 相同名称相同类型的执行复制 1622 // 一定要dst、src 必须传指针类型 1623 1624 // v1.1.2 新增default标签 1625 _ = vto.VoToDo(&c2,&c3) 1626 // c2 CP{ Z1: 1, Z2: "z2"} 1627 // 相同名称相同类型的执行复制 1628 // 一定要dst、src 必须传指针类型 1629 1630 } 1631 ``` 1632 1633 ### votodoPlus 1634 1635 增加了字段&tag&默认值 1636 1637 1638 ## window 1639 1640 提供指标窗口 1641 ```go 1642 package main 1643 1644 import ( 1645 "fmt" 1646 "github.com/songzhibin97/gkit/window" 1647 "time" 1648 ) 1649 func main() { 1650 w := window.NewWindow() 1651 slice := []window.Index{ 1652 {Name: "1", Score: 1}, {Name: "2", Score: 2}, 1653 {Name: "2", Score: 2}, {Name: "3", Score: 3}, 1654 {Name: "2", Score: 2}, {Name: "3", Score: 3}, 1655 {Name: "4", Score: 4}, {Name: "3", Score: 3}, 1656 {Name: "5", Score: 5}, {Name: "2", Score: 2}, 1657 {Name: "6", Score: 6}, {Name: "5", Score: 5}, 1658 } 1659 /* 1660 [{1 1} {2 2}] 1661 [{2 4} {3 3} {1 1}] 1662 [{1 1} {2 6} {3 6}] 1663 [{3 9} {4 4} {1 1} {2 6}] 1664 [{1 1} {2 8} {3 9} {4 4} {5 5}] 1665 [{5 10} {3 9} {2 6} {4 4} {6 6}] 1666 */ 1667 for i := 0; i < len(slice); i += 2 { 1668 w.AddIndex(slice[i].Name, slice[i].Score) 1669 w.AddIndex(slice[i+1].Name, slice[i+1].Score) 1670 time.Sleep(time.Second) 1671 fmt.Println(w.Show()) 1672 } 1673 } 1674 ``` 1675 1676 1677 ## trace 1678 链路追踪 1679 1680 ```go 1681 package main 1682 1683 import ( 1684 "context" 1685 "fmt" 1686 1687 gtrace "github.com/songzhibin97/gkit/trace" 1688 "go.opentelemetry.io/otel/trace" 1689 ) 1690 1691 type _Transport struct { 1692 } 1693 1694 func (tr *_Transport) Get(key string) string { 1695 panic("implement me") 1696 } 1697 1698 func (tr *_Transport) Set(key string, value string) { 1699 panic("implement me") 1700 } 1701 1702 func (tr *_Transport) Keys() []string { 1703 panic("implement me") 1704 } 1705 func main() { 1706 // trace.WithServer() 服务端使用中间件 1707 // trace.WithClient() 客户端使用中间件 1708 tracer := gtrace.NewTracer(trace.SpanKindServer) 1709 ctx, span := tracer.Start(context.Background(), "使用gkit", &_Transport{}) 1710 fmt.Println(span) 1711 defer tracer.End(ctx, span, "replay", nil) 1712 } 1713 ``` 1714 1715 ## watching 1716 1717 系统监控(包含cpu、mum、gc、goroutine等窗口监控,在预设波动值后dump pprof)