背景
假设用户数据存在数据库,现在需要一个函数,通过用户名称返回用户信息。
期望:在一次web请求中,不过调用多少次这个函数,只请求一次数据库。
基本信息
type User struct {Name stringAge 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]*Userfor _, 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]*Userfor _, 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 = allUserMapctx = 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 mainfunc ChangeMap(t map[string]string) {t["a"] = "b"}func main() {x := map[string]string{"a": "a"}ChangeMap(x)println(x["a"])
}
完整代码
package mainimport ("context""fmt"
)type User struct {Name stringAge 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]*Userfor _, 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]*Userfor _, 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 = allUserMapctx = 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)}