在GoLang开发中,当项目运行时,go的html/template默认行为是每次请求都得重新解析模板文件,当高并发,频繁的磁盘读取会造成非常大的负担,成为明显瓶颈,所以,为了避免重复解析模板文件,减少磁盘I/O和CPU开销,模板缓存系统诞生。

        以下是非常简便的模板缓存系统的实现,大家可以根据这个简便的例子来理解这个系统。

文件准备

模板页面        base.page.html

{{define "base"}}
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Document</title>
    </head>
    <body>
        {{block "content" .}}
        {{end}}
    </body>
    </html>
{{end}}

目标页面        home.page.html

{{template "base" .}}

{{define "content"}}
    <div class="container">
        <div class="row">
            <div class="col">
                <h1>This is the Home Page</h1>
                <p>This is some text</p>
            </div>
        </div>
    </div>
{{end}}

我们这里准备了一个模板页面和一个目标文件,可以看到目标文件是以模板文件来设计布局的。

然后我们可以开始设计模板缓存系统了。

首先,我们先make出一个map,key键值存目标页面的名字,value值存模板的相关信息

var tc = make(map[string]*template.Template)

然后,每次刷新页面后,我们先到这个map中查询是否存在这个名字的页面模板,如果不存在,我们需要从磁盘读取这个页面文件(第一次从磁盘中加载),并将这个模板存进这个map中,实现缓存的效果;如果读取到了,那就直接从缓存读取这个模板,页面被快速地刷新读取出来,比从磁盘读取的速度快很多。

func RenderTemplate(w http.ResponseWriter, t string) {
	var tmpl *template.Template
	var err error
	_, inMap := tc[t]
	if !inMap {
		//需要将这个模板放进tc切片中
		err = CreateTemplateCache(t)
		if err != nil {
			log.Println(err)
		}
		log.Println("模板未使用过,正在创建模板缓存...")
	} else {
		log.Println("正在使用模板缓存...")
	}

	tmpl = tc[t]
	err = tmpl.Execute(w, nil)
}

接着,我们再创建一个创建模板缓存的函数,也就是将模板存入map中。

这段代码实现了一个模板缓存的创建函数,接收模板名称参数后,自动拼接该模板文件路径和基础布局模板路径,调用Go标准库的 template.ParseFiles 一次性解析这两个文件,将解析后的模板对象存入全局缓存映射 tc 中,若解析过程中遇到文件不存在或模板语法错误则立即返回错误,否则成功缓存供后续请求直接复用,避免了每次HTTP请求都重复读取磁盘和解析模板的开销。

func CreateTemplateCache(t string) error {
	templates := []string{
		fmt.Sprintf("./template/%s", t),
		"./template/base.layout.html",
	}

	tmpl, err := template.ParseFiles(templates...)
	if err != nil {
		return err
	}
	tc[t] = tmpl
	return nil
}

最后我们查看实现的效果,可以看到第一次是从磁盘读取的,后面刷新了几次,就从缓存中直接读取了。

到这里,一个非常简便的模板缓存系统就完成了。

更多推荐