Golang实现多存储驱动设计SDK案例

目录
  • 支持功能
  • 开发缘由
  • 如何使用
    • 安装
  • 存储适配器
    • 缓存适配器
    • 编写自己的缓存或存储
  • 压缩

前言:

Gocache是一个基于Go语言编写的多存储驱动的缓存扩展组件。它为您带来了许多缓存数据的功能。

支持功能

多个缓存驱动存储:支持内存、redis或您自定义存储驱动。

支持如下功能:

  • 链式缓存:使用具有优先级顺序的多个缓存(例如,内存然后回退到redis共享缓存)。
  • 可加载缓存:允许您调用回调函数将数据放回缓存中。
  • 指标缓存,可让您存储有关缓存使用情况的指标(命中、未命中、设置成功、设置错误……)。
  • 自动编组/解组缓存值作为结构的编组器。
  • 在存储中定义默认值并在设置数据时覆盖它们。
  • 通过过期时间和/或使用标签缓存失效。
  • 泛型的使用。

默认情况下,Gocache支持如下几种缓存驱动:

  • 内存 (bigcache) (allegro/bigcache)。
  • 内存 (ristretto) (dgraph-io/ristretto)。
  • 内存 (go-cache) (patrickmn/go-cache)。
  • 内存缓存(bradfitz/memcache)。
  • Redis (go-redis/redis)。
  • 空闲缓存(coocood/freecache)。
  • Pegasus ( apache/incubator-pegasus )基准测试。

开发缘由

在作者的官网博客中提到这样的几句话:

当我开始在 GraphQL Go 项目上实现缓存时,它已经有一个内存缓存,它使用了一个具有简单 API 的小库,但也使用了另一个内存缓存库来使用具有不同库和 API 的批处理模式加载数据,做同样的事情:缓存项目。后来,我们还有一个需求:除了这个内存缓存之外,我们还想使用 Redis 添加一层分布式缓存,主要是为了避免我们的新 Kubernetes pod 在将新版本的应用程序投入生产时出现空缓存。

因此,作者想到是时候拥有一个统一的API来管理多个缓存存储:内存、redis、memcache 或任何你想要的。

如何使用

安装

要开始使用最新版本的 go-cache,您可以使用以下命令:

go get github.com/eko/gocache/v3

为避免尝试导入库时出现任何错误,请使用以下导入语句:

import (
	"github.com/eko/gocache/v3/cache"
	"github.com/eko/gocache/v3/store"
)

如果您遇到任何错误,请务必运行go mod tidy以清理您的 go.mod 文件。

存储适配器

首先,当您要缓存项目时,您必须选择要缓存项目的位置:在内存中?在共享的 redis 或 memcache 中?或者可能在另一个存储中。

目前,Gocache 实现了以下存储:

  • BigCache:内存中的存储。
  • Ristretto : DGraph 提供的另一个内存存储。
  • Memcache:基于 bradfitz/gomemcache 客户端库的 memcache 存储。
  • Redis:基于 go-redis/redis 客户端库的 redis 存储。

所有这些商店都实现了一个非常简单的 API,它遵循以下接口:

type StoreInterface interface {
	Get(key interface{}) (interface{}, error)
	Set(key interface{}, value interface{}, options *Options) error
	Delete(key interface{}) error
	Invalidate(options InvalidateOptions) error
	Clear() error
	GetType() string
}

此接口代表您可以在商店中执行的所有操作,并且每个操作都调用客户端库中的必要方法。所有这些存储都有不同的配置,具体取决于您要使用的客户端库,

例如,初始化 Memcache 存储:

store := store.NewMemcache(
	memcache.New("10.0.0.1:11211", "10.0.0.2:11211", "10.0.0.3:11212"),
	&store.Options{
		Expiration: 10*time.Second,
	},
)

然后,必须将初始化的存储传递给缓存对象构造函数。

缓存适配器

一个缓存接口来统治它们。

缓存接口与存储接口完全相同,因为基本上,缓存将对存储执行操作:

