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

news2024/11/22 16:37:27
背景

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

}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1673446.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

K8s源码分析(一)-K8s调度框架及调度器初始化介绍

本文首发在个人博客上,欢迎来踩! 文章目录 调度框架介绍K8s scheduler 介绍K8s scheduler的初始化Cobra介绍K8s scheduler中初始化的源代码解析 调度框架介绍 这是官方对于v1.27调度框架的介绍文档:https://v1-27.docs.kubernetes.io/docs/…

开源模型应用落地-CodeQwen模型小试-集成langchain(四)

一、前言 通过学习代码专家模型,开发人员可以获得高效、准确和个性化的代码支持。这不仅可以提高工作效率,还可以在不同的技术环境中简化软件开发工作流程。代码专家模型的引入将为开发人员带来更多的机会去关注创造性的编程任务,从而推动软件…

kubernetes1.27.3版本单主机部署详细教程

本次kubernetes单主机部署是基于AnolisOS-7.9操作系统进行部署的,当然也适用于Centos7的操作系统,根据个人情况,选择适合自己的版本! 本次部署的kubernetes版本是1.27.3版本,系统架构是etcd,API Server,Con…

【Jenkins】Pipeline流水线语法解析全集 -- 声明式流水线

👨‍🎓博主简介 🏅CSDN博客专家   🏅云计算领域优质创作者   🏅华为云开发者社区专家博主   🏅阿里云开发者社区专家博主 💊交流社区:运维交流社区 欢迎大家的加入&#xff01…

嵌入式学习<2>:EXTI、ADC、NVIC和AFIO

嵌入式学习_part2 本部分笔记用于学习记录,笔记源头 >>b站江科大_STM32入门教程_EXTI EXTI、ADC、NVIC和AFIO 开发环境:keil MDK、STM32F103C8T6 1 )EXTI STM32F10xxx参考手册(中文)-> 中断与事件 ->…

死锁调试技巧:工作线程和用户界面线程

有人碰到了一个死锁问题,找到我们想请我们看看,这个是关于应用程序用户界面相关的死锁问题。 我也不清楚他为什么会找上我们,可能是因为我们经常会和窗口管理器打交道吧。 下面,我们来看看死锁的两个线程。 >> 请移步至 …

【SpringSecurity源码】过滤器链加载流程

theme: smartblue highlight: a11y-dark 一、前言及准备 1.1 SpringSecurity过滤器链简单介绍 在Spring Security中,过滤器链(Filter Chain)是由多个过滤器(Filter)组成的,这些过滤器按照一定的顺序对进…

LeetCode算法题:49. 字母异位词分组(Java)

给你一个字符串数组,请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。 字母异位词 是由重新排列源单词的所有字母得到的一个新单词。 示例 1: 输入: strs ["eat", "tea", "tan", "ate", "nat", …

试试这四个AI论文工具和降重技术,低成本高回报

在科研领域,AI写作工具如同新一代的科研利器,它们能够极大提高文献查阅、思路整理和表达优化的效率,本质上促进了科研工作的进步。AI写作工具不仅快速获取并整理海量信息,还帮助我们精确提炼中心思想,显著提升论文写作…

Windows系统安装MongoDB数据库

MongoDB是一个基于分布式文件存储的NoSQL数据库,由C语言编写的。MongoDB的数据存储基本单元是文档,它是由多个键值对有序组合的数据单元,类似于关系数据库中的数据记录。适合存储JSON形式的数据,数据格式自由,不固定。…

自制无感无刷电机驱动板

自制无感无刷电机驱动板 分别测试了基于C251的STC32G单片机、Arduino AVR的ATmega328PB、以及ARM的ST32F103单片机。 🧲测试转动效果 ✒目前市面上开源的有关无刷电机的项目数不胜数,其控制原理都大同小异,在没有领透其技术要领情况下&#x…

怎么申请一年期免费的https证书

随着互联网的推广和普及,如今HTTPS证书的普及度还是比较高的了,大家对于https证书的需求度也在日益提升。针对于一些个人用户或是企业而言,实现网站的https访问已经成为了一种标配。从去年年底开始,各大SSL证书厂商陆续下架一年期…

用Python和GUI实现Socket多线程通信方案

下面是一个使用 Python 和 Tkinter GUI 库实现 Socket 多线程通信的简单示例。在这个示例中,我是创建了一个简单的聊天应用,其中服务器和客户端可以通过 Socket 进行通信。 1、问题背景 这个问题与在 Python 应用中使用 pyGTK、线程和套接字相关。开发者…

GD32驱动LCD12864

目录 1、引言 1.1、LCD12864基本概念和作用。 1.2、硬件引脚 2、GD32微控制器简介 3、LCD12864显示屏简介 3.1、模块引脚说明 3.2、模块连接方式 4、驱动原理 4.1、指令集 4.2、显示坐标关系 5、软件开发 6、硬件连接 7、效果演示 8、附录 1、引言 1.1、LCD12…

地下车库导航地图怎么做?停车场地图绘制软件哪个好?

上海懒图科技以先进技术和丰富的行业服务经验为用户提供停车场景下的全流程服务平台,用户基于平台可自主快速绘制酷炫的停车场地图,通过提供完善的停车场应用功能集和扩展API服务包,可以方便地实现电子地图服务于您的各类停车场应用中&#x…

【SRC实战】小游戏漏洞修改分数打榜

挖个洞先 https://mp.weixin.qq.com/s/Um0HU2srvZ0UlZRAsbSVug “ 以下漏洞均为实验靶场,如有雷同,纯属巧合 ” 01 — 漏洞证明 “ 如何刷分提高排名?” 1、进入小游戏,类似于跳一跳 2、开始时每次加1分 3、随着游戏进行…

中国GDP空间分布数据集

中国GDP空间分布公里网格数据集是在全国分县GDP统计数据的基础上,考虑人类活动密切相关的土地利用类型、夜间灯光亮度、居民点密度数据与GDP的空间互动规律,通过空间插值生成的空间格网数据。数据包括1995、2000、2005、2010、2015和2019年6期。该数据集…

Vue3实战笔记(20)—封装头部导航组件

文章目录 前言一、封装头部导航栏二、使用步骤总结 前言 Vue 3 封装头部导航栏有助于提高代码复用性、统一风格、降低维护成本、提高可配置性和模块化程度,同时还可以实现动态渲染等功能,有利于项目开发和维护。 一、封装头部导航栏 封装头部导航栏&am…

11个免费的 android数据恢复应用程序功能分析

在手机上丢失数据是一个很大的错误。但是,在这种情况下,除了惊慌失措之外,最好开始使用android数据恢复应用程序搜索以查找将其取回的方法。您可以检查手机的备份存储以在Android上进行数据恢复,但是如果数据仍然无处可寻&#xf…

spring cloud alibaba、spring cloud和springboot三者的版本兼容

官方版本说明地址: 版本说明 alibaba/spring-cloud-alibaba Wiki GitHub 组件版本关系 每个 Spring Cloud Alibaba 版本及其自身所适配的各组件对应版本如下表所示(注意,Spring Cloud Dubbo 从 2021.0.1.0 起已被移除出主干,不再随主干演进): Spring Cloud Alibaba Ve…