Compare commits
2 Commits
9de6bda75b
...
4c90ac7490
Author | SHA1 | Date | |
---|---|---|---|
4c90ac7490 | |||
dbda182b10 |
@ -7,4 +7,42 @@
|
|||||||
|
|
||||||
package gcache
|
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
|
package gcache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-redis/redis/v8"
|
||||||
|
)
|
||||||
|
|
||||||
// 使用 redis 服务缓存
|
// 使用 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
|
package gcache
|
||||||
|
|
||||||
|
import (
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/go-redis/redis/v8"
|
||||||
|
)
|
||||||
|
|
||||||
type Engine struct {
|
type Engine struct {
|
||||||
ICacheAdapter
|
client ICacheAdapter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var once sync.Once
|
||||||
|
|
||||||
|
var engine *Engine
|
||||||
|
|
||||||
func New(adapter ICacheAdapter) *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"
|
import "time"
|
||||||
|
|
||||||
type ICacheAdapter interface {
|
type ICacheAdapter interface {
|
||||||
Get(key string) (string, error)
|
Get(key string, dest interface{}) error
|
||||||
Set(key string, val interface{}, exp time.Duration) error
|
Set(key string, val interface{}, exp time.Duration) error
|
||||||
Del(keys ...string) int64
|
Del(keys ...string) (int64, error)
|
||||||
Has(key string) bool
|
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
|
:imagesdir: ./img
|
||||||
|
|
||||||
|
|
||||||
**注:** 暂时直接使用 `go-resdis/cache`
|
== 实现
|
||||||
|
|
||||||
|
已于 2023-06-21 实现。
|
||||||
|
|
||||||
|
可以基于 `redis` 进行一些基本使用。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
== 设计
|
||||||
|
|
||||||
从使用倒推设计。
|
从使用倒推设计。
|
||||||
|
|
||||||
== 场景1
|
=== 场景1
|
||||||
|
|
||||||
自己管理 `key`:
|
自己管理 `key`:
|
||||||
|
|
||||||
@ -38,7 +45,7 @@ return data
|
|||||||
<1> `get` 值为 `false` 表示没有缓存或缓存已过期
|
<1> `get` 值为 `false` 表示没有缓存或缓存已过期
|
||||||
<2> 7200 为缓存有效期(单位为秒),若指定为 0 表示不过期。
|
<2> 7200 为缓存有效期(单位为秒),若指定为 0 表示不过期。
|
||||||
|
|
||||||
== 场景2
|
=== 场景2
|
||||||
|
|
||||||
程序自动管理 `key`:
|
程序自动管理 `key`:
|
||||||
|
|
||||||
|
@ -9,6 +9,7 @@ package helper
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AnyToInt(val any, dv int) (int, error) {
|
func AnyToInt(val any, dv int) (int, error) {
|
||||||
@ -115,3 +116,24 @@ func AnyToString(val any, dv string) (string, error) {
|
|||||||
|
|
||||||
return dv, nil
|
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