golang cache带索引超时缓存库实战示例
作者:w008p 发布时间:2023-07-24 04:43:11
标签:golang,cache,索引,超时,缓存库
cache 是一个带索引带超时的缓存库
目的在于优化代码结构,提供了若干实践。 https://github.com/weapons97/cache
example
定义泛型函数
1.18 已经发布一段实践了。通过泛型函数。我们可以减少循环的使用,优化代码结构。下面分享几个泛型函数和代码上的实践。
Filter 函数
// Filter filter one slice
func Filter[T any](objs []T, filter func(obj T) bool) []T {
res := make([]T, 0, len(objs))
for i := range objs {
ok := filter(objs[i])
if ok {
res = append(res, objs[i])
}
}
return res
}
// 测试[]int
func TestFilter(t *testing.T) {
ans := []int{2, 4, 6}
a := []int{1, 2, 3, 4, 5, 6}
b := Filter(a, func(i int) bool {
return i%2 == 0
})
require.Equal(t, ans, b)
spew.Dump(b)
}
// 结果
=== RUN TestFilter
([]int) (len=3 cap=6) {
(int) 2,
(int) 4,
(int) 6
}
--- PASS: TestFilter (0.00s)
PASS
// NoSpace is filter func for strings
func NoSpace(s string) bool {
return strings.TrimSpace(s) != ""
}
// 测试[]sting
func TestFilterNoSpace(t *testing.T) {
ans1 := []string{"1", "2", "3"}
a := []string{"", "1", "", "2", "", "3", ""}
b := Filter(a, NoSpace)
require.Equal(t, ans1, b)
spew.Dump(b)
}
// 结果
=== RUN TestFilterNoSpace
([]string) (len=3 cap=7) {
(string) (len=1) "1",
(string) (len=1) "2",
(string) (len=1) "3"
}
--- PASS: TestFilterNoSpace (0.00s)
PASS
Map 函数
// Map one slice
func Map[T any, K any](objs []T, mapper func(obj T) ([]K, bool)) []K {
res := make([]K, 0, len(objs))
for i := range objs {
others, ok := mapper(objs[i])
if ok {
res = append(res, others...)
}
}
return res
}
// 测试 []int -> []string
func TestMap(t *testing.T) {
ans := []string{"2", "4", "6", "end"}
a := []int{1, 2, 3, 4, 5, 6}
b := Map(a, func(i int) ([]string, bool) {
if i == 6 {
return []string{fmt.Sprintf(`%v`, i), `end`}, true
}
if i%2 == 0 {
return []string{fmt.Sprintf(`%v`, i)}, true
} else {
return nil, false
}
})
require.Equal(t, ans, b)
spew.Dump(b)
}
// 结果
=== RUN TestMap
([]string) (len=4 cap=6) {
(string) (len=1) "2",
(string) (len=1) "4",
(string) (len=1) "6",
(string) (len=3) "end"
}
--- PASS: TestMap (0.00s)
PASS
First 函数
// First make return first for slice
func First[T any](objs []T) (T, bool) {
if len(objs) > 0 {
return objs[0], true
}
return *new(T), false
}
func TestFirstInt(t *testing.T) {
ans1, ans2 := 1, 0
a := []int{1, 2, 3, 4, 5, 6}
b, ok := First(a)
require.True(t, ok)
require.Equal(t, ans1, b)
spew.Dump(b)
c := []int{}
d, ok := First(c)
require.False(t, ok)
require.Equal(t, ans2, d)
spew.Dump(d)
}
// result
=== RUN TestFirstInt
(int) 1
(int) 0
--- PASS: TestFirstInt (0.00s)
PASS
func TestFirstString(t *testing.T) {
ans1, ans2 := "1", ""
a := []string{"1", "2", "3", "4", "5", "6"}
b, ok := First(a)
require.True(t, ok)
require.Equal(t, ans1, b)
spew.Dump(b)
c := []string{}
d, ok := First(c)
require.False(t, ok)
require.Equal(t, ans2, d)
spew.Dump(d)
}
// result
=== RUN TestFirstString
(string) (len=1) "1"
(string) ""
--- PASS: TestFirstString (0.00s)
PASS
带超时的cache
某些情况下,我们删除过期的cache, 通过利用带超时的cache,简化代码
cache 结构
// 用辅助map删除
if apiRet.TotalCount > 0 {
var hc sync.Map
for _, h := range apiRet.Hcis {
hc.Store(h.HostID, h)
hostCpu.Store(h.HostID, h)
}
hostCpu.Range(func(key, _ interface{}) bool {
_, ok := hc.Load(key)
if !ok {
hostCpu.Delete(key)
}
return true
})
}
// 直接设置,过期的key 会删除
for _, h := range apiRet.Hcis {
hostCpu.Set(h.HostID, h)
}
func TestNewCache(t *testing.T) {
c := NewCache(WithTTL[string, int](time.Second))
b := 1
c.Set(`a`, b)
d, ok := c.Get(`a`)
require.True(t, ok)
require.Equal(t, b, d)
time.Sleep(time.Second)
d, ok = c.Get(`a`)
require.False(t, ok)
// 超时返回0值
require.Equal(t, d, 0)
}
集合操作
通过 set 做集合,可以给集合去重。可以给结合相并,想交,等操作。
set 结构
func TestSetUnion(t *testing.T) {
s := NewSet[string]()
s.Add(`a`)
s.Add(`b`)
s2 := NewSet[string]()
s2.Add(`b`)
s2.Add(`d`)
s3 := s.Union(s2)
wantS3 := []string{`a`, `b`, `d`}
ans := s3.List()
sort.Strings(ans)
require.Equal(t, wantS3, ans)
spew.Dump(s.List(), s2.List(), s3.List())
}
func TestSetJoin(t *testing.T) {
s := NewSet[string]()
s.Add(`a`)
s.Add(`b`)
s2 := NewSet[string]()
s2.Add(`b`)
s2.Add(`d`)
s3 := s.Join(s2)
wantS3 := []string{`b`}
ans := s3.List()
sort.Strings(ans)
require.Equal(t, wantS3, ans)
spew.Dump(s.List(), s2.List(), s3.List())
}
func TestSetJoinLeft(t *testing.T) {
s := NewSet[string]()
s.Add(`a`)
s.Add(`b`)
s2 := NewSet[string]()
s2.Add(`b`)
s2.Add(`d`)
s3 := s.JoinLeft(s2)
wantS3 := []string{`a`, `b`}
ans := s3.List()
sort.Strings(ans)
require.Equal(t, wantS3, ans)
spew.Dump(s.List(), s2.List(), s3.List())
}
func TestSetJoinRight(t *testing.T) {
s := NewSet[string]()
s.Add(`a`)
s.Add(`b`)
s2 := NewSet[string]()
s2.Add(`b`)
s2.Add(`d`)
s3 := s.JoinRight(s2)
wantS3 := []string{`b`, `d`}
ans := s3.List()
sort.Strings(ans)
require.Equal(t, wantS3, ans)
spew.Dump(s.List(), s2.List(), s3.List())
}
func TestSetSub(t *testing.T) {
s := NewSet[string]()
s.Add(`a`)
s.Add(`b`)
s2 := NewSet[string]()
s2.Add(`b`)
s2.Add(`d`)
s3 := s.Sub(s2)
wantS3 := []string{`a`}
ans := s3.List()
sort.Strings(ans)
require.Equal(t, wantS3, ans)
spew.Dump(s.List(), s2.List(), s3.List())
}
通过set 去重
// ShowImageInManifest 抓取 manifest 中imgs
func ShowImageInManifest(manifest string) (imgs []string) {
rx := regImages.FindAllStringSubmatch(manifest, -1)
set := cache.NewSet[string]()
for i := range rx {
for j := range rx[i] {
if strings.HasPrefix(rx[i][j], `image:`) {
continue
}
tx0 := strings.TrimSpace(rx[i][j])
tx1 := strings.Trim(tx0, `'`)
tx2 := strings.Trim(tx1, `"`)
set.Add(tx2)
}
}
imgs = set.List()
return imgs
}
带索引的cache
某些情况下,我们可能根据cache 的某个元素对cache进行遍历,这时候如果给cache 加上索引结构,可以对遍历加速。
index 结构
type Person struct {
id string
lastName string
fullName string
country string
}
const (
IndexByLastName = `IndexByLastName`
IndexByCountry = `IndexByCountry`
)
func (p *Person) Indexs() map[string]IndexFunc {
return map[string]IndexFunc{
IndexByLastName: func(indexed Indexed) (key []string) {
ci := indexed.(*Person)
return []string{ci.lastName}
},
IndexByCountry: func(indexed Indexed) (key []string) {
ci := indexed.(*Person)
return []string{ci.country}
},
}
}
func (p *Person) ID() (mainKey string) {
return p.id
}
func (p *Person) Set(v interface{}) (Indexed, bool) {
rx, ok := v.(*Person)
if !ok {
return nil, false
}
return rx, true
}
func (p *Person) Get(v Indexed) (interface{}, bool) {
rx, ok := v.(*Person)
if !ok {
return nil, false
}
return rx, true
}
// 测试数据
var (
p1 = &Person{
id: `1`,
lastName: "魏",
fullName: "魏鹏",
country: `China`,
}
p2 = &Person{
id: `2`,
lastName: "魏",
fullName: "魏无忌",
country: `America`,
}
p3 = &Person{
id: `3`,
lastName: "李",
fullName: "李云",
country: `China`,
}
p4 = &Person{
id: `4`,
lastName: "黄",
fullName: "黄帅来",
country: `China`,
}
p5 = &Person{
id: `5`,
lastName: "Cook",
fullName: "TimCook",
country: `America`,
}
p6 = &Person{
id: `6`,
lastName: "Jobs",
fullName: "SteveJobs",
country: `America`,
}
p7 = &Person{
id: `7`,
lastName: "Musk",
fullName: "Elon Musk",
country: `America`,
}
)
func TestIndexByCountry(t *testing.T) {
index := NewIndexer(&Person{})
// set
index.Set(p1)
index.Set(p2)
index.Set(p3)
index.Set(p4)
index.Set(p5)
index.Set(p6)
index.Set(p7)
// search
rs := index.Search(IndexByCountry, `China`)
require.False(t, rs.Failed())
rx := rs.InvokeAll()
require.Len(t, rx, 3)
spew.Dump(rx)
one := rs.InvokeOne().(*Person)
require.Equal(t, one.country, `China`)
spew.Dump(one)
}
// result
=== RUN TestIndexByCountry
([]interface {}) (len=3 cap=3) {
(*cache.Person)(0x14139c0)({
id: (string) (len=1) "3",
lastName: (string) (len=3) "李",
fullName: (string) (len=6) "李云",
country: (string) (len=5) "China"
}),
(*cache.Person)(0x1413a00)({
id: (string) (len=1) "4",
lastName: (string) (len=3) "黄",
fullName: (string) (len=9) "黄帅来",
country: (string) (len=5) "China"
}),
(*cache.Person)(0x1413940)({
id: (string) (len=1) "1",
lastName: (string) (len=3) "魏",
fullName: (string) (len=6) "魏鹏",
country: (string) (len=5) "China"
})
}
(*cache.Person)(0x14139c0)({
id: (string) (len=1) "3",
lastName: (string) (len=3) "李",
fullName: (string) (len=6) "李云",
country: (string) (len=5) "China"
})
--- PASS: TestIndexByCountry (0.00s)
PASS
来源:https://studygolang.com/articles/35840


