[add] Configuring SUI page caching to optimize static page loading speed

This commit is contained in:
Max 2024-06-27 20:02:03 +08:00
parent 6ff2ad2ead
commit 6dcd704ea4
8 changed files with 172 additions and 45 deletions

View file

@ -55,11 +55,11 @@ func withStaticFileServer(c *gin.Context) {
for _, rewrite := range rewriteRules { for _, rewrite := range rewriteRules {
// log.Debug("Rewrite: %s => %s", c.Request.URL.Path, rewrite.Replacement) // log.Debug("Rewrite: %s => %s", c.Request.URL.Path, rewrite.Replacement)
if matches := rewrite.Pattern.FindStringSubmatch(c.Request.URL.Path); matches != nil { if matches := rewrite.Pattern.FindStringSubmatch(c.Request.URL.Path); matches != nil {
rewriteOriginalPath := c.Request.URL.Path
c.Set("rewrite", true) c.Set("rewrite", true)
c.Set("matches", matches) c.Set("matches", matches)
c.Request.URL.Path = rewrite.Pattern.ReplaceAllString(c.Request.URL.Path, rewrite.Replacement) c.Request.URL.Path = rewrite.Pattern.ReplaceAllString(c.Request.URL.Path, rewrite.Replacement)
log.Trace("Rewrite FindStringSubmatch Matched: %s => %s", rewriteOriginalPath, rewrite.Replacement) // rewriteOriginalPath := c.Request.URL.Path
// log.Trace("Rewrite FindStringSubmatch Matched: %s => %s", rewriteOriginalPath, rewrite.Replacement)
break break
} }
} }

View file

@ -7,6 +7,7 @@ import (
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
"time"
"github.com/fatih/color" "github.com/fatih/color"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -87,8 +88,8 @@ func (r *Request) Render() (string, int, error) {
// The page is not cached // The page is not cached
message := fmt.Sprintf("[SUI] The page %s is not cached. file=%s DisableCache=%v", r.Request.URL.Path, r.File, r.Request.DisableCache()) message := fmt.Sprintf("[SUI] The page %s is not cached. file=%s DisableCache=%v", r.Request.URL.Path, r.File, r.Request.DisableCache())
fmt.Println(color.YellowString(message)) go fmt.Println(color.YellowString(message))
log.Warn(message) go log.Warn(message)
// Read the file // Read the file
content, err := application.App.Read(r.File) content, err := application.App.Read(r.File)
@ -104,6 +105,9 @@ func (r *Request) Render() (string, int, error) {
guard := "" guard := ""
guardRedirect := "" guardRedirect := ""
configText := "" configText := ""
cacheStore := ""
cacheTime := 0
configSel := doc.Find("script[name=config]") configSel := doc.Find("script[name=config]")
if configSel != nil && configSel.Length() > 0 { if configSel != nil && configSel.Length() > 0 {
configText = configSel.Text() configText = configSel.Text()
@ -124,6 +128,10 @@ func (r *Request) Render() (string, int, error) {
guard = parts[0] guard = parts[0]
guardRedirect = parts[1] guardRedirect = parts[1]
} }
// Cache store
cacheStore = conf.CacheStore
cacheTime = conf.Cache
} }
dataText := "" dataText := ""
@ -153,9 +161,11 @@ func (r *Request) Render() (string, int, error) {
Guard: guard, Guard: guard,
GuardRedirect: guardRedirect, GuardRedirect: guardRedirect,
Config: configText, Config: configText,
CacheStore: cacheStore,
CacheTime: time.Duration(cacheTime) * time.Second,
} }
core.SetCache(r.File, c) go core.SetCache(r.File, c)
log.Trace("[SUI] The page %s is cached file=%s", r.Request.URL.Path, r.File) go log.Trace("[SUI] The page %s is cached file=%s", r.Request.URL.Path, r.File)
} }
// Guard the page // Guard the page
@ -180,6 +190,16 @@ func (r *Request) Render() (string, int, error) {
data["$global"] = global data["$global"] = global
} }
// Read from cache directly
key := fmt.Sprintf("page:%s:%s", r.Hash(), data.Hash())
if !r.Request.DisableCache() && c.CacheTime > 0 && c.CacheStore != "" {
html, exists := c.GetHTML(key)
if exists {
log.Trace("[SUI] The page %s is cached %v file=%s", r.Request.URL.Path, c.CacheTime, r.File)
return html, 200, nil
}
}
// Set the page request data // Set the page request data
option := core.ParserOption{ option := core.ParserOption{
Theme: r.Request.Theme, Theme: r.Request.Theme,
@ -195,6 +215,11 @@ func (r *Request) Render() (string, int, error) {
return "", 500, fmt.Errorf("render error, please re-complie the page %s", err.Error()) return "", 500, fmt.Errorf("render error, please re-complie the page %s", err.Error())
} }
// Save to The Cache
if c.CacheTime > 0 && c.CacheStore != "" {
go c.SetHTML(key, html, c.CacheTime)
}
return html, 200, nil return html, 200, nil
} }

