mirror of
https://github.com/charlienet/go-mixed.git
synced 2025-07-18 00:22:41 +08:00
update
This commit is contained in:
31
cache/big_cache.go → cache/bigcache/big_cache.go
vendored
31
cache/big_cache.go → cache/bigcache/big_cache.go
vendored
@ -1,4 +1,4 @@
|
||||
package cache
|
||||
package bigcache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@ -8,8 +8,6 @@ import (
|
||||
"github.com/charlienet/go-mixed/logx"
|
||||
)
|
||||
|
||||
var _ MemCache = &bigCacheClient{}
|
||||
|
||||
type BigCacheConfig struct {
|
||||
Shards int
|
||||
LifeWindow time.Duration
|
||||
@ -49,8 +47,13 @@ func NewBigCache(c BigCacheConfig) (*bigCacheClient, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *bigCacheClient) Get(key string) ([]byte, error) {
|
||||
return c.cache.Get(key)
|
||||
func (c *bigCacheClient) Get(key string) ([]byte, bool) {
|
||||
b, err := c.cache.Get(key)
|
||||
if err == nil {
|
||||
return b, false
|
||||
}
|
||||
|
||||
return b, true
|
||||
}
|
||||
|
||||
func (c *bigCacheClient) Set(key string, entry []byte, expire time.Duration) error {
|
||||
@ -68,9 +71,23 @@ func (c *bigCacheClient) Delete(keys ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *bigCacheClient) Exist(key string) {
|
||||
func (c *bigCacheClient) Exist(key string) bool {
|
||||
_, err := c.cache.Get(key)
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return !errors.Is(err, bigcache.ErrEntryNotFound)
|
||||
}
|
||||
|
||||
func (c *bigCacheClient) Clear() {
|
||||
|
||||
}
|
||||
|
||||
func (c *bigCacheClient) IsNotFound(err error) bool {
|
||||
return errors.Is(err, bigcache.ErrEntryNotFound)
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
|
||||
return !errors.Is(err, bigcache.ErrEntryNotFound)
|
||||
}
|
27
cache/bigcache/big_cache_test.go
vendored
Normal file
27
cache/bigcache/big_cache_test.go
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
package bigcache
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestBigCache(t *testing.T) {
|
||||
r := require.New(t)
|
||||
|
||||
c, err := NewBigCache(BigCacheConfig{})
|
||||
r.Nil(err)
|
||||
|
||||
cacheKey := "a"
|
||||
cacheValue := "bbb"
|
||||
|
||||
c.Set(cacheKey, []byte(cacheValue), time.Second*5)
|
||||
|
||||
r.True(c.Exist(cacheKey))
|
||||
r.False(c.Exist("abb"))
|
||||
|
||||
b, ok := c.Get(cacheKey)
|
||||
r.True(ok)
|
||||
r.Equal(cacheValue, string(b))
|
||||
}
|
170
cache/cache.go
vendored
170
cache/cache.go
vendored
@ -6,62 +6,73 @@ import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/charlienet/go-mixed/bytesconv"
|
||||
"github.com/charlienet/go-mixed/json"
|
||||
"github.com/charlienet/go-mixed/locker"
|
||||
"github.com/charlienet/go-mixed/logx"
|
||||
"golang.org/x/sync/singleflight"
|
||||
)
|
||||
|
||||
var ErrNotFound = errors.New("key not found")
|
||||
|
||||
// 数据加载函数定义
|
||||
type LoadFunc func(context.Context) (any, error)
|
||||
|
||||
type Cache struct {
|
||||
prefix string // 键前缀
|
||||
retry int // 资源获取时的重试次数
|
||||
mem MemCache // 内存缓存
|
||||
distributdCache DistributdCache // 分布式缓存
|
||||
publishSubscribe PublishSubscribe // 发布订阅
|
||||
lock locker.ChanLocker // 资源锁
|
||||
stats *Stats // 缓存命中计数
|
||||
qps *qps // 访问计数
|
||||
logger logx.Logger // 日志记录
|
||||
type ICache interface {
|
||||
}
|
||||
|
||||
func NewCache(opts ...option) *Cache {
|
||||
type Cache struct {
|
||||
prefix string // 键前缀
|
||||
retry int // 资源获取时的重试次数
|
||||
mem MemCache // 内存缓存
|
||||
rds DistributedCache // 远程缓存
|
||||
publishSubscribe PublishSubscribe // 发布订阅
|
||||
group singleflight.Group // singleflight.Group
|
||||
lock locker.ChanLocker // 资源锁
|
||||
stats *Stats // 缓存命中计数
|
||||
qps *qps // 访问计数
|
||||
logger logx.Logger // 日志记录
|
||||
}
|
||||
|
||||
func New(opts ...option) (*Cache, error) {
|
||||
|
||||
c := acquireDefaultCache()
|
||||
for _, f := range opts {
|
||||
if err := f(c); err != nil {
|
||||
return c
|
||||
return c, nil
|
||||
}
|
||||
}
|
||||
|
||||
go c.subscribe()
|
||||
// 未设置内存缓存时,添加默认缓存
|
||||
if c.mem == nil {
|
||||
c.mem = NewTinyLFU(1<<12, time.Second*30)
|
||||
}
|
||||
|
||||
return c
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func (c *Cache) Set(key string, value any, expiration time.Duration) error {
|
||||
if c.mem != nil {
|
||||
bytes, err := bytesconv.Encode(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func (c *Cache) Set(ctx context.Context, key string, value any, expiration time.Duration) error {
|
||||
buf, err := Marshal(value)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.mem.Set(key, bytes, expiration)
|
||||
if c.mem != nil {
|
||||
c.mem.Set(key, buf, expiration)
|
||||
}
|
||||
|
||||
if c.rds != nil {
|
||||
c.rds.Set(ctx, key, buf, expiration)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cache) Get(key string, out any) error {
|
||||
func (c *Cache) Get(ctx context.Context, key string, out any) error {
|
||||
if c.mem != nil {
|
||||
c.getFromMem(key, out)
|
||||
c.getFromMem(key)
|
||||
}
|
||||
|
||||
if c.distributdCache != nil {
|
||||
if err := c.distributdCache.Get(key, out); err != nil {
|
||||
if c.rds != nil {
|
||||
if err := c.rds.Get(ctx, key, out); err != nil {
|
||||
|
||||
}
|
||||
}
|
||||
@ -70,7 +81,7 @@ func (c *Cache) Get(key string, out any) error {
|
||||
}
|
||||
|
||||
func (c *Cache) GetFn(ctx context.Context, key string, out any, fn LoadFunc, expiration time.Duration) (bool, error) {
|
||||
c.Get(key, out)
|
||||
c.Get(ctx, key, out)
|
||||
|
||||
// 多级缓存中未找到时,放置缓存对象
|
||||
ret, err := fn(ctx)
|
||||
@ -78,7 +89,7 @@ func (c *Cache) GetFn(ctx context.Context, key string, out any, fn LoadFunc, exp
|
||||
return false, err
|
||||
}
|
||||
|
||||
c.Set(key, ret, expiration)
|
||||
c.Set(ctx, key, ret, expiration)
|
||||
|
||||
return false, nil
|
||||
}
|
||||
@ -87,32 +98,69 @@ func (c *Cache) Exist(key string) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
func (c *Cache) Delete(key ...string) error {
|
||||
func (c *Cache) Delete(ctx context.Context, key ...string) error {
|
||||
if c.mem != nil {
|
||||
c.mem.Delete(key...)
|
||||
}
|
||||
|
||||
if c.distributdCache != nil {
|
||||
c.distributdCache.Delete(key...)
|
||||
if c.rds != nil {
|
||||
c.rds.Delete(ctx, key...)
|
||||
}
|
||||
|
||||
for _, k := range key {
|
||||
c.group.Forget(k)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Cache) subscribe() {
|
||||
// 清除本地缓存
|
||||
func (c *Cache) ClearMem() {
|
||||
if c.mem != nil {
|
||||
c.mem.Clear()
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) getFromMem(key string, out any) error {
|
||||
bytes, err := c.mem.Get(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
func (c *Cache) Clear() {
|
||||
|
||||
if err := bytesconv.Decode(bytes, out); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
func (c *Cache) Disable() {
|
||||
|
||||
}
|
||||
|
||||
func (c *Cache) Enable() {
|
||||
|
||||
}
|
||||
|
||||
func (c *Cache) getOnce(ctx context.Context, key string) (b []byte, cached bool, err error) {
|
||||
if c.mem != nil {
|
||||
b, ok := c.mem.Get(key)
|
||||
if ok {
|
||||
return b, true, nil
|
||||
}
|
||||
}
|
||||
c.group.Do(key, func() (any, error) {
|
||||
if c.mem != nil {
|
||||
b, ok := c.mem.Get(key)
|
||||
if ok {
|
||||
return b, nil
|
||||
}
|
||||
}
|
||||
|
||||
if c.rds != nil {
|
||||
c.rds.Get(ctx, key, nil)
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (c *Cache) getFromMem(key string) ([]byte, bool) {
|
||||
bytes, cached := c.mem.Get(key)
|
||||
return bytes, cached
|
||||
}
|
||||
|
||||
// 从缓存加载数据
|
||||
@ -125,8 +173,6 @@ func (c *Cache) getFromCache() {
|
||||
// 从数据源加载数据
|
||||
func (c *Cache) getFromSource(ctx context.Context, key string, fn LoadFunc) error {
|
||||
|
||||
// 1. 尝试获取资源锁,如成功获取到锁加载数据
|
||||
// 2. 未获取到锁,等待从缓存中获取
|
||||
ch, ok := c.lock.Get(key)
|
||||
if ok {
|
||||
defer c.lock.Release(key)
|
||||
@ -150,39 +196,3 @@ func (c *Cache) getFromSource(ctx context.Context, key string, fn LoadFunc) erro
|
||||
return c.getFromSource(ctx, key, fn)
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Cache) marshal(value any) ([]byte, error) {
|
||||
switch value := value.(type) {
|
||||
case nil:
|
||||
return nil, nil
|
||||
case []byte:
|
||||
return value, nil
|
||||
case string:
|
||||
return []byte(value), nil
|
||||
}
|
||||
|
||||
b, err := json.Marshal(value)
|
||||
return b, err
|
||||
}
|
||||
|
||||
func (c *Cache) unmarshal(b []byte, value any) error {
|
||||
if len(b) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch value := value.(type) {
|
||||
case nil:
|
||||
return nil
|
||||
case *[]byte:
|
||||
clone := make([]byte, len(b))
|
||||
copy(clone, b)
|
||||
*value = clone
|
||||
return nil
|
||||
case *string:
|
||||
*value = string(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
err := json.Unmarshal(b, value)
|
||||
return err
|
||||
}
|
||||
|
128
cache/cache_builder.go
vendored
128
cache/cache_builder.go
vendored
@ -1,99 +1,81 @@
|
||||
package cache
|
||||
|
||||
import "github.com/charlienet/go-mixed/logx"
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/charlienet/go-mixed/cache/bigcache"
|
||||
"github.com/charlienet/go-mixed/cache/freecache"
|
||||
"github.com/charlienet/go-mixed/logx"
|
||||
)
|
||||
|
||||
const defaultPrefix = "cache"
|
||||
|
||||
|
||||
type option func(*Cache) error
|
||||
|
||||
type options struct {
|
||||
Prefix string
|
||||
}
|
||||
|
||||
func acquireDefaultCache() *Cache {
|
||||
return &Cache{
|
||||
prefix: defaultPrefix,
|
||||
qps: NewQps(),
|
||||
func WithRedis(opts RedisConfig) option {
|
||||
return func(c *Cache) error {
|
||||
if len(opts.Prefix) == 0 {
|
||||
opts.Prefix = defaultPrefix
|
||||
}
|
||||
|
||||
rds := NewRedis(opts)
|
||||
|
||||
c.rds = rds
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
|
||||
defer cancel()
|
||||
return rds.Ping(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
type cacheBuilder struct {
|
||||
prefix string
|
||||
redisOptions RedisConfig
|
||||
bigCacheConfig BigCacheConfig
|
||||
freeSize int
|
||||
publishSubscribe PublishSubscribe
|
||||
log logx.Logger
|
||||
func WithBigCache(opts bigcache.BigCacheConfig) option {
|
||||
return func(c *Cache) error {
|
||||
mem, err := bigcache.NewBigCache(opts)
|
||||
|
||||
c.mem = mem
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
func NewCacheBuilder() *cacheBuilder {
|
||||
return &cacheBuilder{}
|
||||
}
|
||||
func WithFreeCache(size int) option {
|
||||
return func(c *Cache) error {
|
||||
mem := freecache.NewFreeCache(size)
|
||||
c.mem = mem
|
||||
|
||||
func (b *cacheBuilder) WithLogger(log logx.Logger) *cacheBuilder {
|
||||
b.log = log
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *cacheBuilder) WithPrefix(prefix string) *cacheBuilder {
|
||||
b.prefix = prefix
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *cacheBuilder) WithRedis(opts RedisConfig) *cacheBuilder {
|
||||
b.redisOptions = opts
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *cacheBuilder) WithBigCache(opts BigCacheConfig) *cacheBuilder {
|
||||
b.bigCacheConfig = opts
|
||||
return b
|
||||
}
|
||||
|
||||
func (b *cacheBuilder) WithFreeCache(size int) *cacheBuilder {
|
||||
b.freeSize = size
|
||||
return b
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// 使用自定义分布式缓存
|
||||
func WithDistributedCache(c DistributdCache) {
|
||||
|
||||
func WithDistributedCache(rds DistributedCache) option {
|
||||
return func(c *Cache) error {
|
||||
c.rds = rds
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (b *cacheBuilder) WithPublishSubscribe(p PublishSubscribe) *cacheBuilder {
|
||||
b.publishSubscribe = p
|
||||
return b
|
||||
func WithPublishSubscribe(p PublishSubscribe) option {
|
||||
return func(c *Cache) error {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func (b cacheBuilder) Build() (*Cache, error) {
|
||||
var err error
|
||||
cache := acquireDefaultCache()
|
||||
if len(b.prefix) > 0 {
|
||||
cache.prefix = b.prefix
|
||||
func WithLogger(log logx.Logger) option {
|
||||
return func(c *Cache) error {
|
||||
c.logger = log
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func acquireDefaultCache() *Cache {
|
||||
return &Cache{
|
||||
qps: NewQps(),
|
||||
}
|
||||
|
||||
b.redisOptions.Prefix = cache.prefix
|
||||
|
||||
redis := NewRedis(b.redisOptions)
|
||||
if err := redis.Ping(); err != nil {
|
||||
return cache, err
|
||||
}
|
||||
|
||||
var mem MemCache
|
||||
if b.freeSize > 0 {
|
||||
mem = NewFreeCache(b.freeSize)
|
||||
} else {
|
||||
if b.log != nil {
|
||||
b.bigCacheConfig.log = b.log
|
||||
}
|
||||
|
||||
mem, err = NewBigCache(b.bigCacheConfig)
|
||||
}
|
||||
|
||||
cache.distributdCache = redis
|
||||
cache.mem = mem
|
||||
cache.publishSubscribe = b.publishSubscribe
|
||||
cache.logger = b.log
|
||||
|
||||
return cache, err
|
||||
}
|
||||
|
47
cache/cache_builder_test.go
vendored
47
cache/cache_builder_test.go
vendored
@ -3,27 +3,38 @@ package cache
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/charlienet/go-mixed/logx"
|
||||
)
|
||||
|
||||
func TestBuilder(t *testing.T) {
|
||||
cache, err := NewCacheBuilder().
|
||||
WithLogger(logx.NewLogrus(logx.WithFormatter(logx.NewNestedFormatter(logx.NestedFormatterOption{
|
||||
Color: true,
|
||||
})))).
|
||||
WithRedis(RedisConfig{
|
||||
Addrs: []string{"192.168.2.222:6379"},
|
||||
Password: "123456",
|
||||
}).
|
||||
WithBigCache(BigCacheConfig{}).
|
||||
// WithFreeCache(10 * 1024 * 1024).
|
||||
Build()
|
||||
now := time.Now()
|
||||
t.Log(now)
|
||||
t1, _ := time.ParseDuration("9h27m")
|
||||
t1 += time.Hour * 24
|
||||
t2, _ := time.ParseDuration("16h28m")
|
||||
t.Log(t1)
|
||||
t.Log(t2)
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
f := time.Date(2022, time.December, 12, 8, 0, 0, 0, time.Local)
|
||||
t.Log(f.Sub(time.Now()))
|
||||
|
||||
u := SimpleUser{FirstName: "Radomir", LastName: "Sohlich"}
|
||||
t.Log(cache.Set(defaultKey, u, time.Minute*10))
|
||||
// cache, err := New(
|
||||
// WithLogger(logx.NewLogrus(logx.WithNestedFormatter(logx.NestedFormatterOption{
|
||||
// Color: true,
|
||||
// }))).
|
||||
// UseRedis(RedisConfig{
|
||||
// Addrs: []string{"192.168.2.222:6379"},
|
||||
// Password: "123456",
|
||||
// }).
|
||||
// UseBigCache(bigcache.BigCacheConfig{}).
|
||||
// Build()
|
||||
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
// ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
|
||||
// defer cancel()
|
||||
|
||||
// u := SimpleUser{FirstName: "Radomir", LastName: "Sohlich"}
|
||||
// t.Log(cache.Set(ctx, defaultKey, u, time.Minute*10))
|
||||
}
|
||||
|
12
cache/cache_preload.go
vendored
Normal file
12
cache/cache_preload.go
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
package cache
|
||||
|
||||
import "context"
|
||||
|
||||
// PreLoadItem 预加载数据项
|
||||
type PreLoadItem struct {
|
||||
Key string
|
||||
Value any
|
||||
}
|
||||
|
||||
// PreloadFunc 数据预加载函数定义
|
||||
type PreloadFunc func(context.Context) ([]PreLoadItem, error)
|
85
cache/cache_test.go
vendored
85
cache/cache_test.go
vendored
@ -6,9 +6,6 @@ import (
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/charlienet/go-mixed/bytesconv"
|
||||
"github.com/charlienet/go-mixed/logx"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -16,25 +13,26 @@ var (
|
||||
)
|
||||
|
||||
func TestNewCache(t *testing.T) {
|
||||
c, err := NewCacheBuilder().
|
||||
WithRedis(RedisConfig{
|
||||
Addrs: []string{"192.168.2.222:6379"},
|
||||
Password: "123456",
|
||||
}).
|
||||
WithPrefix("cache_test").
|
||||
WithLogger(logx.NewLogrus()).
|
||||
Build()
|
||||
// c, err := NewCacheBuilder().
|
||||
// UseRedis(RedisConfig{
|
||||
// Addrs: []string{"192.168.2.222:6379"},
|
||||
// Password: "123456",
|
||||
// }).
|
||||
// WithPrefix("cache_test").
|
||||
// WithLogger(logx.NewLogrus()).
|
||||
// Build()
|
||||
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// if err != nil {
|
||||
// t.Fatal(err)
|
||||
// }
|
||||
|
||||
c.Set("abc", "value", time.Minute*10)
|
||||
// ctx := context.Background()
|
||||
// c.Set(ctx, "abc", "value", time.Minute*10)
|
||||
|
||||
var s string
|
||||
c.Get("abc", &s)
|
||||
// var s string
|
||||
// c.Get(ctx, "abc", &s)
|
||||
|
||||
t.Log(s)
|
||||
// t.Log(s)
|
||||
}
|
||||
|
||||
type SimpleUser struct {
|
||||
@ -43,45 +41,43 @@ type SimpleUser struct {
|
||||
}
|
||||
|
||||
func TestMemCache(t *testing.T) {
|
||||
b, _ := NewBigCache(BigCacheConfig{})
|
||||
var mems = []MemCache{
|
||||
NewFreeCache(10 * 1024 * 1024),
|
||||
b,
|
||||
}
|
||||
// b, _ := bigcache.NewBigCache(bigcache.BigCacheConfig{})
|
||||
// var mems = []MemCache{
|
||||
// NewFreeCache(10 * 1024 * 1024),
|
||||
// b,
|
||||
// }
|
||||
|
||||
u := SimpleUser{FirstName: "Radomir", LastName: "Sohlich"}
|
||||
encoded, _ := bytesconv.Encode(u)
|
||||
for _, m := range mems {
|
||||
m.Set(defaultKey, encoded, time.Second)
|
||||
ret, err := m.Get(defaultKey)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
// u := SimpleUser{FirstName: "Radomir", LastName: "Sohlich"}
|
||||
// encoded, _ := bytesconv.Encode(u)
|
||||
// for _, m := range mems {
|
||||
// m.Set(defaultKey, encoded, time.Second)
|
||||
// ret, _ := m.Get(defaultKey)
|
||||
|
||||
var u2 SimpleUser
|
||||
bytesconv.Decode(ret, &u2)
|
||||
t.Log(u2)
|
||||
}
|
||||
// var u2 SimpleUser
|
||||
// bytesconv.Decode(ret, &u2)
|
||||
// t.Log(u2)
|
||||
// }
|
||||
}
|
||||
|
||||
func TestDistributedCache(t *testing.T) {
|
||||
c := NewRedis(RedisConfig{Addrs: []string{"192.168.2.222:6379"}, DB: 6, Password: "123456", Prefix: "abcdef"})
|
||||
|
||||
if err := c.Ping(); err != nil {
|
||||
ctx := context.Background()
|
||||
if err := c.Ping(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
t.Log(c.Exist(defaultKey))
|
||||
t.Log(c.Exist(ctx, defaultKey))
|
||||
|
||||
u := SimpleUser{FirstName: "redis client"}
|
||||
|
||||
var u2 SimpleUser
|
||||
c.Get(defaultKey, &u2)
|
||||
c.Get(ctx, defaultKey, &u2)
|
||||
|
||||
c.Set(defaultKey, u, time.Minute*10)
|
||||
t.Log(c.Exist(defaultKey))
|
||||
c.Set(ctx, defaultKey, u, time.Minute*10)
|
||||
t.Log(c.Exist(ctx, defaultKey))
|
||||
|
||||
if err := c.Get(defaultKey, &u2); err != nil {
|
||||
if err := c.Get(ctx, defaultKey, &u2); err != nil {
|
||||
t.Fatal("err:", err)
|
||||
}
|
||||
t.Logf("%+v", u2)
|
||||
@ -136,10 +132,9 @@ func load() (any, error) {
|
||||
}
|
||||
|
||||
func buildCache() *Cache {
|
||||
c, err := NewCacheBuilder().
|
||||
WithFreeCache(10 * 1024 * 1024).
|
||||
WithRedis(RedisConfig{Addrs: []string{"192.168.2.222:6379"}, DB: 6, Password: "123456"}).
|
||||
Build()
|
||||
c, err := New(
|
||||
WithFreeCache(10*1024*1024),
|
||||
WithRedis(RedisConfig{Addrs: []string{"192.168.2.222:6379"}, DB: 6, Password: "123456"}))
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
10
cache/distributd_cache.go
vendored
10
cache/distributd_cache.go
vendored
@ -1,10 +0,0 @@
|
||||
package cache
|
||||
|
||||
import "time"
|
||||
|
||||
type DistributdCache interface {
|
||||
Get(key string, out any) error
|
||||
Set(key string, value any, expiration time.Duration) error
|
||||
Delete(key ...string) error
|
||||
Ping() error
|
||||
}
|
14
cache/distributed_cache.go
vendored
Normal file
14
cache/distributed_cache.go
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
)
|
||||
|
||||
// 分布式缓存接口
|
||||
type DistributedCache interface {
|
||||
Get(ctx context.Context, key string, out any) error
|
||||
Set(ctx context.Context, key string, value any, expiration time.Duration) error
|
||||
Delete(ctx context.Context, key ...string) error
|
||||
Ping(ctx context.Context) error
|
||||
}
|
10
cache/empty_cache_adaper.go
vendored
Normal file
10
cache/empty_cache_adaper.go
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
package cache
|
||||
|
||||
import "context"
|
||||
|
||||
// var emptyCache DistributedCache = &emptyCacheAdapter{}
|
||||
|
||||
type emptyCacheAdapter struct {
|
||||
}
|
||||
|
||||
func (*emptyCacheAdapter) Delete(ctx context.Context, keys ...string) {}
|
@ -1,4 +1,4 @@
|
||||
package cache
|
||||
package freecache
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@ -10,8 +10,6 @@ import (
|
||||
|
||||
const defaultSize = 10 * 1024 * 1024 // 10M
|
||||
|
||||
var _ MemCache = &freeCache{}
|
||||
|
||||
type freeCache struct {
|
||||
cache *freecache.Cache
|
||||
}
|
||||
@ -29,8 +27,12 @@ func NewFreeCache(size int) *freeCache {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *freeCache) Get(key string) ([]byte, error) {
|
||||
return c.cache.Get([]byte(key))
|
||||
func (c *freeCache) Get(key string) ([]byte, bool) {
|
||||
b, err := c.cache.Get([]byte(key))
|
||||
if err != nil {
|
||||
return b, false
|
||||
}
|
||||
return b, true
|
||||
}
|
||||
|
||||
func (c *freeCache) Set(key string, value []byte, d time.Duration) error {
|
||||
@ -54,6 +56,10 @@ func (c *freeCache) Exist(key string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *freeCache) Clear() {
|
||||
|
||||
}
|
||||
|
||||
func (c *freeCache) IsNotFound(err error) bool {
|
||||
return errors.Is(err, freecache.ErrNotFound)
|
||||
}
|
1
cache/freecache/free_cache_test.go
vendored
Normal file
1
cache/freecache/free_cache_test.go
vendored
Normal file
@ -0,0 +1 @@
|
||||
package freecache
|
7
cache/local_cache.go
vendored
7
cache/local_cache.go
vendored
@ -1,7 +0,0 @@
|
||||
package cache
|
||||
|
||||
type LocalCache interface {
|
||||
Set(key string, data []byte)
|
||||
Get(key string) ([]byte, bool)
|
||||
Del(key string)
|
||||
}
|
2
cache/lru.go
vendored
Normal file
2
cache/lru.go
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
package cache
|
||||
|
5
cache/mem.go
vendored
5
cache/mem.go
vendored
@ -3,7 +3,8 @@ package cache
|
||||
import "time"
|
||||
|
||||
type MemCache interface {
|
||||
Get(key string) ([]byte, error)
|
||||
Set(key string, entry []byte, expire time.Duration) error
|
||||
Get(key string) ([]byte, bool)
|
||||
Set(key string, b []byte, expire time.Duration) error
|
||||
Delete(key ...string) error
|
||||
Clear()
|
||||
}
|
||||
|
43
cache/msg_pack.go
vendored
Normal file
43
cache/msg_pack.go
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
package cache
|
||||
|
||||
import (
|
||||
"github.com/vmihailenco/msgpack/v5"
|
||||
)
|
||||
|
||||
func Marshal(v any) ([]byte, error) {
|
||||
switch v := v.(type) {
|
||||
case nil:
|
||||
return nil, nil
|
||||
case []byte:
|
||||
return v, nil
|
||||
case string:
|
||||
return []byte(v), nil
|
||||
}
|
||||
|
||||
b, err := msgpack.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return b, err
|
||||
}
|
||||
|
||||
func Unmarshal(b []byte, v any) error {
|
||||
if len(b) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
switch v := v.(type) {
|
||||
case nil:
|
||||
return nil
|
||||
case *[]byte:
|
||||
clone := make([]byte, len(b))
|
||||
copy(clone, b)
|
||||
*v = clone
|
||||
case *string:
|
||||
*v = string(b)
|
||||
return nil
|
||||
}
|
||||
|
||||
return msgpack.Unmarshal(b, v)
|
||||
}
|
6
cache/qps.go
vendored
6
cache/qps.go
vendored
@ -31,7 +31,9 @@ func (q *qps) statisticsTotal() {
|
||||
}
|
||||
}()
|
||||
|
||||
ticker := time.NewTicker(time.Second)
|
||||
ticker := time.NewTicker(time.Minute)
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
q.all.viewTotal = atomic.SwapInt64(&q.all.total, 0)
|
||||
q.memoryTotal.viewTotal = atomic.SwapInt64(&q.memoryTotal.total, 0)
|
||||
@ -39,5 +41,7 @@ func (q *qps) statisticsTotal() {
|
||||
q.redisTotal.viewTotal = atomic.SwapInt64(&q.redisTotal.total, 0)
|
||||
q.redisHit.viewTotal = atomic.SwapInt64(&q.redisHit.total, 0)
|
||||
q.sourceTotal.viewTotal = atomic.SwapInt64(&q.sourceTotal.total, 0)
|
||||
|
||||
// percnt := 0
|
||||
}
|
||||
}
|
||||
|
8
cache/readme.md
vendored
8
cache/readme.md
vendored
@ -14,6 +14,14 @@
|
||||
|
||||
## 使用方式
|
||||
|
||||
创建
|
||||
|
||||
```go
|
||||
cache.New().UseRedis().UseBigCache().Build()
|
||||
|
||||
```
|
||||
|
||||
```go
|
||||
Cache.Get(key, dist, func() (bool,error){}, options func(){})
|
||||
Cache.GetFn(context, key, dist, func() (bool, error))
|
||||
```
|
||||
|
14
cache/redis.go
vendored
14
cache/redis.go
vendored
@ -54,7 +54,7 @@ func NewRedis(c RedisConfig) *redisClient {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *redisClient) Get(key string, out any) error {
|
||||
func (c *redisClient) Get(cxt context.Context, key string, out any) error {
|
||||
val, err := c.client.Get(context.Background(), c.getKey(key)).Result()
|
||||
if errors.Is(err, redis.Nil) {
|
||||
return ErrNotFound
|
||||
@ -72,23 +72,23 @@ func (c *redisClient) Get(key string, out any) error {
|
||||
return json.Unmarshal(bytesconv.StringToBytes(val), out)
|
||||
}
|
||||
|
||||
func (c *redisClient) Set(key string, value any, expiration time.Duration) error {
|
||||
func (c *redisClient) Set(ctx context.Context, key string, value any, expiration time.Duration) error {
|
||||
j, _ := json.Marshal(value)
|
||||
return c.client.Set(context.Background(), c.getKey(key), j, expiration).Err()
|
||||
}
|
||||
|
||||
func (c *redisClient) Exist(key string) (bool, error) {
|
||||
func (c *redisClient) Exist(ctx context.Context, key string) (bool, error) {
|
||||
val, err := c.client.Exists(context.Background(), c.getKey(key)).Result()
|
||||
return val > 0, err
|
||||
}
|
||||
|
||||
func (c *redisClient) Delete(key ...string) error {
|
||||
func (c *redisClient) Delete(ctx context.Context, key ...string) error {
|
||||
keys := make([]string, 0, len(key))
|
||||
for _, k := range key {
|
||||
keys = append(keys, c.getKey(k))
|
||||
}
|
||||
|
||||
_ , err := c.client.Del(context.Background(), keys...).Result()
|
||||
_, err := c.client.Del(context.Background(), keys...).Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -96,8 +96,8 @@ func (c *redisClient) Delete(key ...string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *redisClient) Ping() error {
|
||||
_, err := c.client.Ping(context.Background()).Result()
|
||||
func (c *redisClient) Ping(ctx context.Context) error {
|
||||
_, err := c.client.Ping(ctx).Result()
|
||||
return err
|
||||
}
|
||||
|
||||
|
4
cache/stats.go
vendored
4
cache/stats.go
vendored
@ -7,11 +7,11 @@ type Stats struct {
|
||||
Misses uint64
|
||||
}
|
||||
|
||||
func (s *Stats) AddHits() {
|
||||
func (s *Stats) IncrementHits() {
|
||||
atomic.AddUint64(&s.Hits, 1)
|
||||
}
|
||||
|
||||
func (s *Stats) AddMisses() {
|
||||
func (s *Stats) IncrementMisses() {
|
||||
atomic.AddUint64(&s.Misses, 1)
|
||||
}
|
||||
|
||||
|
22
cache/tiny_lfu.go
vendored
22
cache/tiny_lfu.go
vendored
@ -7,6 +7,8 @@ import (
|
||||
"github.com/vmihailenco/go-tinylfu"
|
||||
)
|
||||
|
||||
var _ MemCache = &TinyLFU{}
|
||||
|
||||
type TinyLFU struct {
|
||||
mu locker.Locker
|
||||
lfu *tinylfu.T
|
||||
@ -21,15 +23,17 @@ func NewTinyLFU(size int, ttl time.Duration) *TinyLFU {
|
||||
}
|
||||
}
|
||||
|
||||
func (c *TinyLFU) Set(key string, b []byte, expire time.Duration) {
|
||||
func (c *TinyLFU) Set(key string, b []byte, expire time.Duration) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
c.lfu.Set(&tinylfu.Item{
|
||||
Key: key,
|
||||
Value: b,
|
||||
Key: key,
|
||||
Value: b,
|
||||
ExpireAt: time.Now().Add(c.ttl),
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *TinyLFU) Get(key string) ([]byte, bool) {
|
||||
@ -44,9 +48,17 @@ func (c *TinyLFU) Get(key string) ([]byte, bool) {
|
||||
return val.([]byte), true
|
||||
}
|
||||
|
||||
func (c *TinyLFU) Del(key string) {
|
||||
func (c *TinyLFU) Delete(keys ...string) error {
|
||||
c.mu.Lock()
|
||||
defer c.mu.Unlock()
|
||||
|
||||
c.lfu.Del(key)
|
||||
for _, k := range keys {
|
||||
c.lfu.Del(k)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *TinyLFU) Clear() {
|
||||
|
||||
}
|
||||
|
Reference in New Issue
Block a user