GO—web程序中的请求缓存设置

背景

假设用户数据存在数据库,现在需要一个函数,通过用户名称返回用户信息。

期望:在一次web请求中,不过调用多少次这个函数,只请求一次数据库。

基本信息

type User struct {
	Name string
	Age  int
}

func GetALLUserFromDB() []*User {
	return []*User{
		{
			Name: "Tom",
			Age:  18,
		},
		{
			Name: "Jerry",
			Age:  20,
		},
	}
}
重点

go的赋值都是值copy

实现
函数调用数据库
// GetUser 每次从数据库获取数据,根据名称返回user
func GetUser(_ context.Context, name string) *User {
	fmt.Println("GetALLUserFromDB")
	allUser := GetALLUserFromDB()
	allUserMap := make(map[string]*User)
	for _, user := range allUser {
		allUserMap[user.Name] = user
	}
	return allUserMap[name]
}

func testGetUser(names []string) {
	for _, name := range names {
		fmt.Println(GetUser(context.Background(), name))
	}
}
  • 每次函数调用都会走到数据库
  • 不满足期望
使用一个map存储用户名和用户信息的关系
// GetUserWithParamCache 传入缓存map,如果缓存map为空,从数据库获取数据,根据名称返回user
func GetUserWithParamCache(_ context.Context, name string, cache map[string]*User) *User {
	if cache == nil {
		fmt.Println("GetALLUserFromDB")
		allUser := GetALLUserFromDB()
		allUserMap := make(map[string]*User)
		for _, user := range allUser {
			allUserMap[user.Name] = user
		}
		cache = allUserMap
	}
	return cache[name]
}

func testGetUserWithParamCache(names []string) {
	var cache map[string]*User
	for _, name := range names {
		fmt.Println(GetUserWithParamCache(context.Background(), name, cache))
	}
}
  • 参数传递是值copy,这里的形参不会影响实参。所以下次请求该函数的时候,cache总是为空
  • 注意,这里map的值copy是map的结构,其指向数据如果发生了变化,还是能影响实参的指向的数据的
    • 参考附录
  • 不满足期望
使用一个map存储用户名和用户信息的关系,map参数是指针类型
// GetUserWithParamCacheWithPointer 传入缓存map指针,如果缓存map为空,从数据库获取数据,根据名称返回user
func GetUserWithParamCacheWithPointer(_ context.Context, name string, cache *map[string]*User) *User {
	if cache == nil || *cache == nil {
		fmt.Println("GetALLUserFromDB")
		allUser := GetALLUserFromDB()
		allUserMap := make(map[string]*User)
		for _, user := range allUser {
			allUserMap[user.Name] = user
		}
		*cache = allUserMap
	}
	return (*cache)[name]
}

func testGetUserWithParamCacheWithPointer(names []string) {
	var cache map[string]*User
	for _, name := range names {
		fmt.Println(GetUserWithParamCacheWithPointer(context.Background(), name, &cache))
	}
}
  • 实参和形参都指向同一个map,该map变更时,实参指向的map也会变
  • 满足期望
使用context存储缓存
// GetUserWithCtxCache 从context中获取缓存map,如果缓存map为空,从数据库获取数据,根据名称返回user
func GetUserWithCtxCache(ctx context.Context, name string) *User {
	cache, ok := ctx.Value("cache").(map[string]*User)
	if !ok {
		fmt.Println("GetALLUserFromDB")
		allUser := GetALLUserFromDB()
		allUserMap := make(map[string]*User)
		for _, user := range allUser {
			allUserMap[user.Name] = user
		}
		cache = allUserMap
		ctx = context.WithValue(ctx, "cache", allUserMap)
	}

	return cache[name]
}

func testGetUserWithCtxCache(names []string) {
	ctx := context.Background()
	for _, name := range names {
		fmt.Println(GetUserWithCtxCache(ctx, name))
	}
}
  • context也是值copy,实参不受影响
  • 不满足期望
