Compare commits

...

2 Commits

9 changed files with 374 additions and 8 deletions

View File

@ -7,4 +7,42 @@
package gcache
import (
"sync"
"time"
"git.hexq.cn/tiglog/golib/helper"
)
const default_cache_size = 1024
// 本地内存缓存
type localCacheAdapter struct {
mu sync.Mutex
data map[string][]byte
}
func NewLocalCacheAdapter() ICacheAdapter {
return &localCacheAdapter{
data: make(map[string][]byte, default_cache_size),
}
}
func (c *localCacheAdapter) Get(key string, dest interface{}) error {
val, ok := c.data[key]
if ok {
helper.Scan(val, dest)
}
return nil
}
func (c *localCacheAdapter) Set(key string, val interface{}, ttl time.Duration) error {
return nil
}
func (c *localCacheAdapter) Has(key string) bool {
return false
}
func (c *localCacheAdapter) Del(keys ...string) (int64, error) {
return 0, nil
}

View File

@ -7,4 +7,46 @@
package gcache
import (
"context"
"sync"
"time"
"github.com/go-redis/redis/v8"
)
// 使用 redis 服务缓存
type redisCacheAdapter struct {
mu sync.Mutex
redis *redis.Client
}
func NewRedisCacheAdapter(rds *redis.Client) ICacheAdapter {
return &redisCacheAdapter{
redis: rds,
}
}
func (c *redisCacheAdapter) Get(key string, dest interface{}) error {
cmd := c.redis.Get(context.Background(), key)
return cmd.Scan(dest)
}
func (c *redisCacheAdapter) Set(key string, val interface{}, ttl time.Duration) error {
cmd := c.redis.Set(context.Background(), key, val, ttl)
return cmd.Err()
}
func (c *redisCacheAdapter) Has(key string) bool {
cmd := c.redis.Exists(context.Background(), key)
result, _ := cmd.Result()
if result == 1 {
return true
}
return false
}
func (c *redisCacheAdapter) Del(keys ...string) (int64, error) {
cmd := c.redis.Del(context.Background(), keys...)
return cmd.Result()
}

View File

@ -7,10 +7,51 @@
package gcache
import (
"sync"
"time"
"github.com/go-redis/redis/v8"
)
type Engine struct {
ICacheAdapter
client ICacheAdapter
}
var once sync.Once
var engine *Engine
func New(adapter ICacheAdapter) *Engine {
return &Engine{adapter}
once.Do(func() {
engine = &Engine{
client: adapter,
}
})
return engine
}
func NewWithRedis(rds *redis.Client) *Engine {
once.Do(func() {
engine = &Engine{
client: NewRedisCacheAdapter(rds),
}
})
return engine
}
func (e *Engine) Get(key string, dest interface{}) error {
return e.client.Get(key, dest)
}
func (e *Engine) Set(key string, val interface{}, ttl time.Duration) error {
return e.client.Set(key, val, ttl)
}
func (e *Engine) Has(key string) bool {
return e.client.Has(key)
}
func (e *Engine) Del(keys ...string) (int64, error) {
return e.client.Del(keys...)
}

View File

@ -10,9 +10,8 @@ package gcache
import "time"
type ICacheAdapter interface {
Get(key string) (string, error)
Get(key string, dest interface{}) error
Set(key string, val interface{}, exp time.Duration) error
Del(keys ...string) int64
Del(keys ...string) (int64, error)
Has(key string) bool
End()
}

70
gcache/cache_test.go Normal file
View File

@ -0,0 +1,70 @@
//
// cache_test.go
// Copyright (C) 2023 tiglog <me@tiglog.com>
//
// Distributed under terms of the MIT license.
//
package gcache_test
import (
"context"
"fmt"
"os"
"testing"
"time"
"git.hexq.cn/tiglog/golib/gcache"
"git.hexq.cn/tiglog/golib/gtest"
"github.com/go-redis/redis/v8"
)
func getRedis() *redis.Client {
opt, _ := redis.ParseURL(os.Getenv("REDIS_URL"))
return redis.NewClient(opt)
}
func TestRedis(t *testing.T) {
rds := getRedis()
cmd := rds.Ping(context.Background())
ret, err := cmd.Result()
gtest.Nil(t, err)
fmt.Println(ret)
}
func TestCacheNew(t *testing.T) {
rds := getRedis()
cm1 := gcache.NewWithRedis(rds)
cm2 := gcache.NewWithRedis(rds)
gtest.Equal(t, cm1, cm2)
}
func TestRedisAdapter(t *testing.T) {
rds := getRedis()
cm := gcache.NewWithRedis(rds)
key := "foo"
cm.Del(key)
r1 := cm.Has(key)
gtest.False(t, r1)
var err error
val1 := "bar"
err = cm.Set(key, val1, time.Second)
gtest.Nil(t, err)
var r2 string
err = cm.Get(key, &r2)
gtest.Nil(t, err)
gtest.Equal(t, val1, r2)
val2 := 2
err = cm.Set(key, val2, time.Hour)
gtest.Nil(t, err)
var r3 int
err = cm.Get(key, &r3)
gtest.Nil(t, err)
gtest.Equal(t, val2, r3)
n, err := cm.Del(key)
gtest.Nil(t, err)
gtest.Equal(t, int64(1), n)
}

