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。如果在执行模板或写入其输出时发生错误,则执行将停止,但部分结果可能已写入输出写入器。模板可以安全地并行执行。
所以,发生的事情是这样的:
您从磁盘读取模板
您将文件解析为模板
您选择的数据填补空白与
您
Execute
使用具有该数据的模板,结果将写入io.Writer
您选择的数据是最新的。这与从磁盘重新读取模板,甚至重新解析它无关。这是模板背后的整个想法:一次磁盘访问,一次解析,多个动态最终结果。
上面引用的文档告诉我们另一件事:
模板可以安全地并行执行。
这非常有用,因为您的http.HandlerFunc
s 是并行运行的,如果您有多个并行请求。
那么,现在该怎么办?Read
模板文件一次,Parse
模板一次,每个请求Execute
的模板。
我不确定您是否应该在init()
函数中读取和解析,因为至少Must
可以恐慌(并且不要在那里使用一些相对的硬编码路径!) - 我会尝试在更受控的环境中这样做,例如,提供一个函数(如New()
)来创建服务器的新实例并在其中执行这些操作。
编辑:我重新阅读了你的问题,我可能误解了你:
如果模板本身仍在开发中,那么是的,您必须在每个请求中阅读它才能获得最新的结果。这比每次更改模板时都重新启动服务器更方便。对于生产,模板应该是固定的,只有数据应该改变。
对不起,如果我在那里弄错了。
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")
}
- 2 回答
- 0 关注
- 158 浏览
添加回答
举报