go http框架下的静态资源代理实现(压缩,缓存验证自定义)

之前在这一篇文章里说了我的第一版静态资源代理,后面我又完善了一下:
上一种方案的问题:

  1. 首页未加入自定义代理中
  2. 依赖了gin框架的file()方法
  3. 反复访问本地文件,访问文件系统是很消耗性能的

所以本次我做了改进,思路是:

  1. 鉴于网站的静态资源占用空间很小,所以我将所有文件加载到内存中,以此摆脱反复访问文件系统,直接访问内存的性能高很多
  2. 由于文件都在内存中,也就不需要依赖gin的file()方法去读取文件,直接用write()方法放回二进制数据
  3. 依赖http的缓存机制,增加自定义的文件修改判断(根据文件的md5码或最后修改时间),配合客户端实现缓存机制

缺点也有:

  1. 需要占用一定的内存,其实一般不多,就10M以内
  2. 静态资源的更新无法及时更新到内存中(可以通过定时任务或写一个接口手工更新)

照着以上思路,可以在其他语言其他框架中实现,因为对框架没有依赖,都是使用的一些基本功能

第一步,将静态资源加载到内存

首先,实现一个静态资源管理文件:

package globalimport ("crypto/md5""encoding/hex""os""path/filepath""strings""github.com/gin-gonic/gin"
)type StaticFile struct {File []byteMd5  string
}var WebFlieMap = make(map[string]*StaticFile) // 加载前端静态文件到内存,以便快速读取/*** @description: 加载静态资源到内存* @param {map[string]*StaticFile} fileMap* @return {*}*/
func LoadWeb(fileMap map[string]*StaticFile) error {// 遍历目录err := filepath.Walk("./web", func(path string, info os.FileInfo, err error) error {if err != nil {return err}// 忽略目录本身,只处理文件if !info.IsDir() {// 移除根目录路径relativePath := strings.ReplaceAll(strings.TrimPrefix(path, "web"), "\\", "/")// 读取文件内容var thisFile StaticFilethisFile.File, err = os.ReadFile(path)sum := md5.Sum(thisFile.File)thisFile.Md5 = hex.EncodeToString(sum[:])if err != nil {return err}// 将文件路径和内容存入mapfileMap[relativePath] = &thisFile}return nil})if err != nil {return err}return nil
}
/*** @description: 更新静态文件的接口* @param {*gin.Context} c* @return {*}*/
func UpdateStaticFile(c *gin.Context) {WebFlieMapNew := make(map[string]*StaticFile) // 先用一个新的map,防止并发问题if err := LoadWeb(WebFlieMapNew); err != nil {thislog := Log{}thislog.Error("更新静态文件失败", err)c.Data(200, "text/html", []byte(err.Error()))return}WebFlieMap = WebFlieMapNew // 然后直接覆盖原来的mapc.Data(200, "text/html", []byte("更新成功"))
}

记得在项目启动时,需要调一遍LoadWeb()

然后就是代理了,http的path,就是map的key值:

package proxyimport ("path"g "src/global""strings""github.com/gin-gonic/gin"
)/*** @description: 静态资源的代理,包含了判断是否支持压缩,是否启用缓存* @param {*gin.Context} c* @return {*}*/
func ProxyStatic(c *gin.Context) {var file stringurlPath := c.Request.URL.Path// 路径以/结尾,或者直接以一个目录结尾的,默认其请求的是index.htmlif strings.HasSuffix(urlPath, "/") || !strings.Contains(urlPath, ".") {file = path.Join(urlPath, "index.html")c.Header("Content-Type", `text/html; charset=utf-8`) // 因为后续可能返回它的压缩文件,必须得手工设置类型,否则c.File获取的是压缩文件的类型} else { // 其他则是带具体文件名的请求,当前我的文件类型里只有.js、.css、.jpg和.json,如果还有其他,需在此补充file = urlPathif strings.HasSuffix(urlPath, ".js") {c.Header("Content-Type", `application/javascript; charset=utf-8`) // 因为后续可能返回它的压缩文件,必须得手工设置类型,否则c.File获取的是压缩文件的类型} else if strings.HasSuffix(urlPath, ".css") {c.Header("Content-Type", `text/css; charset=utf-8`) // 因为后续可能返回它的压缩文件,必须得手工设置类型,否则c.File获取的是压缩文件的类型} else if strings.HasSuffix(urlPath, ".jpg") {c.Header("Content-Type", `image/jpeg`)} else if strings.HasSuffix(urlPath, ".json") {c.Header("Content-Type", `application/json`)}}// 判断文件是否存在,如果不存在则返回404.html_, ok := g.WebFlieMap[file]if !ok {c.Header("Content-Type", `text/html; charset=utf-8`) // 因为后续可能返回它的压缩文件,必须得手工设置类型,否则c.File获取的是压缩文件的类型file = `/404.html`} else if c.Request.Header.Get("If-None-Match") == g.WebFlieMap[file].Md5 { // 检查客户端是否启用缓存,如果启用则检查文件是否修改writeNotModified(c)return}c.Header("Etag", g.WebFlieMap[file].Md5)// 下面是判断请求是否支持压缩,如果支持,先判断是否有对应的压缩文件,有则返回压缩文件,无则返回原文件acceptEncoding := c.Request.Header.Get("Accept-Encoding")if strings.Contains(acceptEncoding, "br") {_, ok = g.WebFlieMap[file+".br"]if ok {c.Header("Content-Encoding", "br")file = file + ".br"}} else if strings.Contains(acceptEncoding, "gzip") {_, ok = g.WebFlieMap[file+".gz"]if ok {c.Header("Content-Encoding", "gzip")file = file + ".gz"}}c.Writer.Write(g.WebFlieMap[file].File)
}/*** @description: 文件未修改的响应,删除掉不需要的header* @param {*gin.Context} c* @return {*}*/
func writeNotModified(c *gin.Context) {delete(c.Writer.Header(), "Content-Type")c.Status(304)
}

