[add] Configuring SUI page caching to optimize static page loading speed
This commit is contained in:
parent
6ff2ad2ead
commit
6dcd704ea4
8 changed files with 172 additions and 45 deletions
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
113
sui/core/cache.go
Normal 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)
|
||||||
|
}
|
||||||
|
|
@ -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) {
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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 {
|
||||||
|
|
|
||||||
|
|
@ -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{}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -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"`
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue