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

报告相同问题?

悬赏问题

  • ¥15 metadata提取的PDF元数据,如何转换为一个Excel
  • ¥15 关于arduino编程toCharArray()函数的使用
  • ¥100 vc++混合CEF采用CLR方式编译报错
  • ¥15 coze 的插件输入飞书多维表格 app_token 后一直显示错误,如何解决?
  • ¥15 vite+vue3+plyr播放本地public文件夹下视频无法加载
  • ¥15 c#逐行读取txt文本,但是每一行里面数据之间空格数量不同
  • ¥50 如何openEuler 22.03上安装配置drbd
  • ¥20 ING91680C BLE5.3 芯片怎么实现串口收发数据
  • ¥15 无线连接树莓派,无法执行update,如何解决?(相关搜索:软件下载)
  • ¥15 Windows11, backspace, enter, space键失灵