通过golang语言实现一个本地内存缓存,接下来将通过代码的形式展示:
1.文件目录结构

2.具体实现
[1].接口定义
Go
package cache
import "time"
type Cache interface {
// 设置缓存大小 1KB 100KB 1MB 2MB 1GB
SetMaxMemory(size string) bool
// 写入缓存
Set(key string, value interface{}, expire time.Duration) bool
// 获取缓存
Get(key string) (interface{}, bool)
// 删除缓存
Del(key string) bool
// 判断缓存是否存在
Exists(key string) bool
// 清空缓存
Flush() bool
// 获取缓存中keys的数量
Keys() int64
}
[2].接口实现
Go
package impl
import (
"fmt"
"project08/cache"
"project08/utils"
"sync"
"time"
)
type CacheImpl struct {
// 最大内存
maxMemorySize int64
// 最大内存表示
maxMemorySizeStr string
// 当前使用内存
currentMemorySize int64
// 缓存键值对
values map[string]*cacheValue
// 读写锁
locker sync.RWMutex
// 清除过期缓存时间间隔
clearExpireKeyInterval time.Duration
}
type cacheValue struct {
// 缓存值
val interface{}
// 过期时间
expireTime time.Time
// 缓存大小
size int64
}
func NewCache() cache.Cache {
ci := &CacheImpl{
values: make(map[string]*cacheValue),
clearExpireKeyInterval: time.Second * 10,
}
clearExpireKey := func() {
// 定时触发器
timeTicker := time.NewTicker(ci.clearExpireKeyInterval)
defer timeTicker.Stop()
for {
select {
case <-timeTicker.C:
for key, item := range ci.values {
if time.Now().After(item.expireTime) {
ci.locker.Lock()
val, ok := ci.values[key]
if ok {
ci.currentMemorySize -= val.size
delete(ci.values, key)
}
ci.locker.Unlock()
}
}
}
}
}
go clearExpireKey()
return ci
}
// 结构体实现Cache接口
func (ci *CacheImpl) SetMaxMemory(size string) bool {
// 通过当前设置的size 如1KB
ci.maxMemorySize, ci.maxMemorySizeStr = utils.ParseMemory(size)
fmt.Println(ci.maxMemorySize, ci.maxMemorySizeStr)
return true
}
func (ci *CacheImpl) Set(key string, value interface{}, expire time.Duration) bool {
ci.locker.Lock()
defer ci.locker.Unlock()
// 先判断当前key是否存在 将原先key对应的val删除
val, ok := ci.values[key]
if ok && val != nil {
ci.currentMemorySize -= val.size
delete(ci.values, key)
}
// 判断当前内存值是否大于最大内存值
size := utils.GetValueSize(value)
if ci.currentMemorySize+size > ci.maxMemorySize {
// 可以加一些内存管理策略
panic(fmt.Sprintf("over max memory size %v", ci.maxMemorySize))
}
v := &cacheValue{
val: value,
expireTime: time.Now().Add(expire),
size: size,
}
// 设置键值
ci.values[key] = v
// 增加当前缓存
ci.currentMemorySize += v.size
return true
}
func (ci *CacheImpl) Get(key string) (interface{}, bool) {
ci.locker.RLock()
defer ci.locker.RUnlock()
val, ok := ci.values[key]
if ok {
// 判断缓存是否过期
if val.expireTime.Before(time.Now()) {
ci.currentMemorySize -= val.size
delete(ci.values, key)
return nil, false
}
return val.val, true
}
return nil, false
}
func (ci *CacheImpl) Del(key string) bool {
ci.locker.Lock()
defer ci.locker.Unlock()
val, ok := ci.values[key]
if ok {
ci.currentMemorySize -= val.size
delete(ci.values, key)
return true
}
return false
}
func (ci *CacheImpl) Exists(key string) bool {
ci.locker.RLock()
defer ci.locker.RUnlock()
_, ok := ci.values[key]
return ok
}
func (ci *CacheImpl) Flush() bool {
ci.locker.Lock()
defer ci.locker.Unlock()
// 将values设置为长度为0的空对象
ci.values = make(map[string]*cacheValue, 0)
ci.currentMemorySize = 0
return true
}
func (ci *CacheImpl) Keys() int64 {
ci.locker.RLock()
defer ci.locker.RUnlock()
return int64(len(ci.values))
}
[3].utils定义
Go
package utils
import (
"encoding/json"
"fmt"
"regexp"
"strconv"
"strings"
)
func ParseMemory(size string) (int64, string) {
// 默认值 100MB
// 匹配出数字
re, _ := regexp.Compile("[0-9]+")
// 单位KB MB...
unit := string(re.ReplaceAll([]byte(size), []byte("")))
// 数字 转为64位10进制
num, _ := strconv.ParseInt(strings.Replace(size, unit, "", 1), 10, 64)
// 单位大小写转换
unit = strings.ToUpper(unit)
var byteNum int64 = 0
switch unit {
case "B":
byteNum = B
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 {
fmt.Println("Cache set size just only support B,KB,MB,GB,TB,PB")
num = 100
byteNum = num * MB
unit = "MB"
}
return byteNum, strconv.FormatInt(num, 10) + unit
}
func GetValueSize(val interface{}) int64 {
bytes, _ := json.Marshal(val)
return int64(len(bytes))
}
[4].main主函数实现
Go
package main
import (
"project08/cache/impl"
)
func main() {
cache := impl.NewCache()
cache.SetMaxMemory("10KB")
cache.Set("james", 2, 6)
cache.Get("james")
cache.Exists("james")
cache.Keys()
cache.Flush()
}
[5].测试自己去编写吧
bash
go test .\test\cache_test.go
其实现原理类似与redis,如有不当之处可参考redis命令做适当修改.
缺少内存淘汰机制。
思路:可在set时做些处理+定时循环机制通过判断达到最大内存占比处理。