文件+内存二级缓存在Go项目中的实现及应用

您所在的位置:网站首页 loader的作用 文件+内存二级缓存在Go项目中的实现及应用

文件+内存二级缓存在Go项目中的实现及应用

#文件+内存二级缓存在Go项目中的实现及应用| 来源: 网络整理| 查看: 265

对于缓存,大家都不陌生。百度百科的定义是这样的:

缓存是指可以进行高速数据交换的存储器,它先于内存与CPU交换数据,因此速率很快。

由此可知,缓存是用来提高数据交换速度的。我们今天要讲的缓存不是CPU中的缓存,而是在应用程序中对数据库的缓存。应用程序先于数据库,从缓存中读取数据,以降低数据库的压力,提高应用程序的读取性能。

在实际项目中,相信大家也都遇到过类似的情景:数据量小,但访问又较频繁(例如国家标准行政区域数据),想将其完全存放于本地内存中。这样就可以避免直接访问mysql或redis,减少网络传输,提高访问速度。那具体应该怎么实现呢?

本文就介绍一种Go项目中经常使用到的方法:将数据从数据库中加载到本地文件,然后再将文件中的数据加载到内存中,内存中的数据直接供应用程序使用。如下图所示:

本文会忽略数据库到本地文件的过程,因为这个环节就是一个文件上传和下载到本地的过程。所以我们会重点讲解如何从本地文件加载数据到内存中这个环节。

01 目标

在Go语言的项目中,将本地文件的数据加载到应用程序的内存中,以供应用程序直接使用。

我们再将目标拆解成两个目标:

1、程序启动时,将本地文件的数据初始化到内存中,即冷启动

2、程序运行期间,本地文件有更新时,将数据更新到内存中。

02 代码实现

本文主要是目的就是给大家讲解目标的实现,所以不会带大家一步步分析,而是通过讲解已实现的代码来给大家提供一种参考实现。

所以,我们先给出我们设计的类图:

从类图中可知,有两个主要的结构体:FileDoubleBuffer和LocalFileLoader。下面我们一一讲解这两个结构体的属性和方法实现。

2.1 场景假设

我们以城市的天气状况为示例,将每个城市的实时温度和风力以json格式存储在文件中,当城市的温度或风力有变化时,再更新该文件。如下:

{ "beijing": { "temperature": 23, "wind": 3 }, "tianjin": { "temperature": 20, "wind": 2 }, "shanghai": { "temperature": 20, "wind": 20 }, "chongqing": { "temperature": 30, "wind": 10 } }

2.2 main的调用

这里,先给出main函数的调用示例,根据main函数中的实现,我们一步步看图中两个主要结构体的实现,代码如下:

//第一步,定义装载文件中数据的结构体 type WeatherContainer struct { Weathers map[string]*Weather //每个城市对应的实况天气 } //文件数据中每个城市的天气状况 type Weather struct { Temperature int //当前气温 `json:"temperature"` Wind int //当前风力 `json:"wind"` } func main() { pwd, _ := os.Getwd() //加载的文件路径 filename := pwd + "/cache/cache.json" //初始化本地文件加载器 localFileLoader := NewLocalFileLoader(filename) //初始化文件缓冲实例,将localFileLoader作为底层的文件缓冲 fileDoubleBuffer := NewFileDoubleBuffer(localFileLoader) // 开始将文件中的内容加载到缓冲变量中,本质上就是通过load和reload加载文件数据 fileDoubleBuffer.StartFileBuffer() //获取数据 weathersConfig := fileDoubleBuffer.Data().(*WeatherContainer) fmt.Println("weathers:", weathersConfig.Weathers["beijing"]) blockCh := make(chan int) //该通道用于阻塞进程不结束,这样reload的协程就可以执行了 loader.lastModifyTime { loader.lastModifyTime = fileInfo.ModTime().Unix() return true } return false }

Alloc() interface{}

用于分配具体的变量,以供装载文件中的数据。这里分配的变量最终会存储到FileDoubleBuffer中的dataBuffer数据中。代码如下:

// 分配具体的变量,来承载文件中的具体内容,变量结构体需要和文件中的结构体保持一致 func (loader *LocalFileLoader) Alloc() interface{} { return &WeatherContainer{ Weathers: make(map[string]*Weather), } }

同样需要一个初始化LocalFileLoader实例的函数:

//指定需要加载的文件路径path func NewLocalFileLoader(path string) *LocalFileLoader { return &LocalFileLoader{ filename: path, } }

总结

这种方式一般适用于数据量较小、频繁读的场景。在文章开始的图中我们可以看到,因为是服务器往往是集群,所以每台机器上的文件内容可能会有短暂的差异,所以该实现也不适用于对数据具有强一致要求的场景中。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3