go实现一个内存缓存系统

1. 支持设定过期时间,精确到秒

2. 支持设定最大内存,当内存超出时作出适当的处理

3. 支持并发安全

4. 按照以下接口要求实现

type Cache interface {
	// size 1KB 100KB 1MB 2MB 1GB
	SetMaxMemory(size string) bool
	// 将value写入缓存
	Set(key string, val interface{}, expire time.Duration)
	// 根据key值获取value
	Get(key string) (interface{},bool)
	// 删除key值
	Del(key string) bool
	// 判断key值是否存在
	Exists(key string) bool
	// 清空所有key
	Flush() bool
	// 获取缓存中所有key的数量
	Keys() int64
}

5. 使用示例

cache:=NewMemCache()
cache.SetMaxMemory("100MB")
cache.Set("int",1)
cache.Set("bool",false)
cache.Set("data",map[string]interface{}{"a":1})
cache.Get("int")
cache.Del("int")
cache.Flush()
cache.Keys()

log.println的用法

log.Println函数是Go标准库中用于记录日志的一种方式,通常用于在程序中输出日志信息。它的用法非常简单,可以根据需要输出不同级别的日志信息。下面是log.Println的几种常见用法:

1. 输出一条普通日志

log.Println("This is a normal log message.")

这将在标准输出中打印一条普通的日志信息。

2. 输出带有时间戳的日志

log.SetFlags(log.LstdFlags) // 设置日志格式,包括时间戳 log.Println("This is a log message with a timestamp.")

这将在日志信息前面添加时间戳,以便记录日志的时间。

3. 输出错误日志

err := someFunction() if err != nil { log.Println("Error:", err) }

这将在发生错误时输出相应的错误日志信息。

4. 自定义日志级别

const (
    LogLevelInfo = iota
    LogLevelWarning
    LogLevelError
)

func logMessage(level int, message string) {
    switch level {
    case LogLevelInfo:
        log.Println("INFO:", message)
    case LogLevelWarning:
        log.Println("WARNING:", message)
    case LogLevelError:
        log.Println("ERROR:", message)
    }
}

logMessage(LogLevelWarning, "This is a warning message.")

这个例子演示了如何根据不同的日志级别输出不同类型的日志信息。

5. 输出到文件

file, err := os.Create("logfile.txt")
if err != nil {
    log.Fatal("Cannot create log file:", err)
}
defer file.Close()

log.SetOutput(file)
log.Println("This log message will be written to the file.")

这将日志输出到文件中,而不是默认的标准输出。

这些是log.Println函数的几种常见用法,可以根据需要选择适合自己场景的用法。

go语言的变量名要求很有意思,需要去复习一下

main.go文件

package main

import (
	"test1/cache_server"
	"time"
)

func main() {
	cache := cache_server.NewMemCache()
	cache.SetMaxMemory("300GB")
	//
	cache.Set("int", 1, time.Second)
	cache.Set("bool", false, time.Second)
	cache.Set("data", map[string]interface{}{"a": 1}, time.Second)

	cache.Set("int", 1)
	cache.Set("bool", false)
	cache.Set("data", map[string]interface{}{"a": 1})

	cache.Get("int")
	cache.Del("int")
	cache.Flush()
	cache.Keys()
	//cache.GetValSize(1)
	//cache.GetValSize(false)
	//cache.GetValSize("张三")
	//cache.GetValSize(map[string]string{
	//	"name": "张三",
	//	"addr": "张三",
	//})
}

util.go文件

package cache

import (
	"encoding/json"
	"fmt"
	"log"
	"regexp"
	"strconv"
	"strings"
)

const (
	/*
		iota 是 Go 语言中的预定义标识符,用于枚举常量的自增值生成器。在常量声明中,iota 从 0 开始自动递增,每遇到一个 const 关键字都会被重置为 0。
	*/
	B = 1 << (iota * 10)
	KB
	MB
	GB
	TB
	PB
)