自定义代理就更灵活,是否返回压缩文件,是否使用缓存,文件是否修改的校验方式,都可以自己实现。

最后注册一个路由:

route.GET("/update_static", global.UpdateStaticFile)

这样,当静态资源更新时,调用一便接口便可以实现更新

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

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

相关文章

0418EmpTomCat项目 初次使用ajax实现局部动态离职

0418EmpTomCat项目包-CSDN博客 数据库字段: 员工部门表 分页查询; 多条件查询; 添加新员工; ajax点击离职操作效果:

【CTF Web】BUUCTF BUU CODE REVIEW 1 Writeup(代码审计+PHP弱类型漏洞+MD5的0e绕过+反序列化)

BUU CODE REVIEW 1 1 https://github.com/glzjin/buusec_2019_code_review_1 解法 <?php /*** Created by PhpStorm.* User: jinzhao* Date: 2019/10/6* Time: 8:04 PM*/highlight_file(__FILE__);class BUU {public $correct "";public $input ""…

Fabric,一个简洁远程系统管理Python库

Fabric,一个简洁远程系统管理Python库&#xff01; 什么是 Fabric 库&#xff1f; Fabric 是一个 Python 库,用于简化远程系统管理和部署任务的执行.它提供了一种简洁的方式来定义并运行SSH 命令,使得管理远程服务器变得更加高效和便捷. 安装与使用 # 通过 pip 来安装 Fabri…

动态数据结构中的表扩张性:摊还分析、伪代码与C语言实现

动态数据结构中的表扩张性&#xff1a;摊还分析、伪代码与C语言实现 引言表扩张性的概念摊还分析在表扩张性中的应用伪代码示例&#xff1a;TABLE-INSERT操作C语言实现结论 引言 在处理数据结构时&#xff0c;尤其是表&#xff08;或数组&#xff09;&#xff0c;我们经常面临…

Idea报错:无法访问org.springframework.boot.SpringApplication

在开发项目时&#xff0c;常常会遇到这种问题&#xff0c;报错信息如下图所示 版本号与jdk版本号存在对应关系&#xff0c;61.0对应jdk17&#xff0c;52.0对应jdk8 所以是某个依赖的版本太高&#xff0c;降低该依赖的版本即可 具体步骤&#xff1a; ①修改pom.xml中spring b…

【linuxC语言】exec函数族

文章目录 前言一、exec函数族二、示例代码2.1 代码12.2 代码22.3 代码3 总结 前言 在Linux环境下&#xff0c;C语言提供了一组强大的函数族&#xff0c;即exec函数族&#xff0c;用于执行其他程序。这些函数允许程序在运行时加载并执行不同的程序&#xff0c;从而实现了程序之…

使用docker部署nacos2.2.3单节点

docker部署nacos2.2.3 首先nacos要配合mysql进行初始化数据&#xff0c;部署一个mysql5.7版本的。 systemctl stop firewalld && setenforce 0 关闭防火墙和selinuxdocker pull mysql:5.7 && docker pull nacos/nacos-server:v2.2.3 拉取镜像docker …

redis中的集群模式

主从复制、主从同步(解决高并发读的问题) 主从同步原理&#xff1a; 1.全量同步 slave&#xff08;从节点&#xff09;每次请求数据同步会带两个参数&#xff1a;replid和offset。 replid&#xff1a;第一次请求同步时&#xff0c;replid和master的replid不一样&#xff0c;这…

