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)

}
相关推荐
熊小猿24 分钟前
在 Spring Boot 项目中使用分页插件的两种常见方式
java·spring boot·后端
paopaokaka_luck28 分钟前
基于SpringBoot+Vue的助农扶贫平台(AI问答、WebSocket实时聊天、快递物流API、协同过滤算法、Echarts图形化分析、分享链接到微博)
java·vue.js·spring boot·后端·websocket·spring
小蒜学长2 小时前
springboot酒店客房管理系统设计与实现(代码+数据库+LW)
java·数据库·spring boot·后端
橙子家2 小时前
Serilog 日志库简单实践(一):文件系统 Sinks(.net8)
后端
Yeats_Liao3 小时前
Go Web 编程快速入门 13 - 部署与运维:Docker容器化、Kubernetes编排与CI/CD
运维·前端·后端·golang
Yeats_Liao3 小时前
Go Web 编程快速入门 14 - 性能优化与最佳实践:Go应用性能分析、内存管理、并发编程最佳实践
前端·后端·性能优化·golang
七夜zippoe4 小时前
仓颉语言核心特性深度解析——现代编程范式的集大成者
开发语言·后端·鸿蒙·鸿蒙系统·仓颉
软件架构师-叶秋4 小时前
spring boot入门篇之开发环境搭建
java·spring boot·后端
QX_hao5 小时前
【Go】--接口(interface)
开发语言·后端·golang
不爱笑的良田5 小时前
从零开始的云原生之旅(一):把 Go 应用塞进 Docker
docker·云原生·golang