113
sui/core/cache.go Normal file
View file

@ -0,0 +1,113 @@
package core
import (
"time"
"github.com/yaoapp/gou/store"
"github.com/yaoapp/kun/log"
)
// Cache the cache
type Cache struct {
Data string
Global string
Config string
Guard string
GuardRedirect string
HTML string
CacheStore string
CacheTime time.Duration
}
const (
saveCache uint8 = iota
removeCache
)
type cacheData struct {
file string
cache *Cache
cmd uint8
}
// Caches the caches
var Caches = map[string]*Cache{}
var ch = make(chan *cacheData, 1)
func init() {
go cacheWriter()
}
func cacheWriter() {
for {
select {
case data := <-ch:
switch data.cmd {
case saveCache:
Caches[data.file] = data.cache
case removeCache:
delete(Caches, data.file)
}
}
}
}
// SetCache set the cache
func SetCache(file string, cache *Cache) {
ch <- &cacheData{file, cache, saveCache}
}
// GetCache get the cache
func GetCache(file string) *Cache {
if cache, has := Caches[file]; has {
return cache
}
return nil
}
// RemoveCache remove the cache
func RemoveCache(file string) {
ch <- &cacheData{file, nil, removeCache}
}
// CleanCache clean the cache
func CleanCache() {
Caches = map[string]*Cache{}
}
// GetHTML get the html
func (c *Cache) GetHTML(hash string) (string, bool) {
store, has := store.Pools[c.CacheStore]
if !has {
log.Warn(`[SUI] The cache store "%s" is not found`, c.CacheStore)
return "", false
}
v, has := store.Get(hash)
if !has {
return "", false
}
return v.(string), true
}
// SetHTML set the html
func (c *Cache) SetHTML(hash, html string, ttl time.Duration) {
store, has := store.Pools[c.CacheStore]
if !has {
log.Warn(`[SUI] The cache store "%s" is not found`, c.CacheStore)
return
}
store.Set(hash, html, ttl)
}
// DelHTML del the html
func (c *Cache) DelHTML(hash string) {
store, has := store.Pools[c.CacheStore]
if !has {
log.Warn(`[SUI] The cache store "%s" is not found`, c.CacheStore)
return
}
store.Del(hash)
}

View file

