golang实现一个简易的缓存系统
项目结构:
├── README.md
├── cache
│ ├── cache.go
│ ├── cache_store.go
│ ├── iCache.go
│ ├── mcache.go
│ └── mcache_test.go
├── cache_server
│ ├── cacheServer.go
│ └── cacheServer_test.go
├── constant
│ └── constant.go
├── cuserror
│ └── error.go
├── go.mod
├── logger
│ ├── console.go
│ ├── file.go
│ ├── file_test.go
│ ├── iLogger.go
│ ├── logger.go
│ └── logger_test.go
├── main.go
└── util
├── util.go
└── util_test.go
6 directories, 20 files
package cache
import "time"
/**
该程序需要满⾜足以下要求:
1. 支持设定过期时间,精度为秒级。
2. 支持设定最⼤大内存,当内存超出时候做出合理理的处理理。
3. 支持并发安全。
4. 为简化编程细节,无需实现数据落地。
*/
/**
⽀支持过期时间和最⼤大内存⼤大⼩小的的内存缓存库。
*/
type ICache interface {
//size 是⼀一个字符串串。⽀支持以下参数: 1KB,100KB,1MB,2MB,1GB 等
SetMaxMemory(size string) bool
// 设置⼀一个缓存项,并且在expire时间之后过期
Set(key string, val interface{}, expire time.Duration) // 获取⼀一个值
Get(key string) (interface{}, bool)
// 删除⼀一个值
Del(key string) bool
// 检测⼀一个值 是否存在
Exists(key string) bool
// 情况所有值
Flush() bool
// 返回所有的key 多少
Keys() int64
//清理过期的节点
ClearExpireNode()
}
package cache
import (
"fmt"
"time"
)
/**
* 缓存存在结构(使用单链表实现)
*/
type mNode struct{
Key string //键
Value interface{} //值
InsertTime time.Time //记录插入时间
Expire time.Duration //过期间隔(单位为秒)
Next *mNode //定义一地址域(指向下一个)
}
type linkedList struct {
firstNode *mNode //头结点
}
func NewLinkedList() *linkedList{
return &linkedList{}
}
//判断是否为空的单链接表
func (link *linkedList) isEmpty() bool {
if link.firstNode == nil {
return true
}
return false
}
//返回总数
func (link *linkedList) Size() int64{
first := link.firstNode //获取链表的头结点
var count int64 = 0 //定义一个计数器
for first != nil {
//如果节点不为空,则count++
count++
first = first.Next
}
return count
}
//从表头添加元素
func (link *linkedList)Add(key string,value interface{},expire time.Duration) bool{
node := &mNode{
Key:key,
Value: value,
Expire:expire,
InsertTime:time.Now(),
}
node.Next = link.firstNode
link.firstNode = node
return true
}
//在尾部添加数据,需要从头部开始遍历,直到nil
func (link *linkedList) Append(key string,value interface{},expire time.Duration) bool{
newNode := &mNode{
Key:key,
Value:value,
Expire:expire,
InsertTime:time.Now(),
}
node := link.firstNode
//首部是空
if node == nil {
link.firstNode = newNode
return true
}else {
for node.Next != nil {
node = node.Next
}
//已经到最后
node.Next = newNode
return true
}
return false
}
//在指定位置插入
func (link *linkedList)Insert(index int64,key string,value interface{},expire time.Duration) bool {
newMode := &mNode{
Key:key,
Value:value,
Expire:expire,
}
node := link.firstNode
if index < 0 {
//index小于0就放在首部
link.Add(key,value,expire)
return true
} else if index > link.Size(){
//index大于 长度就放在尾部
link.Append(key,value,expire)
return true
}else {
var count int64 = 0
//找到index之前的元素
for count < (index -1 ){
node = node.Next
count +=1
}
//已经找到index之前的元素
newMode.Next = node.Next
node.Next = newMode
return true
}
return false
}
//删除指定元素,从首部遍历该元素删除,并且需要维护指针
func (link *linkedList) Delete(key interface{}) bool {
node := link.firstNode
//如果是首部
if node != nil && node.Key == key {
link.firstNode = node.Next
}else{
for node != nil && node.Next != nil {
//找到,改指针
if node.Next.Key == key{
node.Next = node.Next.Next
return true
}else{
node = node.Next
}
}
}
return false
}
//循环遍历链表
func (link *linkedList) forEachLink() {
node := link.firstNode
for node != nil {
str := fmt.Sprintf("{\"%v\":%v}",node.Key,node.Value)
fmt.Printf("%v\n",str)
node = node.Next
}
}
func (link *linkedList) Get(key string) *mNode {
node := link.firstNode
for node != nil {
if node.Key == key {
return node
}
node = node.Next
}
return nil
}
func (link *linkedList) IsExists(key string) bool {
node := link.firstNode
for node != nil {
if node.Key == key {
return true
}
node = node.Next
}
return false
}
//检查这个key是否过期
func (link *linkedList) isExpire(key string) (string,bool) {
node := link.firstNode
for node != nil {
if node.Key == key {
return key,time.Now().Sub(node.InsertTime) > node.Expire
}
node = node.Next
}
return key,false
}
func (link *linkedList) Empty() bool{
node := link.firstNode
for node != nil {
link.Delete(node.Key)
node = node.Next
//node = nil
}
return true
}
//获取所有过期的keys
func (link *linkedList) GetExpireKeys() (keys []string ) {
node := link.firstNode
for node != nil {
if time.Now().Sub(node.InsertTime) > node.Expire {
keys = append(keys,node.Key)
}
node = node.Next
}
return
}
package cache
import (
"cache-system/logger"
"cache-system/util"
"sync"
"time"
)
type Cache struct {
SizeStr string //1KB,100KB,1MB,2MB,1GB
size int64
cookieMap *linkedList //存在cache
rwLock sync.RWMutex //读写所
Interval int // 多长时间执行一次清理任务,操作单位秒
}
func NewCache(size string,interval int) ICache{
return &Cache{
SizeStr: size,
size: util.ParseSize(size),
cookieMap: NewLinkedList(),
Interval:interval,
}
}
func (c *Cache)SetMaxMemory(size string) bool {
c.rwLock.RLock()
defer c.rwLock.Unlock()
c.SizeStr = size
c.size = util.ParseSize(size)
return true
}
// 设置⼀一个缓存项,并且在expire时间之后过期
func (c *Cache)Set(key string, val interface{}, expire time.Duration) {
c.rwLock.Lock() //使用互斥锁来保证写的数据确证性
defer c.rwLock.Unlock()
if c.Exists(key){
c.cookieMap.Delete(key)
}
c.cookieMap.Add(key,val,expire) //Add方法在头部插入
}
func (c *Cache)Get(key string) (interface{}, bool){
c.rwLock.RLock()
defer c.rwLock.RUnlock()
var t bool = false
cache := c.cookieMap.Get(key)
if cache != nil {
t = true
}
if cache != nil {
return cache.Value,t
}
return nil,false
}
// 删除一个值
func (c *Cache)Del(key string) bool{
c.rwLock.Lock()
defer c.rwLock.Unlock()
return c.cookieMap.Delete(key)
}
// 检测一个值是否存在
func (c *Cache)Exists(key string) bool{
return c.cookieMap.IsExists(key)
}
// 清空所有值
func (c *Cache)Flush() bool{
c.rwLock.Lock()
defer c.rwLock.Unlock()
return c.cookieMap.Empty()
}
// 返回所有的key 多少
func (c *Cache)Keys() int64{
c.rwLock.Lock()
defer c.rwLock.Unlock()
return c.cookieMap.Size()
}
func (c *Cache) ClearExpireNode(){
for{
logger.Info("exec clear expire key task")
select {
case <-time.After(time.Duration(c.Interval) *time.Second):
if keys := c.cookieMap.GetExpireKeys(); len(keys) != 0 {
for _,key := range keys{
logger.Info("clear expire key:%v",key)
c.Del(key)
}
}
}
}
}
package cache
import (
"cache-system/logger"
"time"
)
var cache ICache
var currentSize int64
func InitMCache() {
//默认初始化一个cache实例,并设置可用存储不能过物理内存可用容量和总容量
logger.Info("init cache instance,cap:%v","256MB")
cache = NewCache("256MB",5) //可用存储容量
//go clearExpireNode() //执行清理任务
}
func SetMaxMemory(size string) bool{
logger.Info("exec cache.SetMaxMemory,value:%v",size)
return cache.SetMaxMemory(size)
}
// 设置⼀一个缓存项,并且在expire时间之后过期
func Set(key string, val interface{}, expire int64){
var ex time.Duration = time.Duration(expire* int64(1000000000))
logger.Info("exec cache.Set,key:%v,value:%v,expire:%v",key,val,expire)
cache.Set(key,val,ex)
}
func Get(key string) (interface{}, bool) {
v,t := cache.Get(key)
logger.Info("exec cache.Get,in-value:%v,out-value:%v,status:%v",key,v,t)
return v,t
}
// 删除⼀个值
func Del(key string) bool {
t := cache.Del(key)
logger.Info("exec cache.Del, value:%v",key)
return t
}
// 检测⼀个值 是否存在
func Exists(key string) bool {
t := cache.Exists(key)
logger.Info("exec cache.Exists,value:%v,status:%v",key,t)
return t
}
// 情况所有值
func Flush() bool {
t := cache.Flush()
logger.Info("exec cache.Flush(),ret-value:%v",t)
return t
}
// 返回所有的key 多少
func Keys() int64 {
t := cache.Keys()
logger.Info("exec cache.Keys, ret-value:%v",t)
return t
}
func ClearExpireNode(){
cache.ClearExpireNode()
}
package cache
import (
"cache-system/logger"
"fmt"
"runtime"
"testing"
"time"
)
func init() {
config := make(map[string]string,1)
config["log_level"] = "debug"
logger.InitLogger("console",config)
InitMCache()
}
func TestSet(t *testing.T) {
Set("name","mfz",5) //10秒后过期
time.Sleep(5*time.Second)
Set("age","22",25) //10秒后过期
time.Sleep(5*time.Second)
Set("work","it-dev",40) //10秒后过期
}
func TestGet(t *testing.T) {
value,ok := Get("name")
if ok {
fmt.Printf("name:%v\n",value)
}else {
t.Errorf("Get key:%v is not exists","name")
}
select {
}
}
func MStat(t *testing.T) {
type MemStatus struct {
All uint32 `json:"all"`
Used uint32 `json:"used"`
Free uint32 `json:"free"`
Self uint64 `json:"self"`
}
memStat := new(runtime.MemStats)
//自身占用
for i:=0; i < 200000; i++{
runtime.ReadMemStats(memStat)
mem := MemStatus{}
mem.Self = memStat.Alloc
time.Sleep(1*time.Second)
}
}
package main
import (
"cache-system/logger"
"fmt"
"math/rand"
"time"
)
import "cache-system/cache"
func init(){
rand.Seed(time.Now().UnixNano()) //初始化随机种子
}
func InitLog(){
config := make(map[string]string,1)
config["log_level"] = "debug"
logger.InitLogger("console",config)
}
func InitCache(){
cache.InitMCache()
}
func main(){
InitLog() //初始化日志实例
InitCache() //初化缓存实例
for i:= 0; i < 500; i++{
cache.Set("name:"+fmt.Sprintf("%d",i+1),"tom:"+fmt.Sprintf("%d",i+1),rand.Int63n(10000))
//time.Sleep(200*time.Millisecond)
}
/*
time.Sleep(200*time.Millisecond)
cache.Flush()
time.Sleep(200*time.Millisecond)
fmt.Printf("get cache table size:%d\n",cache.Keys())
time.Sleep(200*time.Millisecond)
*/
fmt.Printf("get cache table size:%d\n",cache.Keys())
//查询name100 key的值
val,ok :=cache.Get("name:100")
fmt.Printf("get name:100 value:%v, status:%v\n",val,ok)
//删除name100 key
cache.Del("name:100")
//查询name100 key是否删除成功
fmt.Printf("get name:100 key:%v\n",cache.Exists("name:100"))
time.Sleep(2*time.Second) //等待两秒后开清理过期任务
go cache.ClearExpireNode()
select { //阻塞等待
}
}