上一节中实现了LRU淘汰算法,这里完成淘汰算法的抽象层:
GOCache\Cache\Cache.go
package Cache
import (
"GOCache/Cache/CacheAlgorithm"
"sync"
)
//cache.go的实现非常简单,实例化 lru(或者其他算法),封装 get 和 put 方法,并添加互斥锁 mu。
type Cache struct {
lock sync.Mutex //互斥锁
CacheImpl CacheAlgorithm.ICacheAlgorithm //实现了ICacheAlgorithm的算法,现在只有LRU
algorithm string //使用的底层算法 例如"LRU"
cacheBytes uint64 //最大可存储的比特大小
}
func (c *Cache) newCache(algorithm string) CacheAlgorithm.ICacheAlgorithm {
switch c.algorithm {
case "lru": //执行实例化 可添加其他算法
return CacheAlgorithm.NewLRUCache(c.cacheBytes)
default:
return CacheAlgorithm.NewLRUCache(c.cacheBytes)
}
}
func (c *Cache) put(key string, value ByteView) {
c.lock.Lock()
defer c.lock.Unlock()
if c.CacheImpl == nil { //延迟初始化
c.CacheImpl = c.newCache(c.algorithm)
}
c.CacheImpl.Put(key, value)
}
func (c *Cache) get(key string) (value ByteView, ok bool) {
c.lock.Lock()
defer c.lock.Unlock()
if c.CacheImpl == nil { //延迟初始化
c.CacheImpl = c.newCache(c.algorithm)
}
if v, ok := c.CacheImpl.Get(key); ok {
return v.(ByteView), ok
}
return
}
抽象了一个只读数据结构 ByteView 用来表示缓存值,ByteView是byte数组的包装类,可以存储真实字节,图片等
GOCache\Cache\byteview.go
package Cache
//抽象了一个只读数据结构 ByteView 用来表示缓存值
type ByteView struct {
Bytes []byte //存储真实字节 可存图片等
}
// 实现了Len方法,ByteView是value的实现类,可以作为缓存的val
func (v ByteView) Len() int {
return len(v.Bytes)
}
// ByteSlice 返回一个比特切片的拷贝
func (v ByteView) ByteSlice() []byte {
return cloneBytes(v.Bytes)
}
// byte[]转为string的方法
func (v ByteView) String() string {
return string(v.Bytes)
}
func cloneBytes(b []byte) []byte {
c := make([]byte, len(b))
copy(c, b)
return c
}
接下来完成Group结构,类似于一个数据库,目前是一个数据库对应一个cache结构,每个数据库有唯一id,默认实例化了一个id为1的group。
GOCache\Cache\group.go
package Cache
import (
"GOCache/conf"
"fmt"
"log"
"sync"
)
type Group struct { //一个group可类似于一个数据库
GroupID int
mainCache Cache //实现的并发缓存
}
var ( //全局变量
mu sync.RWMutex
groups = make(map[int]*Group)
group1 = NewGroup(0) //实例化一个group全局可用
)
func NewGroup(id int) *Group {
mu.Lock()
defer mu.Unlock()
g := &Group{
GroupID: id,
//getter: getter,
mainCache: Cache{cacheBytes: conf.Config.MaxBytes, algorithm: conf.Config.CacheAlgorithm},
}
groups[id] = g
return g
}
//根据id返回实例指针
func GetGroup(groupID int) *Group {
mu.RLock()
g := groups[groupID]
mu.RUnlock()
return g
}
//包装get方法
func (g *Group) Get(key string) (ByteView, error) {
if key == "" {
return ByteView{}, fmt.Errorf("key is required")
}
if v, ok := g.mainCache.get(key); ok {
log.Println("[GOCache] hit")
return v, nil
}
return ByteView{}, fmt.Errorf("[" + key + "]key Not Found!")
}
//包装put方法
func (g *Group) Put(key string, value ByteView) {
g.mainCache.put(key, value)
}
至此,GoCache的最核心部分已完成,接下来完成客户端和服务端,但在这之前,需要编写一个读取用户配置类的模块以便用户自定义一些参数。
3.用户配置类
GOCache\conf\GlobalConfig.go
package conf
import (
"encoding/json"
"io/ioutil"
)
//实例化一个全部配置类,这个配置类会读取外部json文件
var Config *GlobalConfig
type GlobalConfig struct {
MaxBytes uint64 //最大存储比特数
CacheAlgorithm string //使用的淘汰算法
Host string //绑定监听ip
TcpPort int //绑定监听端口
Name string //该服务器实例的名字
Version string //版本号
MaxConn int //最大连接数
PassWord string //密码 一个服务器对应一个密码
//MaxPackageSize uint32
//WorkerPoolSize uint32
//MaxWorkerTaskLen uint32 //能排队的最大值
}
func (g *GlobalConfig) Load() {
data, err := ioutil.ReadFile("./GOCache.json")
if err != nil { //不存在文件
return
}
err = json.Unmarshal(data, &Config)
if err != nil {
panic(err)
}
}
func init() {
Config = &GlobalConfig{
PassWord: "8035aaaa",
MaxBytes: 2147483648,
Host: "0.0.0.0",
TcpPort: 9999,
Name: "GOCacheServer",
Version: "v0.3",
MaxConn: 10,
CacheAlgorithm: "lru",
//MaxPackageSize: 4096,
//WorkerPoolSize: 10,
//MaxWorkerTaskLen: 1024,
}
Config.Load()
}
暂时在相对目录上没弄好,应该是要把json放到执行文件的同级文件上才行,这里暂时先放到包内。
GOCache\conf\GOCache.json
{
"maxBytes":4096,
"Host": "0.0.0.0",
"TcpPort": 9999,
"Name": "GOCacheServer",
"Version": "v0.3",
"MaxConn": 10,
"MaxPackageSize": 4096,
"WorkerPoolSize": 10,
"PassWord": "8035aaaa",
"MaxWorkerTaskLen": 1024
}
4.缓存服务端
如果是以简单或通用性为上,应采用http协议,这里先给出基于TCP的应用层协议 RESP (REdis Serialization Protocol) ,即Redis的应用层通信协议,比HTTP要更加高效,无需HTTP的很多冗余信息。