douqian3712 2015-09-15 05:58
浏览 6
已采纳

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

I'm writing a basic server for a website. Now I face a (for me) difficult performance question. Is it better to read the template file in the init() function?

// Initialize all pages of website
func init(){
 indexPageData, err := ioutil.ReadFile("./tpl/index.tpl")
 check(err)
}

Or in the 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")
}

I think in the first example, after the server is started you have no need to access the disk and increase the performance of the request.
But during development I want to refresh the browser and see the new content. That can be done with the second example.

Does someone have a state-of-the-art solution? Or what is the right from the performance point of view?

  • 写回答

2条回答 默认 最新

  • dtqie02844 2015-09-15 06:50
    关注

    Never read and parse template files in the request handler in production, that is as bad as it can get (you should like always avoid this). During development it is ok of course.

    Read this question for more details:

    It takes too much time when using "template" package to generate a dynamic web page to client in golang

    You could approach this in multiple ways. Here I list 4 with example implementation.

    1. With a "dev mode" setting

    You could have a constant or variable telling if you're running in development mode which means templates are not to be cached.

    Here's an example to that:

    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. Specify in the request (as a param) if you want a fresh template

    When you develop, you may specify an extra URL parameter indicating to read a fresh template and not use the cached one, e.g. http://localhost:8080/index?dev=true

    Example implementation:

    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. Decide based on host

    You can also check the host name of the request URL, and if it is "localhost", you can omit the cache and use a fresh template. This requires the smallest extra code and effort. Note that you may want to accept other hosts as well e.g. "127.0.0.1" (up to you what you want to include).

    Example implementation:

    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. Check template file last modified

    You could also store the last modified time of the template file when it is loaded. Whenever the template is requested, you can check the last modified time of the source template file. If it has changed, you can reload it before executing it.

    Example implementation:

    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")
    }
    
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥60 大一项目课,微信小程序
  • ¥15 求视频摘要youtube和ovp数据集
  • ¥15 在启动roslaunch时出现如下问题
  • ¥15 汇编语言实现加减法计算器的功能
  • ¥20 关于多单片机模块化的一些问题
  • ¥30 seata使用出现报错,其他服务找不到seata
  • ¥35 引用csv数据文件(4列1800行),通过高斯-赛德尔法拟合曲线,在选取(每五十点取1点)数据,求该数据点的曲率中心。
  • ¥20 程序只发送0X01,串口助手显示不正确,配置看了没有问题115200-8-1-no,如何解决?
  • ¥15 Google speech command 数据集获取
  • ¥15 vue3+element-plus页面崩溃