From b47ee0316e671c8c95f10c4f4ae2566e5159a88c Mon Sep 17 00:00:00 2001 From: hlccd <56643462+hlccd@users.noreply.github.com> Date: Wed, 29 Dec 2021 00:25:31 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=BA=86=E7=8B=AC=E7=AB=8B?= =?UTF-8?q?=E8=AF=B7=E6=B1=82singleFlight=E7=9A=84=E5=AE=9E=E7=8E=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- goSTL/algorithm/hash.go | 107 ++++++++++++++++ goSTL/algorithm/singleFlight/singleFlight.go | 124 +++++++++++++++++++ 2 files changed, 231 insertions(+) create mode 100644 goSTL/algorithm/hash.go create mode 100644 goSTL/algorithm/singleFlight/singleFlight.go diff --git a/goSTL/algorithm/hash.go b/goSTL/algorithm/hash.go new file mode 100644 index 0000000..87a7742 --- /dev/null +++ b/goSTL/algorithm/hash.go @@ -0,0 +1,107 @@ +package algorithm + +//@Title algorithm +//@Description +// hash函数 +// 定义了一个hash函数类型,该类型可传入一个key并返回其hash值 +// 该包内定义了一些自带类型的hash函数 +// 当使用自定义的数据结构时若不传入hash函数则使用默认的hash函数 +// 若传入类型非系统自带类型,则返回nil同时对数据的插入失败 + +type Hasher func(key interface{}) uint64 + +func GetHash(e interface{}) (hash Hasher) { + if e == nil { + return nil + } + switch e.(type) { + case bool: + return boolHash + case int: + return intHash + case int8: + return int8Hash + case uint8: + return uint8Hash + case int16: + return int16Hash + case uint16: + return uint16Hash + case int32: + return int32Hash + case uint32: + return uint32Hash + case int64: + return int64Hash + case uint64: + return uint64Hash + case float32: + return float32Hash + case float64: + return float64Hash + case complex64: + return complex64Hash + case complex128: + return complex128Hash + case string: + return stringHash + } + return nil +} +func boolHash(key interface{}) uint64 { + if key.(bool) { + return 1 + } + return 0 +} +func intHash(key interface{}) uint64 { + return uint64(key.(int) * key.(int) / 2) +} +func int8Hash(key interface{}) uint64 { + return uint64(key.(int8) * key.(int8) / 2) +} +func uint8Hash(key interface{}) uint64 { + return uint64(key.(uint8) * key.(uint8) / 2) +} +func int16Hash(key interface{}) uint64 { + return uint64(key.(int16) * key.(int16) / 2) +} +func uint16Hash(key interface{}) uint64 { + return uint64(key.(uint16) * key.(uint16) / 2) +} +func int32Hash(key interface{}) uint64 { + return uint64(key.(int32) * key.(int32) / 2) +} +func uint32Hash(key interface{}) uint64 { + return uint64(key.(uint32) * key.(uint32) / 2) +} +func int64Hash(key interface{}) uint64 { + return uint64(key.(int64) * key.(int64) / 2) +} +func uint64Hash(key interface{}) uint64 { + return uint64(key.(uint64) * key.(uint64) / 2) +} +func float32Hash(key interface{}) uint64 { + return uint64(key.(float32) * key.(float32) / 2) +} +func float64Hash(key interface{}) uint64 { + return uint64(key.(float64) * key.(float64) / 2) +} +func complex64Hash(key interface{}) uint64 { + r := uint64(real(key.(complex64))) + i := uint64(imag(key.(complex64))) + return uint64Hash(r) + uint64Hash(i) +} +func complex128Hash(key interface{}) uint64 { + r := uint64(real(key.(complex64))) + i := uint64(imag(key.(complex64))) + return uint64Hash(r) + uint64Hash(i) +} +func stringHash(key interface{}) uint64 { + bs := []byte(key.(string)) + ans := uint64(0) + for i := range bs { + ans += uint64(bs[i] * 251) + } + return ans +} diff --git a/goSTL/algorithm/singleFlight/singleFlight.go b/goSTL/algorithm/singleFlight/singleFlight.go new file mode 100644 index 0000000..a125d2e --- /dev/null +++ b/goSTL/algorithm/singleFlight/singleFlight.go @@ -0,0 +1,124 @@ +package singleFlight + +//@Title singleFlight +//@Description +// 单次请求-single flight +// 利用可重入锁避免对于一个同类的请求进行多次从而导致的缓存击穿的问题 +// 缓存击穿: +// 缓存在某个时间点过期的时候 +// 恰好在这个时间点对这个Key有大量的并发请求过来 +// 这些请求发现缓存过期一般都会从后端DB加载数据并回设到缓存 +// 这个时候大并发的请求可能会瞬间把后端DB压垮。 + +import "sync" + +//呼叫请求结构体 +type call struct { + wg sync.WaitGroup //可重入锁 + val interface{} //请求结果 + err error //错误反馈 +} + +//一组请求工作 +//每一类请求对应一个call,利用其内部的可重入锁避免一类请求在短时间内频繁执行 +//请求组工作由使用者自行分配空间来实现 +type Group struct { + m map[string]*call //一类请求与同一类呼叫的映射表 + mu sync.Mutex //并发控制锁,保证线程安全 +} + +//@title Do +//@description +// 以请求组做接收者 +// 传入一个请求类的key和请求调用函数fn +// 请求时候需要等待之前有的同类请求先完成在进行 +// 防止击穿缓存,对同一个key进行请求时需要分别进行,利用可重入锁实现 +// 请求完成后返回结果和错误信息即可 +//@receiver g *Group 接受者请求组的指针 +//@param key string 请求的关键词key +//@param fn func() (interface{}, error) 请求执行函数 +//@return v interface{} 请求执行得到的结果 +//@return err error 执行请求后的错误信息 +func (g *Group) Do(key string, fn func() (interface{}, error)) (v interface{}, err error) { + g.mu.Lock() + if g.m == nil { + g.m = make(map[string]*call) + } + //判断以key为关键词的该类请求是否存在 + if c, ok := g.m[key]; ok { + g.mu.Unlock() + // 如果请求正在进行中,则等待 + c.wg.Wait() + return c.val, c.err + } + //该类请求不存在,创建个请求 + c := new(call) + // 发起请求前加锁,并将请求添加到请求组内以表示该类请求正在处理 + c.wg.Add(1) + g.m[key] = c + g.mu.Unlock() + //调用请求函数获取内容 + c.val, c.err = fn() + //请求结束 + c.wg.Done() + g.mu.Lock() + //从请求组中删除该呼叫请求 + delete(g.m, key) + g.mu.Unlock() + return c.val, c.err +} + +//@title DoChan +//@description +// 以请求组做接收者 +// 传入一个请求类的key和请求调用函数fn +// 请求时候需要等待之前有的同类请求先完成在进行 +// 防止击穿缓存,对同一个key进行请求时需要分别进行,利用可重入锁实现 +// 返回一个channel,利用fn函数获取到的数据将会传入其中 +// 可利用channel做超时控制 +//@receiver g *Group 接受者请求组的指针 +//@param key string 请求的关键词key +//@param fn func() (interface{}, error) 请求执行函数 +//@return ch chan interface{} 执行结果将会传入其中,可做超时控制 +func (g *Group) DoChan(key string, fn func() (interface{}, error)) (ch chan interface{}) { + ch = make(chan interface{}, 1) + g.mu.Lock() + if g.m == nil { + g.m = make(map[string]*call) + } + if _, ok := g.m[key]; ok { + g.mu.Unlock() + return ch + } + c := new(call) + c.wg.Add(1) // 发起请求前加锁 + g.m[key] = c // 添加到 g.m,表明 key 已经有对应的请求在处理 + g.mu.Unlock() + go func() { + c.val, c.err = fn() // 调用 fn,发起请求 + c.wg.Done() // 请求结束 + g.mu.Lock() + delete(g.m, key) // 更新 g.m + ch <- c.val + g.mu.Unlock() + }() + return ch +} + +//@title ForgetUnshared +//@description +// 以请求组做接收者 +// 传入一个请求类的key +// 如果该key存在于请求组内,则将其删除即可 +// 从而实现遗忘该类请求的目的 +//@receiver g *Group 接受者请求组的指针 +//@param key string 请求的关键词key +//@return nil +func (g *Group) ForgetUnshared(key string) { + g.mu.Lock() + _, ok := g.m[key] + if ok { + delete(g.m, key) + } + g.mu.Unlock() +}