type CacheInterface interface {
	Get(key interface{}) (interface{}, error)
	Set(key, object interface{}, options *store.Options) error
	Delete(key interface{}) error
	Invalidate(options store.InvalidateOptions) error
	Clear() error
	GetType() string
}

使用这个界面,我可以对缓存项执行所有必要的操作:设置、获取、删除、无效数据、清除所有缓存和另一个方法 (GetType),它可以让我知道当前缓存项是什么,很有用在某些情况下。

从这个接口开始,实现的缓存类型如下:

  • Cache:允许操作来自给定存储的数据的基本缓存。
  • Chain:一个特殊的缓存适配器,允许链接多个缓存(可能是因为你有一个内存缓存,一个redis缓存等......)。
  • Loadable: 一个特殊的缓存适配器,允许指定一种回调函数,如果过期或失效,自动将数据重新加载到缓存中。
  • Metric:一个特殊的缓存适配器,允许存储有关缓存数据的指标:设置、获取、失效、成功与否的项目数。 当所有这些缓存都实现相同的接口并且可以相互包装时,美妙之处就出现了:一个指标缓存可以采用一个可加载的缓存,该缓存可以采用一个可以采用多个缓存的链式缓存。

下面是一个简单的 Memcache 示例:

memcacheStore := store.NewMemcache(
	memcache.New("10.0.0.1:11211", "10.0.0.2:11211", "10.0.0.3:11212"),
	&store.Options{
		Expiration: 10*time.Second,
	},
)
cacheManager := cache.New(memcacheStore)
err := cacheManager.Set("my-key", []byte("my-value"), &cache.Options{
	Expiration: 15*time.Second, // Override default value of 10 seconds defined in the store
})
if err != nil {
    panic(err)
}
value := cacheManager.Get("my-key")
cacheManager.Delete("my-key")
cacheManager.Clear()
// Clears the entire cache, in case you want to flush all cache

现在,假设您想要一个链式缓存,其中包含一个内存 Ristretto 存储和一个分布式 Redis 存储作为后备,

并带有一个 marshaller 和指标作为结果:

// Initialize Ristretto cache and Redis client
ristrettoCache, err := ristretto.NewCache(&ristretto.Config{NumCounters: 1000, MaxCost: 100, BufferItems: 64})
if err != nil {
    panic(err)
}
redisClient := redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379"})

// Initialize stores
ristrettoStore := store.NewRistretto(ristrettoCache, nil)
redisStore := store.NewRedis(redisClient, &cache.Options{Expiration: 5*time.Second})

// Initialize Prometheus metrics
promMetrics := metrics.NewPrometheus("my-amazing-app")
// Initialize chained cache
cacheManager := cache.NewMetric(promMetrics, cache.NewChain(
    cache.New(ristrettoStore),
    cache.New(redisStore),
))
// Initializes a marshaler
marshal := marshaler.New(cacheManager)

key := BookQuery{Slug: "my-test-amazing-book"}
value := Book{ID: 1, Name: "My test amazing book", Slug: "my-test-amazing-book"}

// Set the value in cache using given key
err = marshal.Set(key, value)
if err != nil {
    panic(err)
}
returnedValue, err := marshal.Get(key, new(Book))
if err != nil {
    panic(err)
}
// Then, do what you want with the value

我们还没有谈到 Marshaler,但它是 Gocache 的另一个功能:我们提供了一项服务来帮助您自动编组/解组您的对象从/到您的存储。

这在使用 struct 对象作为键而不是内存存储时很有用,因为您必须将对象转换为字节。

所有这些功能:带有内存和 redis 的链式缓存、Prometheus 指标和封送处理程序只需大约 20 行代码即可完成。

编写自己的缓存或存储

如果您想实现自己的专有缓存,也很容易做到。

这是一个简单的示例,以防您想要记录在缓存中完成的每个操作(这不是一个好主意,但很好,这是一个简单的 todo 示例):

package customcache
import (
	"log"

	"github.com/eko/gocache/cache"
	"github.com/eko/gocache/store"
)
const LoggableType = "loggable"
type LoggableCache struct {
	cache cache.CacheInterface
}
func NewLoggable(cache cache.CacheInterface) *LoggableCache {
	return &LoggableCache{
		cache: cache,
	}
}

