Compare commits
2 Commits
9de6bda75b
...
4c90ac7490
Author | SHA1 | Date | |
---|---|---|---|
4c90ac7490 | |||
dbda182b10 |
@ -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
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
|
@ -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...)
|
||||
}
|
||||
|
@ -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
70
gcache/cache_test.go
Normal 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)
|
||||
}
|
@ -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`:
|
||||
|
||||
|
@ -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
124
helper/scan_helper.go
Normal 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)
|
||||
}
|
||||
}
|
23
helper/scan_helper_test.go
Normal file
23
helper/scan_helper_test.go
Normal 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)
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user