Git Commit命令撤销操作

Git Commit命令撤销操作 在某些时候&#xff0c;通过git commit命令讲代码提交到了暂存区&#xff0c;但还未推送至远程仓库需要进行撤销&#xff0c;可以使用git reset命令来完成 文章目录 Git Commit命令撤销操作1. git commit 撤销操作2. 撤销多次想回到某一次撤销 1. git c…

探索AI工具的巅峰:个人体验与深度剖析

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

[C++] 类和对象 _ 剖析构造、析构与拷贝

一、构造函数 构造函数是特殊的成员函数&#xff0c;它在创建对象时自动调用。其主要作用是初始化对象的成员变量&#xff08;不是开辟空间&#xff09;。构造函数的名字必须与类名相同&#xff0c;且没有返回类型&#xff08;即使是void也不行&#xff09;。 在C中&#xff0…

Vue2动态添加属性方式

Vue2不允许在已经创建的实例上动态添加新的响应式属性;若想实现数据与视图同步更新&#xff0c;可采取下面两种解决方案&#xff1a; 1.使用Vue.set&#xff0c;里面的参数是Vue.set(target,index, value) // 部分代码 export default {data() {return {info: {username: ala…

【Canvas技法】流星雨的实现

【关键点】 流星的绘制&#xff0c;本质上还是绘制一条直线&#xff0c;但在渲染上有差别。 通常绘制直线都是给的固定颜色&#xff0c;绘制流星给的是渐变色&#xff0c;渐变色的开头是与背景色对比度明显的亮色&#xff0c;结尾是与背景色相同的暗色&#xff0c;中间渐变过…

Vue---router实现路由跳转

Vue—router实现路由跳转 目录 Vue---router实现路由跳转基本使用路由跳转html实现路由跳转JS实现路由跳转 基本使用 所谓路由&#xff0c;就是将一个个组件映射到不同的路由url中 首先要将App内的内容换成router-view // App.vue <template><div id"app"…

区间预测 | PSO-RF-KDE的粒子群优化随机森林结合核密度估计多变量回归区间预测(Matlab)

区间预测 | PSO-RF-KDE的粒子群优化随机森林结合核密度估计多变量回归区间预测&#xff08;Matlab&#xff09; 目录 区间预测 | PSO-RF-KDE的粒子群优化随机森林结合核密度估计多变量回归区间预测&#xff08;Matlab&#xff09;效果一览基本介绍程序设计参考资料 效果一览 基…

【八股】UML 2的基础结构和上层结构

UML&#xff08;统一建模语言&#xff09;是一种广泛使用的建模语言&#xff0c;用于软件工程中的系统设计。UML 2是UML的一个重要版本&#xff0c;它扩展了原有的功能&#xff0c;提供了更加丰富和灵活的建模能力。UML 2的结构可以分为两部分&#xff1a;基础结构&#xff08;…

C++ //练习 14.8 你在7.5.1节的练习7.40(第261页)中曾经选择并编写了一个类,为它定义一个输出运算符。

C Primer&#xff08;第5版&#xff09; 练习 14.8 练习 14.8 你在7.5.1节的练习7.40&#xff08;第261页&#xff09;中曾经选择并编写了一个类&#xff0c;为它定义一个输出运算符。 环境&#xff1a;Linux Ubuntu&#xff08;云服务器&#xff09; 工具&#xff1a;vim 代…

WPF中Nlog的使用--能够提取Nlog的日志内容,并定义使用

背景 使用Nlog日志进行本地文件输出,以及进行定时清理删除参考其他文章 在用户本地计算机上进行Nlog输出,或者使用Web的Http的Post请求输出到后端数据库使用File或者Web的数据类型就可以了,但是我这里希望把我的日志进行输出到自己的云端,但是存在如下情况: 1、用户在一天…

孩子多大可以学编程,需要具备哪些基础知识?

孩子学习编程的最佳年龄取决于孩子的兴趣和学习能力。一般来说&#xff0c;孩子从5岁开始就可以接触编程教育&#xff0c;但更多的孩子在7到12岁之间开始学习编程。这个年龄段的孩子通常具有较好的逻辑思维能力和抽象思维能力&#xff0c;更容易理解编程的概念。 要学习编程&a…

巧用 TiCDC Syncpiont 构建银行实时交易和准实时计算一体化架构

本文阐述了某商业银行如何利用 TiCDC Syncpoint 功能&#xff0c;在 TiDB 平台上构建一个既能处理实时交易又能进行准实时计算的一体化架构&#xff0c;用以优化其零售资格业务系统的实践。通过迁移到 TiDB 并巧妙应用 Syncpoint&#xff0c;该银行成功解决了原有多个 MySQL 集…