func ParseSize(size string) (int64, string) {
	// 默认大小为100MB
	re, _ := regexp.Compile("[0-9]+")
	uint := string(re.ReplaceAll([]byte(size), []byte("")))
	num, _ := strconv.ParseInt(strings.Replace(size, uint, "", 1), 10, 64)
	uint = strings.ToUpper(uint)
	var byteNum int64 = 0
	switch uint {
	case "B":
		byteNum = num
	case "KB":
		byteNum = num * KB
	case "MB":
		byteNum = num * MB
	case "GB":
		byteNum = num * GB
	case "TB":
		byteNum = num * TB
	case "PB":
		byteNum = num * PB
	default:
		num = 0
	}
	if num == 0 {
		log.Println("ParseSize 仅支持B,KB,MB,GB,TB,PB")
		num = 10
		byteNum = num * MB
		uint = "MB"
	}
	sizeStr := strconv.FormatInt(num, 10) + uint
	return byteNum, sizeStr
}

// 根据这个值,计算一下它的大小是多少

func GetValSize(val interface{}) int64 {
	bytes, _ := json.Marshal(val)
	size := int64(len(bytes))
	fmt.Println(size)
	return size
}

memCache_test.go文件

package cache

import (
	"fmt"
	"reflect"
	"testing"
	"time"
)

func TestCacheOP(t *testing.T) {
	testData := []struct {
		key    string
		val    interface{}
		expire time.Duration
	}{
		{"djksndsdn", 678, time.Second * 10},
		{"ddjshf", false, time.Second * 12},
		{"ig", true, time.Second * 12},
		{"fdggd", map[string]interface{}{"a": 3, "b": false}, time.Second * 11},
		{"trtry", "huhu", time.Second * 3},
		{"twuuw", "aahha", time.Second * 5},
	}
	c := NewMemCache()
	c.SetMaxMemory("10MB")
	for _, item := range testData {
		//fmt.Println("ahha")
		c.Set(item.key, item.val, item.expire)
		//fmt.Println("ahha1")
		val, ok := c.Get(item.key)
		//fmt.Println("ahha2")
		if !ok {
			t.Error("缓存取值失败")
		}
		if !reflect.DeepEqual(val, item.val) {
			t.Errorf("缓存取值数据与预期的不一样,key: %s, 期望: %v, 实际: %v", item.key, item.val, val)
		}
	}
	if int64(len(testData)) != c.Keys() {
		t.Errorf("缓存数量不一样,期望: %d, 实际: %d", len(testData), c.Keys())
	}
	fmt.Println("ahah")
	c.Del(testData[0].key)
	c.Del(testData[1].key)
	if int64(len(testData))-2 != c.Keys() {
		t.Errorf("缓存数量不一样,期望: %d, 实际: %d", len(testData)-2, c.Keys())
	}
	fmt.Println("finish")
	time.Sleep(time.Second * 16)
	if c.Keys() != 0 {
		t.Error("过期缓存清空失败")
	}
}

memCache.go文件

package cache

import (
	"log"
	"sync"
	"time"
)

type memCache struct {
	maxMemorySize               int64  // 最大的内存空间是多少
	maxMemorySizeStr            string // 这个内存空间用字符串表示是多少
	currMemorySize              int64  // 当前使用了多少内存
	values                      map[string]*memCacheValue
	locker                      sync.RWMutex
	clearExpireItemTimeInterval time.Duration // 相隔多长时间删除一些过期的内容
}

type memCacheValue struct { // 内存空间里面的每一步的结构是什么样的
	val        interface{}   // 它的值是多少
	expireTime time.Time     // 它的过期时间是多少
	expire     time.Duration // 还有多少时间过期
	/*
		time.Duration 是 Go 语言中用于表示时间间隔(持续时间)的类型。它是一个有符号整数类型,单位为纳秒(nanoseconds)。
	*/
	size int64 // 它占这一个内存空间的大小是多少
}

// 首先应该先给创建这么一个实例一个方法
func NewMemCache() *memCache {
	mc := &memCache{
		values:                      make(map[string]*memCacheValue),
		clearExpireItemTimeInterval: time.Second,
	}
	go mc.clearExpireItemTime()
	return mc
}

// 怎么清空呢
func (mc *memCache) clearExpireItemTime() {
	timeTicker := time.NewTicker(mc.clearExpireItemTimeInterval)
	defer timeTicker.Stop()
	for {
		select {
		case <-timeTicker.C:
			mc.locker.Lock()
			for key, item := range mc.values {
				if item.expire != 0 && time.Now().After(item.expireTime) {
					mc.del(key)
				}
			}
			mc.locker.Unlock()
		}
	}
}

