为了账号安全,请及时绑定邮箱和手机立即绑定

在 init 或处理程序函数中读取模板?

在 init 或处理程序函数中读取模板?

Go
忽然笑 2021-11-08 15:47:10
我正在为一个网站编写一个基本的服务器。现在我面临一个(对我来说)困难的性能问题。读取init()函数中的模板文件是否更好?// Initialize all pages of websitefunc init(){ indexPageData, err := ioutil.ReadFile("./tpl/index.tpl") check(err)}还是在http.HandlerFunc?func index(w http.ResponseWriter, req *http.Request){  indexPageData, err := ioutil.ReadFile("./tpl/index.tpl")  check(err)  indexPageTpl := template.Must(template.New("index").Parse(string(indexPageData)))  indexPageTpl.Execute(w, "test")}我认为在第一个示例中,服务器启动后您无需访问磁盘并提高请求的性能。但是在开发过程中我想刷新浏览器并查看新内容。这可以通过第二个例子来完成。有人有最先进的解决方案吗?或者从性能的角度来看什么是正确的?
查看完整描述

2 回答

?
当年话下

TA贡献1890条经验 获得超9个赞

我们来分析一下性能:

我们将您的第一个解决方案(略有变化,见下文)命名为a和您的第二个解决方案b

一个请求:
a:一个磁盘访问
b:一个磁盘访问

十次请求:
a:一次磁盘访问
b:十次磁盘访问

10 000 000 次请求:
a:一次磁盘访问
b:10 000 000 次磁盘访问(这很慢)

因此,第一个解决方案的性能更好。但是您对最新数据的担忧呢?从文档func (t *Template) Execute(wr io.Writer, data interface{}) error

Execute 将解析的模板应用于指定的数据对象,将输出写入 wr。如果在执行模板或写入其输出时发生错误,则执行将停止,但部分结果可能已写入输出写入器。模板可以安全地并行执行。

所以,发生的事情是这样的:

  1. 您从磁盘读取模板

  2. 您将文件解析为模板

  3. 您选择的数据填补空白

  4. Execute使用具有该数据的模板,结果将写入io.Writer

您选择的数据是最新的。这与从磁盘重新读取模板,甚至重新解析它无关。这是模板背后的整个想法:一次磁盘访问,一次解析,多个动态最终结果。

上面引用的文档告诉我们另一件事:

模板可以安全地并行执行。

这非常有用,因为您的http.HandlerFuncs 是并行运行的,如果您有多个并行请求。

那么,现在该怎么办?
Read模板文件一次
Parse模板一次每个请求
Execute的模板。

我不确定您是否应该在init()函数中读取和解析,因为至少Must可以恐慌(并且不要在那里使用一些相对的硬编码路径!) - 我会尝试在更受控的环境中这样做,例如,提供一个函数(如New())来创建服务器的新实例并在其中执行这些操作。

编辑:我重新阅读了你的问题,我可能误解了你:

如果模板本身仍在开发中,那么是的,您必须在每个请求中阅读它才能获得最新的结果。这比每次更改模板时都重新启动服务器更方便。对于生产,模板应该是固定的,只有数据应该改变。

对不起,如果我在那里弄错了。


查看完整回答
反对 回复 2021-11-08
?
蛊毒传说

TA贡献1895条经验 获得超3个赞

永远不要在生产中的请求处理程序中读取和解析模板文件,这是最糟糕的(你应该总是避免这种情况)。在开发过程中当然是可以的。


阅读此问题了解更多详情:


golang中使用“模板”包生成动态网页给客户端的时间太长了


你可以通过多种方式来解决这个问题。在这里,我列出了 4 个示例实现。


1.带有“开发模式”设置

如果您在开发模式下运行,您可以有一个常量或变量告诉您,这意味着模板不会被缓存。


这是一个例子:


const dev = true


var indexTmpl *template.Template


func init() {

    if !dev { // Prod mode, read and cache template

        indexTmpl = template.Must(template.New("index").ParseFiles(".tpl/index.tpl"))

    }

}


func getIndexTmpl() *template.Template {

    if dev { // Dev mode, always read fresh template

        return template.Must(template.New("index").ParseFiles(".tpl/index.tpl"))

    } else { // Prod mode, return cached template

        return indexTmpl

    }

}


func indexHandler(w http.ResponseWriter, r *http.Request) {

    getIndexTmpl().Execute(w, "test")

}

2. 在请求中指定(作为参数)是否需要新模板

开发时,您可以指定一个额外的 URL 参数,指示读取新模板而不使用缓存的模板,例如 http://localhost:8080/index?dev=true


示例实现:


var indexTmpl *template.Template


func init() {

    indexTmpl = getIndexTmpl()

}


func getIndexTmpl() *template.Template {

    return template.Must(template.New("index").ParseFiles(".tpl/index.tpl"))

}


func indexHandler(w http.ResponseWriter, r *http.Request) {

    t := indexTmpl

    if r.FormValue("dev") != nil {

        t = getIndexTmpl()

    }

    t.Execute(w, "test")

}

3.根据主机决定

您还可以检查请求 URL 的主机名,如果是"localhost",则可以省略缓存并使用新模板。这需要最少的额外代码和努力。请注意,您可能还想接受其他主机,例如"127.0.0.1"(取决于您想要包含的内容)。


示例实现:


var indexTmpl *template.Template


func init() {

    indexTmpl = getIndexTmpl()

}


func getIndexTmpl() *template.Template {

    return template.Must(template.New("index").ParseFiles(".tpl/index.tpl"))

}


func indexHandler(w http.ResponseWriter, r *http.Request) {

    t := indexTmpl

    if r.URL.Host == "localhost" || strings.HasPrefix(r.URL.Host, "localhost:") {

        t = getIndexTmpl()

    }

    t.Execute(w, "test")

}

4.检查模板文件最后修改

您还可以存储模板文件加载时的最后修改时间。每当请求模板时,您可以检查源模板文件的最后修改时间。如果它已更改,您可以在执行之前重新加载它。


示例实现:


type mytempl struct {

    t       *template.Template

    lastmod time.Time

    mutex   sync.Mutex

}


var indexTmpl mytempl


func init() {

    // You may want to call this in init so first request won't be slow

    checkIndexTempl()

}


func checkIndexTempl() {

    nm := ".tpl/index.tpl"

    fi, err := os.Stat(nm)

    if err != nil {

        panic(err)

    }

    if indexTmpl.lastmod != fi.ModTime() {

        // Changed, reload. Don't forget the locking!

        indexTmpl.mutex.Lock()

        defer indexTmpl.mutex.Unlock()

        indexTmpl.t = template.Must(template.New("index").ParseFiles(nm))

        indexTmpl.lastmod = fi.ModTime()

    }

}


func indexHandler(w http.ResponseWriter, r *http.Request) {

    checkIndexTempl()

    indexTmpl.t.Execute(w, "test")

}


查看完整回答
反对 回复 2021-11-08
  • 2 回答
  • 0 关注
  • 158 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信