context参数是指针,存储缓存
// GetUserWithCtxCacheWithPointer context为指针。从context中获取缓存map指针,如果缓存map为空,从数据库获取数据,根据名称返回user
func GetUserWithCtxCacheWithPointer(ctx *context.Context, name string) *User {
	cache, ok := (*ctx).Value("cache").(map[string]*User)
	if !ok {
		fmt.Println("GetALLUserFromDB")
		allUser := GetALLUserFromDB()
		allUserMap := make(map[string]*User)
		for _, user := range allUser {
			allUserMap[user.Name] = user
		}
		cache = allUserMap
		*ctx = context.WithValue(*ctx, "cache", allUserMap)
	}

	return cache[name]
}

func testGetUserWithCtxCacheWithPointer(names []string) {
	ctx := context.Background()
	for _, name := range names {
		fmt.Println(GetUserWithCtxCacheWithPointer(&ctx, name))
	}
}
  • 满足期望
附录
context设置value的情景
	t := context.Background().Value("cache")
	x,ok := t.(map[string]*User)
	fmt.Println(x,ok)

	ct := context.WithValue(context.Background(), "cache", nil)
	t = ct.Value("cache")
	x,ok = t.(map[string]*User)
	fmt.Println(x,ok)

	ct = context.WithValue(context.Background(), "cache", make(map[string]*User))
	t = ct.Value("cache")
	x,ok = t.(map[string]*User)
	fmt.Println(x,ok)

	ct = context.WithValue(context.Background(), "cache", map[string]*User{"Tom":&User{Name:"Tom",Age:18},"Jerry":&User{Name:"Jerry",Age:20}})
	t = ct.Value("cache")
	x,ok = t.(map[string]*User)
	fmt.Println(x,ok)
map作为参数
package main

func ChangeMap(t map[string]string) {
	t["a"] = "b"

}

func main() {
	x := map[string]string{"a": "a"}
	ChangeMap(x)
	println(x["a"])
}
完整代码
package main

import (
	"context"
	"fmt"
)

type User struct {
	Name string
	Age  int
}

func GetALLUserFromDB() []*User {
	return []*User{
		{
			Name: "Tom",
			Age:  18,
		},
		{
			Name: "Jerry",
			Age:  20,
		},
	}
}

// GetUser 每次从数据库获取数据,根据名称返回user
func GetUser(_ context.Context, name string) *User {
	fmt.Println("GetALLUserFromDB")
	allUser := GetALLUserFromDB()
	allUserMap := make(map[string]*User)
	for _, user := range allUser {
		allUserMap[user.Name] = user
	}
	return allUserMap[name]
}

func testGetUser(names []string) {
	for _, name := range names {
		fmt.Println(GetUser(context.Background(), name))
	}
}

// GetUserWithParamCache 传入缓存map,如果缓存map为空,从数据库获取数据,根据名称返回user
func GetUserWithParamCache(_ context.Context, name string, cache map[string]*User) *User {
	if cache == nil {
		fmt.Println("GetALLUserFromDB")
		allUser := GetALLUserFromDB()
		allUserMap := make(map[string]*User)
		for _, user := range allUser {
			allUserMap[user.Name] = user
		}
		cache = allUserMap
	}
	return cache[name]
}

func testGetUserWithParamCache(names []string) {
	var cache map[string]*User
	for _, name := range names {
		fmt.Println(GetUserWithParamCache(context.Background(), name, cache))
	}
}

// GetUserWithParamCacheWithPointer 传入缓存map指针,如果缓存map为空,从数据库获取数据,根据名称返回user
func GetUserWithParamCacheWithPointer(_ context.Context, name string, cache *map[string]*User) *User {
	if cache == nil || *cache == nil {
		fmt.Println("GetALLUserFromDB")
		allUser := GetALLUserFromDB()
		allUserMap := make(map[string]*User)
		for _, user := range allUser {
			allUserMap[user.Name] = user
		}
		*cache = allUserMap
	}
	return (*cache)[name]
}

func testGetUserWithParamCacheWithPointer(names []string) {
	var cache map[string]*User
	for _, name := range names {
		fmt.Println(GetUserWithParamCacheWithPointer(context.Background(), name, &cache))
	}
}