View File

@ -15,12 +15,19 @@
:imagesdir: ./img
**注:** 暂时直接使用 `go-resdis/cache`
== 实现
已于 2023-06-21 实现。
可以基于 `redis` 进行一些基本使用。
== 设计
从使用倒推设计。
== 场景1
=== 场景1
自己管理 `key`:
@ -38,7 +45,7 @@ return data
<1> `get` 值为 `false` 表示没有缓存或缓存已过期
<2> 7200 为缓存有效期(单位为秒),若指定为 0 表示不过期。
== 场景2
=== 场景2
程序自动管理 `key`:

View File

@ -9,6 +9,7 @@ package helper
import (
"strconv"
"unsafe"
)
func AnyToInt(val any, dv int) (int, error) {
@ -115,3 +116,24 @@ func AnyToString(val any, dv string) (string, error) {
return dv, nil
}
// BytesToString converts byte slice to string.
func BytesToString(b []byte) string {
return *(*string)(unsafe.Pointer(&b))
}
func Atoi(b []byte) (int, error) {
return strconv.Atoi(BytesToString(b))
}
func ParseInt(b []byte, base int, bitSize int) (int64, error) {
return strconv.ParseInt(BytesToString(b), base, bitSize)
}
func ParseUint(b []byte, base int, bitSize int) (uint64, error) {
return strconv.ParseUint(BytesToString(b), base, bitSize)
}
func ParseFloat(b []byte, bitSize int) (float64, error) {
return strconv.ParseFloat(BytesToString(b), bitSize)
}

124
helper/scan_helper.go Normal file
View File

@ -0,0 +1,124 @@
//
// scan_helper.go
// Copyright (C) 2023 tiglog <me@tiglog.com>
//
// Distributed under terms of the MIT license.
//
package helper
import (
"encoding"
"fmt"
"time"
)
func Scan(b []byte, v interface{}) error {
switch v := v.(type) {
case nil:
return fmt.Errorf("golib helper: Scan(nil)")
case *string:
*v = BytesToString(b)
return nil
case *[]byte:
*v = b
return nil
case *int:
var err error
*v, err = Atoi(b)
return err
case *int8:
n, err := ParseInt(b, 10, 8)
if err != nil {
return err
}
*v = int8(n)
return nil
case *int16:
n, err := ParseInt(b, 10, 16)
if err != nil {
return err
}
*v = int16(n)
return nil
case *int32:
n, err := ParseInt(b, 10, 32)
if err != nil {
return err
}
*v = int32(n)
return nil
case *int64:
n, err := ParseInt(b, 10, 64)
if err != nil {
return err
}
*v = n
return nil
case *uint:
n, err := ParseUint(b, 10, 64)
if err != nil {
return err
}
*v = uint(n)
return nil
case *uint8:
n, err := ParseUint(b, 10, 8)
if err != nil {
return err
}
*v = uint8(n)
return nil
case *uint16:
n, err := ParseUint(b, 10, 16)
if err != nil {
return err
}
*v = uint16(n)
return nil
case *uint32:
n, err := ParseUint(b, 10, 32)
if err != nil {
return err
}
*v = uint32(n)
return nil
case *uint64:
n, err := ParseUint(b, 10, 64)
if err != nil {
return err
}
*v = n
return nil
case *float32:
n, err := ParseFloat(b, 32)
if err != nil {
return err
}
*v = float32(n)
return err
case *float64:
var err error
*v, err = ParseFloat(b, 64)
return err
case *bool:
*v = len(b) == 1 && b[0] == '1'
return nil
case *time.Time:
var err error
*v, err = time.Parse(time.RFC3339Nano, BytesToString(b))
return err
case *time.Duration:
n, err := ParseInt(b, 10, 64)
if err != nil {
return err
}
*v = time.Duration(n)
return nil
case encoding.BinaryUnmarshaler:
return v.UnmarshalBinary(b)
default:
return fmt.Errorf(
"golib: can't unmarshal %T (consider implementing BinaryUnmarshaler)", v)
}
}

View File

@ -0,0 +1,23 @@
//
// scan_helper_test.go
// Copyright (C) 2023 tiglog <me@tiglog.com>
//
// Distributed under terms of the MIT license.
//
package helper_test
import (
"fmt"
"testing"
"git.hexq.cn/tiglog/golib/helper"
)
func TestScan(t *testing.T) {
var foo = []byte("hello")
var dest string
helper.Scan(foo, &dest)
fmt.Println(dest)
}