猜你喜欢
- 一、前言嗨,大家好,我是新发。有同学私信我让我写一篇Unity网格相关的教程,那我就带大家来一次Unity的网格探险之旅吧~二、Hello
- 关于代理模式、装饰模式设计模式中经常提到的代理模式、装饰模式,这两种叫法实际上是说的同一件事,只是侧重点有所不同而已。这两者都是通过在原有对
- 最近准备使用Python+Hadoop+Pandas进行一些深度的分析与机器学习相关工作。(当然随着学习过程的进展,现在准备使用Python
- 将数据写入Excel文件中,用python实现起来非常的简单,下面一步步地教大家。一、导入excel表格文件处理函数import xlwt注
- Python语言有一种独特的推导式语法,相当于语法糖的存在,可以帮助你在某些场合写出较为精简酷炫的代码。但没有它,也不会有太多影响。Pyth
- 问题微信公众号获取code时的跳转链接,默认是获取当前页面的链接,代码如下:// 说明:获取当前页面的url地址function GetCu
- import turtle as tt.setup(800,600,0,0,)t.pensize(2)t.speed(1)t.color(&
- 导语🎁哈喽!哈喽!我是木木子😎,今日游戏更新——超级玛丽华丽上线🎊啦!“超级玛丽”有多少人还记得这款经典游戏?对于90、00后应该不大熟悉,
- 本文实例为大家分享了opencv实现图像平移效果的具体代码,供大家参考,具体内容如下图像平移:按照指定方向和距离,移动到相应位置格式:cv.
- 1,exists和in的理解exists:如果子查询中包括某一行,那么就为TRUE in:如果操作数为TRUE等于表达式列表中的一个,那么就
- if exists (select * from dbo.sysobjects where id = object_id(N'[db
- 如果你之前没用过进度条,八成是觉得它会增加不必要的复杂性或者很难维护,其实不然。要加一个进度条其实只需要几行代码。在这几行代码中,我们可以看
- Opera, 作为 A-Grade 浏览器,在现在的前端开发中务必支持。它很优秀,很不幸,bug是每个浏览器都不可避免的问题,Opera亦难
- MySQL 8.0.27 下载、安装与配置 超详细教程(Windows64位),供大家参考,具体内容如下1.官网下载1.下载地址:MySQL
- 登录注册系统是日常上网最普通的操作,我设了一个分类一步步完善注册登录系统,若哪里有误,请见谅。所用语言:php数据库 :mysql本次实现功
- 准备图片选择一张shape为(500,500,3)的梵高的《星月夜》以便示例。1. 缩放 cv2.resize()方法cv2.resize(
- JavaScript 中的 this 指向问题有很多文章在解释,仍然有很多人问。上周我们的开发团队连续两个人遇到相关问题,所以我不得不将关于
- 一、写在前面我从未想过自己会写python系列的自动化文章,有些同学会问,那你现在为什么又开始写了?不止一个人找过我,问我可以写一些Pyth
- PHP用代码实现文件下载,阅读PHP用代码实现文件下载,我们一般实现下载都是调用url来下载,但是遇到ie能识别打开的文件就不能用这种方式了
- 今天在群里,熊猫君提议整理一个帖子,一方面为初学者提供一个入门指南,另一方面也象借此和已经在从事这个行业进行一点交流。下面是我从事这个行当多