@ -2,6 +2,7 @@ package core
import ( import (
"fmt" "fmt"
"hash/fnv"
"regexp" "regexp"
"strings" "strings"
@ -23,6 +24,13 @@ var options = []expr.Option{
expr.AllowUndefinedVariables(), expr.AllowUndefinedVariables(),
} }
// Hash get the hash of the data
func (data Data) Hash() string {
h := fnv.New64a()
h.Write([]byte(fmt.Sprintf("%v", data)))
return fmt.Sprintf("%x", h.Sum64())
}
// New create a new expression // New create a new expression
func (data Data) New(stmt string) (*vm.Program, error) { func (data Data) New(stmt string) (*vm.Program, error) {

View file

@ -60,12 +60,14 @@ func (page *Page) GetConfig() *PageConfig {
// ExportConfig export the config // ExportConfig export the config
func (page *Page) ExportConfig() string { func (page *Page) ExportConfig() string {
if page.Config == nil { if page.Config == nil {
return "" return fmt.Sprintf(`{"cache_store": "%s"}`, page.CacheStore)
} }
config, err := jsoniter.MarshalToString(map[string]interface{}{ config, err := jsoniter.MarshalToString(map[string]interface{}{
"title": page.Config.Title, "title": page.Config.Title,
"guard": page.Config.Guard, "guard": page.Config.Guard,
"cache_store": page.CacheStore,
"cache": page.Config.Cache,
}) })
if err != nil { if err != nil {

View file

@ -2,6 +2,7 @@ package core
import ( import (
"fmt" "fmt"
"hash/fnv"
"strings" "strings"
jsoniter "github.com/json-iterator/go" jsoniter "github.com/json-iterator/go"
@ -10,19 +11,6 @@ import (
"github.com/yaoapp/kun/log" "github.com/yaoapp/kun/log"
) )
// Cache the cache
type Cache struct {
Data string
Global string
Config string
Guard string
GuardRedirect string
HTML string
}
// Caches the caches
var Caches = map[string]*Cache{}
// NewRequestMock is the constructor for Request. // NewRequestMock is the constructor for Request.
func NewRequestMock(mock *PageMock) *Request { func NewRequestMock(mock *PageMock) *Request {
if mock == nil { if mock == nil {
@ -107,6 +95,13 @@ func GetTheme(cookies map[string]string) interface{} {
return nil return nil
} }
// Hash get the hash
func (r *Request) Hash() string {
h := fnv.New64a()
h.Write([]byte(fmt.Sprintf("%v", r)))
return fmt.Sprintf("%x", h.Sum64())
}
// ExecStringMerge exec the string and merge the data // ExecStringMerge exec the string and merge the data
func (r *Request) ExecStringMerge(data Data, raw string) error { func (r *Request) ExecStringMerge(data Data, raw string) error {
@ -380,26 +375,3 @@ func (url ReqeustURL) Map() Data {
"path": url.Path, "path": url.Path,
} }
} }
// SetCache set the cache
func SetCache(file string, cache *Cache) {
Caches[file] = cache
}
// GetCache get the cache
func GetCache(file string) *Cache {
if cache, has := Caches[file]; has {
return cache
}
return nil
}
// RemoveCache remove the cache
func RemoveCache(file string) {
delete(Caches, file)
}
// CleanCache clean the cache
func CleanCache() {
Caches = map[string]*Cache{}
}

View file

@ -12,6 +12,7 @@ type DSL struct {
Guard string `json:"guard,omitempty"` Guard string `json:"guard,omitempty"`
Storage *Storage `json:"storage,omitempty"` Storage *Storage `json:"storage,omitempty"`
Public *Public `json:"public,omitempty"` Public *Public `json:"public,omitempty"`
CacheStore string `json:"cache_store,omitempty"` // The cache store
Sid string `json:"-"` Sid string `json:"-"`
publicRoot string `json:"-"` publicRoot string `json:"-"`
} }
@ -27,6 +28,7 @@ type Setting struct {
type Page struct { type Page struct {
Route string `json:"route"` Route string `json:"route"`
Name string `json:"name,omitempty"` Name string `json:"name,omitempty"`
CacheStore string `json:"-"`
TemplateID string `json:"-"` TemplateID string `json:"-"`
SuiID string `json:"-"` SuiID string `json:"-"`
Config *PageConfig `json:"-"` Config *PageConfig `json:"-"`
@ -294,6 +296,8 @@ type PageConfig struct {
type PageSetting struct { type PageSetting struct {
Title string `json:"title,omitempty"` Title string `json:"title,omitempty"`
Guard string `json:"guard,omitempty"` Guard string `json:"guard,omitempty"`
CacheStore string `json:"cache_store,omitempty"`
Cache int `json:"cache,omitempty"`
Description string `json:"description,omitempty"` Description string `json:"description,omitempty"`
SEO *PageSEO `json:"seo,omitempty"` SEO *PageSEO `json:"seo,omitempty"`
} }

View file

@ -447,6 +447,9 @@ func (page *Page) Load() error {
page.Codes.CONF.Code = string(confCode) page.Codes.CONF.Code = string(confCode)
} }
// Set the page CacheStore
page.CacheStore = page.tmpl.local.DSL.CacheStore
// Set the page document // Set the page document
page.Document = page.tmpl.Document page.Document = page.tmpl.Document