// GetUserWithCtxCache 从context中获取缓存map,如果缓存map为空,从数据库获取数据,根据名称返回user
func GetUserWithCtxCache(ctx context.Context, name string) *User {
	cache, ok := ctx.Value("cache").(map[string]*User)
	if !ok {
		fmt.Println("GetALLUserFromDB")
		allUser := GetALLUserFromDB()
		allUserMap := make(map[string]*User)
		for _, user := range allUser {
			allUserMap[user.Name] = user
		}
		cache = allUserMap
		ctx = context.WithValue(ctx, "cache", allUserMap)
	}

	return cache[name]
}

func testGetUserWithCtxCache(names []string) {
	ctx := context.Background()
	for _, name := range names {
		fmt.Println(GetUserWithCtxCache(ctx, name))
	}
}

// GetUserWithCtxCacheWithPointer context为指针。从context中获取缓存map指针,如果缓存map为空,从数据库获取数据,根据名称返回user
func GetUserWithCtxCacheWithPointer(ctx *context.Context, name string) *User {
	cache, ok := (*ctx).Value("cache").(map[string]*User)
	if !ok {
		fmt.Println("GetALLUserFromDB")
		allUser := GetALLUserFromDB()
		allUserMap := make(map[string]*User)
		for _, user := range allUser {
			allUserMap[user.Name] = user
		}
		cache = allUserMap
		*ctx = context.WithValue(*ctx, "cache", allUserMap)
	}

	return cache[name]
}

func testGetUserWithCtxCacheWithPointer(names []string) {
	ctx := context.Background()
	for _, name := range names {
		fmt.Println(GetUserWithCtxCacheWithPointer(&ctx, name))
	}
}



func main() {
	names := []string{"Tom", "Jerry", "Lucy"}

	fmt.Println("=====================================", "testGetUser")
	testGetUser(names)
	fmt.Println("=====================================", "testGetUserWithParamCache")
	testGetUserWithParamCache(names)
	fmt.Println("=====================================", "testGetUserWithParamCacheWithPointer")
	testGetUserWithParamCacheWithPointer(names)
	fmt.Println("=====================================", "testGetUserWithCtxCache")
	testGetUserWithCtxCache(names)
	fmt.Println("=====================================", "testGetUserWithCtxCacheWithPointer")
	testGetUserWithCtxCacheWithPointer(names)

	t := context.Background().Value("cache")
	x,ok := t.(map[string]*User)
	fmt.Println(x,ok)

	ct := context.WithValue(context.Background(), "cache", nil)
	t = ct.Value("cache")
	x,ok = t.(map[string]*User)
	fmt.Println(x,ok)

	ct = context.WithValue(context.Background(), "cache", make(map[string]*User))
	t = ct.Value("cache")
	x,ok = t.(map[string]*User)
	fmt.Println(x,ok)

	ct = context.WithValue(context.Background(), "cache", map[string]*User{"Tom":&User{Name:"Tom",Age:18},"Jerry":&User{Name:"Jerry",Age:20}})
	t = ct.Value("cache")
	x,ok = t.(map[string]*User)
	fmt.Println(x,ok)

}
相关推荐
weixin_teach007x1 小时前
springboot+vue+mybatis校园热点新闻系统+PPT+论文+讲解+售后
java·数据库·spring boot·后端·mybatis
有梦想的小何1 小时前
解决前端和后端时间不一致问题的实践指南
java·前端·后端·状态模式
VinciYan2 小时前
使用[Flags]和按位操作符优化C#枚举处理
后端·微软·c#·asp.net·.net·.netcore
AskHarries3 小时前
Spring Boot集成Spring Batch快速入门Demo
java·spring boot·后端·spring·batch·spring batch
子云之风3 小时前
跟ChatGPT学习go语言--time.Sleep 方法 单位是什么
开发语言·学习·golang
码农小旋风3 小时前
IO多路复用之poll、epoll和select区分
后端
代码之光_19804 小时前
构建Scala项目的魔法:Gradle中配置Scala插件
开发语言·后端·scala
VinciYan4 小时前
Visual Studio 2022美化
ide·后端·微软·开发工具·visual studio
huanhuan_m15 小时前
SpringBoot整合iText7导出PDF及性能优化
spring boot·后端·pdf
cong*5 小时前
深入理解PHP基础【代码审计实战指南】
后端·安全·web安全·php·个人开发·安全架构·安全性测试