完善map缓存,优化部分post缓存

This commit is contained in:
xing 2022-09-26 21:25:41 +08:00
parent 528e8e9348
commit 10efcc9711
8 changed files with 193 additions and 59 deletions

View File

@ -20,18 +20,21 @@ var PostContextCache sync.Map
var archivesCaches *Arch var archivesCaches *Arch
var categoryCaches *cache.SliceCache[models.WpTermsMy] var categoryCaches *cache.SliceCache[models.WpTermsMy]
var recentPostsCaches *cache.SliceCache[models.WpPosts] var recentPostsCaches *cache.SliceCache[models.WpPosts]
var monthCaches *cache.MapCache[string, []models.WpPosts]
var recentCommentsCaches *cache.SliceCache[models.WpComments] var recentCommentsCaches *cache.SliceCache[models.WpComments]
var postCommentCaches *cache.MapCache[uint64, []models.WpComments] var postCommentCaches *cache.MapCache[uint64, []models.WpComments]
var postsCache *cache.MapCache[uint64, models.WpPosts]
func InitCache() { func InitActionsCommonCache() {
archivesCaches = &Arch{ archivesCaches = &Arch{
mutex: &sync.Mutex{}, mutex: &sync.Mutex{},
setCacheFunc: archives, setCacheFunc: archives,
} }
postsCache = cache.NewMapBatchCache[uint64, models.WpPosts](getPosts, time.Hour)
categoryCaches = cache.NewSliceCache[models.WpTermsMy](categories, vars.Conf.CategoryCacheTime) categoryCaches = cache.NewSliceCache[models.WpTermsMy](categories, vars.Conf.CategoryCacheTime)
recentPostsCaches = cache.NewSliceCache[models.WpPosts](recentPosts, vars.Conf.RecentPostCacheTime) recentPostsCaches = cache.NewSliceCache[models.WpPosts](recentPosts, vars.Conf.RecentPostCacheTime)
monthCaches = cache.NewMapCache[string, []models.WpPosts](getMonthPost, 30*time.Minute)
recentCommentsCaches = cache.NewSliceCache[models.WpComments](recentComments, vars.Conf.RecentCommentsCacheTime) recentCommentsCaches = cache.NewSliceCache[models.WpComments](recentComments, vars.Conf.RecentCommentsCacheTime)
postCommentCaches = cache.NewMapCache[uint64, []models.WpComments](postComments, time.Minute*5) postCommentCaches = cache.NewMapCache[uint64, []models.WpComments](postComments, time.Minute*5)
} }
@ -43,7 +46,7 @@ type Arch struct {
month time.Month month time.Month
} }
func (c *Arch) GetCache() []models.PostArchive { func (c *Arch) getArchiveCache() []models.PostArchive {
l := len(c.data) l := len(c.data)
m := time.Now().Month() m := time.Now().Month()
if l > 0 && c.month != m || l < 1 { if l > 0 && c.month != m || l < 1 {
@ -67,10 +70,6 @@ type PostContext struct {
setTime time.Time setTime time.Time
} }
func GetMonthPost(ctx context.Context, year, month string) ([]models.WpPosts, error) {
return monthCaches.GetCache(ctx, fmt.Sprintf("%s%s", year, month), time.Second, year, month)
}
func PostComments(ctx context.Context, Id uint64) ([]models.WpComments, error) { func PostComments(ctx context.Context, Id uint64) ([]models.WpComments, error) {
return postCommentCaches.GetCache(ctx, Id, time.Second, Id) return postCommentCaches.GetCache(ctx, Id, time.Second, Id)
} }
@ -151,14 +150,6 @@ func getPostContext(t time.Time) (prev, next models.WpPosts, err error) {
return return
} }
func GetPostFromCache(Id uint64) (r models.WpPosts) {
p, ok := PostsCache.Load(Id)
if ok {
r = *p.(*models.WpPosts)
}
return
}
func QueryAndSetPostCache(postIds []models.WpPosts) (err error) { func QueryAndSetPostCache(postIds []models.WpPosts) (err error) {
var all []uint64 var all []uint64
var needQuery []any var needQuery []any
@ -223,7 +214,7 @@ func archives() ([]models.PostArchive, error) {
} }
func Archives() (r []models.PostArchive) { func Archives() (r []models.PostArchive) {
return archivesCaches.GetCache() return archivesCaches.getArchiveCache()
} }
func Categories(ctx context.Context) []models.WpTermsMy { func Categories(ctx context.Context) []models.WpTermsMy {

34
actions/common/posts.go Normal file
View File

@ -0,0 +1,34 @@
package common
import (
"context"
"github/fthvgb1/wp-go/helper"
"github/fthvgb1/wp-go/models"
"time"
)
func GetPostById(ctx context.Context, id uint64, ids ...uint64) (models.WpPosts, error) {
return postsCache.GetCacheBatch(ctx, id, time.Second, ids)
}
func getPosts(ids ...any) (m map[uint64]models.WpPosts, err error) {
m = make(map[uint64]models.WpPosts)
id := ids[0].([]uint64)
arg := helper.SliceMap(id, helper.ToAny[uint64])
rawPosts, err := models.Find[models.WpPosts](models.SqlBuilder{{
"Id", "in", "",
}}, "a.*,ifnull(d.name,'') category_name,ifnull(taxonomy,'') `taxonomy`", "", nil, models.SqlBuilder{{
"a", "left join", "wp_term_relationships b", "a.Id=b.object_id",
}, {
"left join", "wp_term_taxonomy c", "b.term_taxonomy_id=c.term_taxonomy_id",
}, {
"left join", "wp_terms d", "c.term_id=d.term_id",
}}, 0, arg)
if err == nil {
for _, v := range rawPosts {
m[v.Id] = v
}
}
return
}

View File

@ -55,18 +55,10 @@ func Detail(c *gin.Context) {
} }
} }
ID := uint64(Id) ID := uint64(Id)
post := common.GetPostFromCache(ID) post, err := common.GetPostById(c, ID, ID)
if post.Id == 0 { if post.Id == 0 || err != nil {
er := common.QueryAndSetPostCache([]models.WpPosts{{Id: ID}})
if er != nil {
err = er
return return
} }
post = common.GetPostFromCache(ID)
if post.Id == 0 {
return
}
}
pw := sessions.Default(c).Get("post_password") pw := sessions.Default(c).Get("post_password")
showComment := false showComment := false
if post.CommentCount > 0 || post.CommentStatus == "open" { if post.CommentCount > 0 || post.CommentStatus == "open" {

130
cache/map.go vendored
View File

@ -9,12 +9,17 @@ import (
) )
type MapCache[K comparable, V any] struct { type MapCache[K comparable, V any] struct {
data map[K]V data map[K]mapCacheStruct[V]
mutex *sync.Mutex mutex *sync.Mutex
setCacheFunc func(...any) (V, error) setCacheFunc func(...any) (V, error)
setBatchCacheFn func(...any) (map[K]V, error)
expireTime time.Duration expireTime time.Duration
}
type mapCacheStruct[T any] struct {
setTime time.Time setTime time.Time
incr int incr int
data T
} }
func NewMapCache[K comparable, V any](fun func(...any) (V, error), expireTime time.Duration) *MapCache[K, V] { func NewMapCache[K comparable, V any](fun func(...any) (V, error), expireTime time.Duration) *MapCache[K, V] {
@ -22,41 +27,79 @@ func NewMapCache[K comparable, V any](fun func(...any) (V, error), expireTime ti
mutex: &sync.Mutex{}, mutex: &sync.Mutex{},
setCacheFunc: fun, setCacheFunc: fun,
expireTime: expireTime, expireTime: expireTime,
data: make(map[K]V), data: make(map[K]mapCacheStruct[V]),
}
}
func NewMapBatchCache[K comparable, V any](fn func(...any) (map[K]V, error), expireTime time.Duration) *MapCache[K, V] {
return &MapCache[K, V]{
mutex: &sync.Mutex{},
setBatchCacheFn: fn,
expireTime: expireTime,
data: make(map[K]mapCacheStruct[V]),
} }
} }
func (c *MapCache[K, V]) FlushCache(k any) { func (m *MapCache[K, V]) FlushCache(k any) {
c.mutex.Lock() m.mutex.Lock()
defer c.mutex.Unlock() defer m.mutex.Unlock()
key := k.(K) key := k.(K)
delete(c.data, key) delete(m.data, key)
} }
func (c *MapCache[K, V]) GetCache(ctx context.Context, key K, timeout time.Duration, params ...any) (V, error) { func (m *MapCache[K, V]) Get(k K) V {
_, ok := c.data[key] return m.data[k].data
}
func (m *MapCache[K, V]) Set(k K, v V) {
m.mutex.Lock()
defer m.mutex.Unlock()
m.set(k, v)
}
func (m *MapCache[K, V]) set(k K, v V) {
data, ok := m.data[k]
t := time.Now()
if !ok {
data.data = v
data.setTime = t
data.incr++
m.data[k] = data
} else {
m.data[k] = mapCacheStruct[V]{
data: v,
setTime: t,
}
}
}
func (m *MapCache[K, V]) GetCache(c context.Context, key K, timeout time.Duration, params ...any) (V, error) {
data, ok := m.data[key]
if !ok {
data = mapCacheStruct[V]{}
}
var err error var err error
expired := time.Duration(c.setTime.Unix())+c.expireTime/time.Second < time.Duration(time.Now().Unix()) expired := time.Duration(data.setTime.Unix())+m.expireTime/time.Second < time.Duration(time.Now().Unix())
//todo 这里应该判断下取出的值是否为零值,不过怎么操作呢? //todo 这里应该判断下取出的值是否为零值,不过怎么操作呢?
if !ok || (c.expireTime >= 0 && expired) { if !ok || (ok && m.expireTime >= 0 && expired) {
t := c.incr t := data.incr
call := func() { call := func() {
c.mutex.Lock() m.mutex.Lock()
defer c.mutex.Unlock() defer m.mutex.Unlock()
if c.incr > t { if data.incr > t {
return return
} }
r, er := c.setCacheFunc(params...) r, er := m.setCacheFunc(params...)
if err != nil { if err != nil {
err = er err = er
return return
} }
c.setTime = time.Now() data.setTime = time.Now()
c.data[key] = r data.data = r
c.incr++ m.data[key] = data
data.incr++
} }
if timeout > 0 { if timeout > 0 {
ctx, cancel := context.WithTimeout(ctx, timeout) ctx, cancel := context.WithTimeout(c, timeout)
defer cancel() defer cancel()
done := make(chan struct{}) done := make(chan struct{})
go func() { go func() {
@ -73,5 +116,52 @@ func (c *MapCache[K, V]) GetCache(ctx context.Context, key K, timeout time.Durat
} }
} }
return c.data[key], err return data.data, err
}
func (m *MapCache[K, V]) GetCacheBatch(c context.Context, key K, timeout time.Duration, params ...any) (V, error) {
data, ok := m.data[key]
if !ok {
data = mapCacheStruct[V]{}
}
var err error
expired := time.Duration(data.setTime.Unix())+m.expireTime/time.Second < time.Duration(time.Now().Unix())
//todo 这里应该判断下取出的值是否为零值,不过怎么操作呢?
if !ok || (ok && m.expireTime >= 0 && expired) {
t := data.incr
call := func() {
m.mutex.Lock()
defer m.mutex.Unlock()
if data.incr > t {
return
}
r, er := m.setBatchCacheFn(params...)
if err != nil {
err = er
return
}
for k, v := range r {
m.set(k, v)
}
data.data = m.data[key].data
}
if timeout > 0 {
ctx, cancel := context.WithTimeout(c, timeout)
defer cancel()
done := make(chan struct{})
go func() {
call()
done <- struct{}{}
}()
select {
case <-ctx.Done():
err = errors.New(fmt.Sprintf("get cache %v %s", key, ctx.Err().Error()))
case <-done:
}
} else {
call()
}
}
return data.data, err
} }

View File

@ -10,6 +10,10 @@ import (
"strings" "strings"
) )
func ToAny[T any](v T) any {
return v
}
func IsContainInArr[T comparable](a T, arr []T) bool { func IsContainInArr[T comparable](a T, arr []T) bool {
for _, v := range arr { for _, v := range arr {
if a == v { if a == v {

View File

@ -449,3 +449,27 @@ func TestSliceReverse(t *testing.T) {
}) })
} }
} }
func TestToInterface(t *testing.T) {
type args struct {
v int
}
tests := []struct {
name string
args args
want any
}{
{
name: "t1",
args: args{v: 1},
want: any(1),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if got := ToAny(tt.args.v); !reflect.DeepEqual(got, tt.want) {
t.Errorf("ToAny() = %v, want %v", got, tt.want)
}
})
}
}

View File

@ -30,8 +30,8 @@ func init() {
panic(err) panic(err)
} }
common.InitCache() common.InitActionsCommonCache()
plugins.InitDigest() plugins.InitDigestCache()
} }
func main() { func main() {

View File

@ -18,8 +18,8 @@ var more = regexp.MustCompile("<!--more(.*?)?-->")
var digestCache *cache.MapCache[uint64, string] var digestCache *cache.MapCache[uint64, string]
var quto = regexp.MustCompile(`&quot; *|&amp; *|&lt; *|&gt; ?|&nbsp; *`) var quto = regexp.MustCompile(`&quot; *|&amp; *|&lt; *|&gt; ?|&nbsp; *`)
func InitDigest() { func InitDigestCache() {
digestCache = cache.NewMapCache[uint64](digestRaw, time.Second) digestCache = cache.NewMapCache[uint64](digestRaw, vars.Conf.DigestCacheTime)
} }
func digestRaw(arg ...any) (string, error) { func digestRaw(arg ...any) (string, error) {
@ -108,6 +108,5 @@ func Digest(p *Plugin[models.WpPosts], c *gin.Context, post *models.WpPosts, sce
if scene == Detail { if scene == Detail {
return return
} }
//post.PostContent = DigestCache(c, post.Id, post.PostContent) post.PostContent = DigestCache(c, post.Id, post.PostContent)
post.PostContent = DigestRaw(post.PostContent, vars.Conf.DigestWordCount, post.Id)
} }