func (c *LoggableCache) Get(key interface{}) (interface{}, error) {
	log.Print("Get some data...")
	return c.cache.Get(key)
}

func (c *LoggableCache) Set(key, object interface{}, options *store.Options) error {
	log.Print("Set some data...")
	return c.cache.Set(key, object, options)
}
func (c *LoggableCache) Delete(key interface{}) error {
	log.Print("Delete some data...")
	return c.cache.Delete(key)
}
func (c *LoggableCache) Invalidate(options store.InvalidateOptions) error {
	log.Print("Invalidate some data...")
	return c.cache.Invalidate(options)
}
func (c *LoggableCache) Clear() error {
	log.Print("Clear some data...")
	return c.cache.Clear()
}
func (c *LoggableCache) GetType() string {
	return LoggableType
}

同样,您也可以实现自定义存储。如果您认为其他人可以使您的缓存或存储实现受益,请不要犹豫,打开拉取请求并直接为项目做出贡献,以便我们一起讨论您的想法并带来更强大的缓存库。

压缩

生成模拟测试数据:

go get github.com/golang/mock/mockgen
make mocks

测试套件可以运行:

make test # run unit test
make benchmark-store # run benchmark test

到此这篇关于Golang实现多存储驱动设计SDK案例的文章就介绍到这了,更多相关Golang SDK内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • OpenTelemetry-go的SDK使用方法详解

    目录 例子 原理 方法使用 newExporter newResource trace.NewTracerProvider otel 注意 获取当前跨度 设置span状态 设置span属性 记录错误 设置活动 tp.Shutdown 2019年5月,OpenCensus 和 OpenTracing形成了 OpenTelemetry(简称 OTel) 也就是说,我们在使用链路追踪SDK的时候就需要使用OpenTelemetry的新规范.OpenTelemetry帮我们实现了相应语言的SDK,所以我

  • 利用django+wechat-python-sdk 创建微信服务器接入的方法

    1.版本说明 :python 2.7.10, Django (1.6.11.6),centos7 2.步骤说明: A.django 建立项目 django-admin.py startproject projtest 之后启动服务器,看看是否正确: cd projtest 配置 projtest子目录下面的setting.py文件,允许外部机器访问 [root@VM_4_128_centos projtest]# vim projtest/settings.py 把其中ALLOWED_HOSTS

  • Golang实现多存储驱动设计SDK案例

    目录 支持功能 开发缘由 如何使用 安装 存储适配器 缓存适配器 编写自己的缓存或存储 压缩 前言: Gocache是一个基于Go语言编写的多存储驱动的缓存扩展组件.它为您带来了许多缓存数据的功能. 支持功能 多个缓存驱动存储:支持内存.redis或您自定义存储驱动. 支持如下功能: 链式缓存:使用具有优先级顺序的多个缓存(例如,内存然后回退到redis共享缓存). 可加载缓存:允许您调用回调函数将数据放回缓存中. 指标缓存,可让您存储有关缓存使用情况的指标(命中.未命中.设置成功.设置错误……

  • 详解领域驱动设计之事件驱动与CQRS

    目录 一.前言:从物流详情开始 二.领域事件 2.1.建模领域事件 2.2.领域事件代码解读 2.3.领域事件的存储 2.3.1.单独的EventStore 2.3.2.与业务数据一起存储 2.4.领域事件如何发布 2.4.1.由领域聚合发送领域事件 2.4.2.事件总线VS消息中间件 三.Saga分布式事务 3.1.Saga概要 3.2.Saga实现 3.2.1.协同式(choreography) 3.2.2.编排式(orchestration) 3.2.3.补偿策略 四.CQRS 五.自治服

  • Golang连接池的几种实现案例小结

    因为TCP的三只握手等等原因,建立一个连接是一件成本比较高的行为.所以在一个需要多次与特定实体交互的程序中,就需要维持一个连接池,里面有可以复用的连接可供重复使用. 而维持一个连接池,最基本的要求就是要做到:thread safe(线程安全),尤其是在Golang这种特性是goroutine的语言中. 实现简单的连接池 type Pool struct { m sync.Mutex // 保证多个goroutine访问时候,closed的线程安全 res chan io.Closer //连接存

  • MySQL之权限以及设计数据库案例讲解

    权限及设计数据库 用户管理 使用SQLyog 创建用户,并授予权限演示 基本命令 /* 用户和权限管理 */ ------------------ 用户信息表:mysql.user -- 刷新权限 FLUSH PRIVILEGES -- 增加用户 CREATE USER kuangshen IDENTIFIED BY '123456' CREATE USER 用户名 IDENTIFIED BY [PASSWORD] 密码(字符串) - 必须拥有mysql数据库的全局CREATE USER权限,或

  • Docker 存储驱动详细介绍

    Docker 存储驱动详细介绍 最近做项目,期间对Docker 存储驱动不会,于是在网上找资料,并解决了,这里就记录下. 目的 理解docker的存储方式 docker的image和container在host上的目录结构 docker image和container的内容与配置不同存储 Docker是一个开源的应用容器引擎,主要利用Linux内核namespace实现沙盒隔离,用Cgroup实现资源限制.Docker用于统一开发和部署的轻量级 Linux 容器,试图解决"依赖地狱"问

  • Docker aufs存储驱动layer、diff、mnt目录的区别介绍

    Docker基础信息 首先,先查询Docker使用的后端存储.使用命令docker info,主要关注Storage Driver相关的部分. $ docker info ... Server Version: 1.12.6 Storage Driver: aufs Root Dir: /var/lib/docker/aufs Backing Filesystem: extfs Dirs: 84 Dirperm1 Supported: true ... 由上可见,Docker的版本是1.12.6

  • 浅谈Java开发架构之领域驱动设计DDD落地

    目录 一.前言 二.开发目标 三.服务架构 3.1.应用层{application} 3.2.领域层{domain} 3.3.基础层{infrastructrue} 3.4.接口层{interfaces} 四.开发环境 五.代码示例 六.综上总结 一.前言 整个过程大概是这样的,开发团队和领域专家一起通过 通用语言(Ubiquitous Language)去理解和消化领域知识,从领域知识中提取和划分为一个一个的子领域(核心子域,通用子域,支撑子域),并在子领域上建立模型,再重复以上步骤,这样周而

  • Golang协程池gopool设计与实现

    目录 Goroutine 协程池 gopool 核心实现 Pool Task Worker 整体来看 三个角色的定位 使用 sync.Pool 进行性能优化 Goroutine Goroutine 是 Golang 提供的一种轻量级线程,我们通常称之为「协程」,相比较线程,创建一个协程的成本是很低的.所以你会经常看到 Golang 开发的应用出现上千个协程并发的场景. Goroutine 的优势: 与线程相比,Goroutines 成本很低. 它们的堆栈大小只有几 kb,堆栈可以根据应用程序的需

  • golang实现ftp实时传输文件的案例

    一.项目简介 本项目主要实现的功能是ftp客户端不断地将xml文件和jpg文件实时地上传到服务器,当然也可以是其他格式的文件.每当ftp客户端取到一个文件之后,将文件上传到服务器后,然后将其删除. 项目实现可配置,如果开发者有类似的需求,只需要修改配置文件就可以使用本项目去完成上传文件的功能. 本项目打日志是按照当天时间来生成日志文件,每天每一种类型的日志只打一个文件. 二.项目结构图片 三.项目代码 config配置中的代码 config.ini [path] xml_path = D:\\d

  • golang时间字符串和时间戳转换的案例

    1. 获取当前时间字符串和时间戳 package main import ( "fmt" "time" ) func main() { now := time.Now().UTC() // 显示时间格式: UnixDate = "Mon Jan _2 15:04:05 MST 2006" fmt.Printf("%s\n", now.Format(time.UnixDate)) // 显示时间戳 fmt.Printf(&quo

随机推荐