golang实现多存储驱动设计sdk

7small7 7small7     2022-12-02     196

关键词:

本文已收录​​编程学习笔记​​。涵盖PHP、JavaScript、Linux、Golang、MySQL、Redis和开源工具等等相关内容。

认识Gocache

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

支持功能

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

✅链式缓存:使用具有优先级顺序的多个缓存(例如,内存然后回退到redis共享缓存)。

✅可加载缓存:允许您调用回调函数将数据放回缓存中。

✅指标缓存,可让您存储有关缓存使用情况的指标(命中、未命中、设置成功、设置错误……)。

✅自动编组/解组缓存值作为结构的编组器。

✅在存储中定义默认值并在设置数据时覆盖它们。

✅通过过期时间和/或使用标签缓存失效。

✅泛型的使用。

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

  1. 内存 (bigcache) (allegro/bigcache)。
  2. 内存 (ristretto) (dgraph-io/ristretto)。
  3. 内存 (go-cache) (patrickmn/go-cache)。
  4. 内存缓存(bradfitz/memcache)。
  5. Redis (go-redis/redis)。
  6. 空闲缓存(coocood/freecache)。
  7. 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 实现了以下存储:

  1. BigCache:内存中的存储。
  2. Ristretto : DGraph 提供的另一个内存存储。
  3. Memcache:基于 bradfitz/gomemcache 客户端库的 memcache 存储。
  4. 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.ConfigNumCounters: 1000, MaxCost: 100, BufferItems: 64)
if err != nil
panic(err)


redisClient := redis.NewClient(&redis.OptionsAddr: "127.0.0.1:6379")

