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条)

报告相同问题?

悬赏问题

  • ¥20 usb设备兼容性问题
  • ¥15 错误(10048): “调用exui内部功能”库命令的参数“参数4”不能接受空数据。怎么解决啊
  • ¥15 安装svn网络有问题怎么办
  • ¥15 Python爬取指定微博话题下的内容,保存为txt
  • ¥15 vue2登录调用后端接口如何实现
  • ¥65 永磁型步进电机PID算法
  • ¥15 sqlite 附加(attach database)加密数据库时,返回26是什么原因呢?
  • ¥88 找成都本地经验丰富懂小程序开发的技术大咖
  • ¥15 如何处理复杂数据表格的除法运算
  • ¥15 如何用stc8h1k08的片子做485数据透传的功能?(关键词-串口)