loo*_*e11 2 performance template-engine http go
我正在为网站编写基本服务器.现在,我面临一个(对我而言)难以解决的表现问题.在init()函数中读取模板文件是否更好?
// Initialize all pages of website
func init(){
indexPageData, err := ioutil.ReadFile("./tpl/index.tpl")
check(err)
}
Run Code Online (Sandbox Code Playgroud)
还是在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")
}
Run Code Online (Sandbox Code Playgroud)
我想在第一个例子中,在服务器启动后,您无需访问磁盘并提高请求的性能.
但在开发过程中,我想刷新浏览器并查看新内容.这可以通过第二个例子来完成.
有人拥有最先进的解决方案吗?或者从性能的角度来看是什么?
让我们分析一下表现:
我们将您的第一个解决方案命名为(稍作修改,见下文)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.如果执行模板或写入其输出时发生错误,则执行停止,但部分结果可能已经写入输出编写器.模板可以安全地并行执行.
那么,会发生什么:
Execute带有那个数据的模板,结果写成了一个io.Writer您的数据与您选择的数据一样是最新的.这与从磁盘重新读取模板,甚至重新解析它无关.这是模板背后的整个想法:一个磁盘访问,一个解析,多个动态结束结果.
上面引用的文档告诉我们另一件事:
模板可以安全地并行执行.
这非常有用,因为http.HandlerFunc如果并行有多个请求,则s并行运行.
那么,现在该怎么办?
Read模板文件一次,
Parse模板一次,每个请求
Execute的模板.
我不确定你是否应该阅读和解析init()函数,因为至少Must可以恐慌(并且不要在那里使用一些相对的硬编码路径!) - 我会尝试在更受控制的环境中这样做,例如,提供一个函数(如New())来创建服务器的新实例并在那里执行这些操作.
编辑:我重新阅读你的问题,我可能误解了你:
如果模板本身仍在开发中,那么是的,您必须在每个请求中读取它以获得最新结果.这比每次更改模板时重新启动服务器更方便.对于生产,模板应该是固定的,只有数据应该改变.
对不起,如果我在那里弄错了.
永远不要在生产中的请求处理程序中读取和解析模板文件,这是最糟糕的(您应该始终避免这种情况)。开发过程中当然没问题。
阅读此问题了解更多详细信息:
在golang中使用“模板”包生成动态网页给客户端需要花费太多时间
您可以通过多种方式解决这个问题。这里我列出了 4 个示例实现。
您可以有一个常量或变量来告诉您是否在开发模式下运行,这意味着模板不会被缓存。
这是一个例子:
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")
}
Run Code Online (Sandbox Code Playgroud)
当你开发时,你可以指定一个额外的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")
}
Run Code Online (Sandbox Code Playgroud)
您还可以检查请求 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")
}
Run Code Online (Sandbox Code Playgroud)
您还可以在加载模板文件时存储模板文件的最后修改时间。每当请求模板时,您都可以检查源模板文件的最后修改时间。如果它发生了变化,您可以在执行之前重新加载它。
实施示例:
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")
}
Run Code Online (Sandbox Code Playgroud)