// Initialize stores
ristrettoStore := store.NewRistretto(ristrettoCache, nil)
redisStore := store.NewRedis(redisClient, &cache.OptionsExpiration: 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 := BookQuerySlug: "my-test-amazing-book"
value := BookID: 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

领域驱动(ddd)之我见,基于golang实现

分享一点不成熟的理解,还请本着交流进步的大原则喷之。从去年开始接触和套用DDD以来,已经有1年多时间了。也先后在2个生产项目中主导应用,都是基于.NetCore的,完全参考https://github.com/EduardoPires/EquinoxProject该项目搭建的基... 查看详情

实现领域驱动设计

应用程序服务应用程序服务是一种无状态的服务,它实现应用程序的用例。应用程序服务通常获取和返回dto。它由表示层使用。它使用并协调领域对象(实体、存储库等)来实现用例应用程序服务的常见原则如下:实现特定于当前用... 查看详情

实现ddd领域驱动设计:part2

...接:https://dev.to/salah856/implementing-domain-driven-design-part-ii-2i36实现:构建块这是本系列的重要部分。我们将通过示例介绍和解释一些明确的规则。在实现领域驱动设计时,你可以遵循这些规则并应用到你的解决方案中。示例示... 查看详情

源码分析rocketmqdledger多副本存储实现

RocketMQDLedger的存储实现思路与RocketMQ的存储实现思路相似,本文就不再从源码角度详细剖析其实现,只是点出其实现关键点。我们不妨简单回顾一下CommitLog文件、ConsumeQueue文件设计思想。其文件组成形式如下:正如上图所示,多... 查看详情

Golang 事务 API 设计

】Golang事务API设计【英文标题】:GolangTransactionalAPIdesign【发布时间】:2019-01-2512:38:17【问题描述】:我正在尝试使用Go来关注CleanArchitecture。该应用程序是一个简单的图像管理应用程序。我想知道如何最好地为我的存储库层设计... 查看详情

从golang中open的实现方式看golang的语言设计(代码片段)

Golang的设计目标$Golang有很多优点:开发高效;(C语言写一个hash查找很麻烦,但是go很简单)运行高效;(Python的hash查找好写,但比Python高效很多)很少的系统库依赖;(环境依赖少,一般不依赖各种LibPath等)简单可依赖;(... 查看详情

easyrtc通过golang缓存库fastcache实现在线用户存储在内存中加快速度(代码片段)

EasyRTC是TSINGSEE青犀视频团队在音视频领域多年的技术积累而研发的一款产品。它是覆盖全球的实时音频开发平台,支持一对一、一对多等视频通话。EasyRTC拥有MCU和SFU两种架构,无需安装客户端与插件,纯H5在线视频会... 查看详情

golang:时间窗口法实现限流器

packagemainimport( "container/list" "fmt" "sync" "time")/*Author:GuoDate:3/15/214:58PMDescription:Company:Updated:姓名@时间@版本变更说明*///限量器typeLimitorstruct //锁 Locksync.Mutex //存储元素的双向链表 Elements*list.List //最多可... 查看详情

golang:时间窗口法实现限流器

packagemainimport( "container/list" "fmt" "sync" "time")/*Author:GuoDate:3/15/214:58PMDescription:Company:Updated:姓名@时间@版本变更说明*///限量器typeLimitorstruct //锁 Locksync.Mutex //存储元素的双向链表 Elements*list.List //最多可... 查看详情

golang怎么实现横向扩展

参考技术AGolang进行服务器开发,最显耀的就是其并发架构,能充分榨干每一个CPU.但是Golang和Erlang不一样,Golang使用了CSP的模型,而Erlang采用的是Actor模型.两者区别仅仅只是消息队列归属范围区别而已.但带来的巨大的框架实现及使用... 查看详情

乐鑫esp32学习之旅28分享在esp32sdk实现驱动flashw25q32的封装,扩大容量存储更多的资源。(开源源码工程)。(代码片段)

本系列博客学习由非官方人员半颗心脏潜心所力所写,仅仅做个人技术交流分享,不做任何商业用途。如有不对之处,请留言,本人及时更改。系列一:ESP32系列模组基础学习系列笔记1、爬坑学习新旅程,... 查看详情

golang策略设计模式(代码片段)

...饭的时候,我们有三种主食可以选,米饭、面包和面条。golang代码:packagemainimport"fmt"//定义类接口typeStapleFoodinterface Eat()//定义接口实现类1typeRiceStapleFoodstruct//定义接口实现类2typeNoodleStapleFoodstruct//定义接口实现类3typeBreadSta... 查看详情

领域驱动系列五模型驱动设计的构造块

一、简介为了保证软件实现的简洁性,并且与模型保持一致,不管实际情况有多复杂,必须使用建模和设计的最佳实践,即让通过我们的编程技术(设计模型、指责驱动、契约式设计)充分地体现领域模型,并保持模型地健壮性和可扩展... 查看详情

3.15go微服务实战(微服务理论)---领域驱动设计的go语言实现

第15章 领域驱动设计的Go语言实现15.1 聚合模式介绍 实体(entity):可以持久化存储的对象 值对象(valueobject):值集合的对象,比如"钱"这个值对象包括"币种"和"金额"两个值的集合 工厂(factory):负责初始化对象的对象或方法 ... 查看详情

golang设计模式7.外观模式(代码片段)

7.外观模式外观模式,为子系统中的一组接口提供一个一致的界面,此模式定义了一个高层接口,这个接口使得这一子系统更加容易使用。像平时我们操作slice的时候,尤其是往slice中间插入、删除元素多有不便,我们可以在slice... 查看详情

golang:缺乏包含方法设计理由[关闭]

】golang:缺乏包含方法设计理由[关闭]【英文标题】:go-lang:lackofcontainsmethoddesign-justification[closed]【发布时间】:2015-02-1712:51:26【问题描述】:在浏览contains方法时,我遇到了以下问答contains-method-for-a-slice在这个问答中一再提到该... 查看详情

在 DDD 中将存储库实现保存在哪里?

】在DDD中将存储库实现保存在哪里?【英文标题】:WheretoKeeptheRepositoryimplementationsinDDD?【发布时间】:2014-10-1817:28:30【问题描述】:1)根据域驱动设计,域层应该只有存储库接口,实现不应该是域层的一部分-如果我的理解有误,... 查看详情

golang使用位左移与iota计数配合可优雅地实现存储单位的常量枚举(代码片段)

查看详情