// 根据key值找到val值  然后删除它  但是需要提前判断是否存在这个键值对
func (mc *memCache) del(key string) {
	if val, exists := mc.values[key]; exists {
		mc.currMemorySize -= val.size
		delete(mc.values, key)
	}
}

// 设置最大的内存
func (mc *memCache) SetMaxMemory(size string) bool {
	mc.maxMemorySize, mc.maxMemorySizeStr = ParseSize(size)
	return true
}

// 将value写入缓存
func (mc *memCache) Set(key string, val interface{}, expire time.Duration) bool {
	mc.locker.Lock()
	defer mc.locker.Unlock()
	v := &memCacheValue{
		val:        val,
		expireTime: time.Now().Add(expire),
		expire:     expire,
		size:       GetValSize(val),
	}
	mc.del(key)
	mc.add(key, v)
	if mc.currMemorySize > mc.maxMemorySize {
		mc.del(key)
		log.Printf("max memory size exceeded: %d", mc.maxMemorySize)
		return false
	}
	return true
}

func (mc *memCache) add(key string, val *memCacheValue) {
	mc.values[key] = val
	mc.currMemorySize += val.size
}

// 根据key值获取value
func (mc *memCache) Get(key string) (interface{}, bool) {
	mc.locker.RLock()
	defer mc.locker.RUnlock()
	mcv, ok := mc.get(key)
	if ok {
		if mcv.expire != 0 && time.Now().After(mcv.expireTime) {
			mc.del(key)
			return nil, false
		}
		return mcv.val, true
	}
	return nil, false
}

func (mc *memCache) get(key string) (*memCacheValue, bool) {
	val, ok := mc.values[key]
	return val, ok
}

// 删除key值
func (mc *memCache) Del(key string) bool {
	mc.locker.Lock()
	defer mc.locker.Unlock()
	mc.del(key)
	return true
}

// 判断key值是否存在
func (mc *memCache) Exists(key string) bool {
	mc.locker.RLock()
	defer mc.locker.RUnlock()
	_, ok := mc.values[key]
	return ok
}

// 清空所有key
func (mc *memCache) Flush() bool {
	mc.locker.Lock()
	defer mc.locker.Unlock()
	mc.values = make(map[string]*memCacheValue)
	mc.currMemorySize = 0
	return true
}

// 获取缓存中所有key的数量
func (mc *memCache) Keys() int64 {
	mc.locker.RLock()
	defer mc.locker.RUnlock()
	return int64(len(mc.values))
}

Cache.go文件

package cache

import "time"

type Cache interface {
	// size 1KB 100KB 1MB 2MB 1GB
	SetMaxMemory(size string) bool
	// 将value写入缓存
	Set(key string, val interface{}, expire time.Duration) bool
	// 根据key值获取value
	Get(key string) (interface{}, bool)
	// 删除key值
	Del(key string) bool
	// 判断key值是否存在
	Exists(key string) bool
	// 清空所有key
	Flush() bool
	// 获取缓存中所有key的数量
	Keys() int64
}

cacheServer.go

package cache_server

import (
	"test1/cache"
	"time"
)

type cacheServer struct {
	memCache cache.Cache
}

func NewMemCache() *cacheServer {
	return &cacheServer{
		memCache: cache.InitCache(),
	}
}

func (cs *cacheServer) SetMaxMemory(size string) bool {
	return cs.memCache.SetMaxMemory(size)
}

// 将value写入缓存
func (cs *cacheServer) Set(key string, val interface{}, expire ...time.Duration) bool {
	expireTs := time.Second * 0
	if len(expire) > 0 {
		expireTs = expire[0]
	}
	return cs.memCache.Set(key, val, expireTs)
}

// 根据key值获取value
func (cs *cacheServer) Get(key string) (interface{}, bool) {
	return cs.memCache.Get(key)
}

// 删除key值
func (cs *cacheServer) Del(key string) bool {
	return cs.memCache.Del(key)
}

// 判断key值是否存在
func (cs *cacheServer) Exists(key string) bool {
	return cs.memCache.Exists(key)
}

// 清空所有key
func (cs *cacheServer) Flush() bool {
	return cs.memCache.Flush()
}

// 获取缓存中所有key的数量
func (cs *cacheServer) Keys() int64 {
	return cs.memCache.Keys()
}

  • 4
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值