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)

}
相关推荐
明月看潮生1 分钟前
青少年编程与数学 02-003 Go语言网络编程 14课题、Go语言Udp编程
青少年编程·golang·网络编程·编程与数学
monkey_meng1 小时前
【Rust中的迭代器】
开发语言·后端·rust
余衫马1 小时前
Rust-Trait 特征编程
开发语言·后端·rust
monkey_meng1 小时前
【Rust中多线程同步机制】
开发语言·redis·后端·rust
hlsd#1 小时前
go 集成go-redis 缓存操作
redis·缓存·golang
paopaokaka_luck5 小时前
【360】基于springboot的志愿服务管理系统
java·spring boot·后端·spring·毕业设计
码农小旋风7 小时前
详解K8S--声明式API
后端
Peter_chq7 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
Yaml47 小时前
Spring Boot 与 Vue 共筑二手书籍交易卓越平台
java·spring boot·后端·mysql·spring·vue·二手书籍
小小小妮子~7 小时前
Spring Boot详解:从入门到精通
java·spring boot·后端