代码拉取完成,页面将自动刷新
package cache
import (
"errors"
"log/slog"
"time"
"golang.org/x/sync/singleflight"
)
// pluginDo 自动初始化回调缓存插件
type pluginDo[T any] struct {
store CacheIO
key string
tags []string
expire time.Duration
forgetTime time.Duration
call func() (callVal T, callErr error)
}
// PluginDo 缓存防击穿 singleflight 默认只能尝试一次,阻塞请求,直到执行完毕。
// forgetTime 每个PluginDo实例可以自定义, 默认为 0,即singleflight默认方式。
// - 如设置为 100ms, 表示100ms内的请求最多发起1次请求,每秒最大并发:10 RPS
// - 如设置为 200ms,表示200ms内的请求最多发起1次请求,每秒最大并发:5 RPS
// - 如设置为 500ms,表示500ms内的请求最多发起1次请求,每秒最大并发:2 RPS
// - 如设置为 1000ms,表示1000ms内的请求最多发起1次请求,每秒最大并发:1 RPS
var pluginDoGroup singleflight.Group
// PluginDo 获取一个缓存自动初始化回调缓存包装接口 ( 使用默认 DefaultStore 实例 )
func PluginDo[T any]() *pluginDo[T] {
return usePluginDo[T](GetDefault())
}
// PluginDoMemory 获取一个缓存自动初始化回调缓存包装接口 ( 使用默认 StoreMemory 实例 )
func PluginDoMemory[T any]() *pluginDo[T] {
return usePluginDo[T](GetDefaultMemory())
}
// PluginDoRedis 获取一个缓存自动初始化回调缓存包装接口 ( 使用默认 StoreRedis 实例 )
func PluginDoRedis[T any]() *pluginDo[T] {
return usePluginDo[T](GetDefaultRedis())
}
// PluginDoWith 获取一个缓存自动初始化回调缓存包装接口 ( 使用指定 Store CacheIO 实例 )
func PluginDoWith[T any](store CacheIO) *pluginDo[T] {
return usePluginDo[T](store)
}
func usePluginDo[T any](store CacheIO) *pluginDo[T] {
if store == nil {
panic(ErrStoreInstanceIsNil)
}
return &pluginDo[T]{store: store, expire: -1}
}
// Expire 过期时间 ( 未设置时默认使用缓存Store默认过期时间 )
func (c *pluginDo[T]) Expire(expire time.Duration) *pluginDo[T] {
c.expire = expire
return c
}
func (c *pluginDo[T]) Tags(tags ...string) *pluginDo[T] {
c.tags = tags
return c
}
func (c *pluginDo[T]) Key(key string) *pluginDo[T] {
if key == "" {
panic(ErrKeyCanNotEmpty)
}
c.key = key
return c
}
func (c *pluginDo[T]) ForgetTime(forgetTime time.Duration) *pluginDo[T] {
c.forgetTime = forgetTime
return c
}
func (c *pluginDo[T]) Do(call func() (callVal T, callErr error)) *pluginDo[T] {
c.call = call
return c
}
func (c *pluginDo[T]) Read() (cacheVal T, err error) {
if c.key == "" {
panic(ErrKeyCanNotEmpty)
}
if c.store == nil {
panic(ErrStoreInstanceIsNil)
}
// 创建局部变量避免竞态
pc := getEntryPC(3, c.store, true)
key := c.key
forgetTime := c.forgetTime
var callVal any
callVal, err, _ = pluginDoGroup.Do(key, func() (any, error) {
if forgetTime > 0 {
go func(k string, d time.Duration) {
<-time.After(d)
pluginDoGroup.Forget(k)
}(key, forgetTime)
}
//缓存读取
if err = c.store.scanWithPC(pc, key, &cacheVal); err == nil {
return cacheVal, nil
} else {
// 清理可能存在的旧缓存
c.store.Del(key)
}
v, e := c.call()
if e != nil {
return nil, e
}
c.setCache(key, v)
return v, nil
})
if err != nil {
logWrapAttr(pc, slog.LevelError, "cache:pluginDo:Call() Failed",
slog.String("key", key),
slog.String("error", err.Error()))
err = errors.New("cache:pluginDo:Call() Failed," + err.Error())
return
}
return callVal.(T), nil
}
func (c *pluginDo[T]) setCache(key string, val T) {
if len(c.tags) > 0 {
if c.expire >= 0 {
c.store.Tags(c.tags...).SetExpire(key, val, c.expire)
} else {
c.store.Tags(c.tags...).Set(key, val)
}
} else {
if c.expire >= 0 {
c.store.SetExpire(key, val, c.expire)
} else {
c.store.Set(key, val)
}
}
}
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。