背景
假设用户数据存在数据库,现在需要一个函数,通过用户名称返回用户信息。
期望:在一次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)
}