实现文档缓存

pull/219/merge
Minho 2018-02-27 17:20:42 +08:00
parent 405a9c309f
commit 849058ff8a
6 changed files with 323 additions and 38 deletions

46
cache/cache.go vendored 100644
View File

@ -0,0 +1,46 @@
package cache
import (
"github.com/astaxie/beego/cache"
"time"
)
var bm cache.Cache
func Get(key string) interface{} {
return bm.Get(key)
}
func GetMulti(keys []string) []interface{} {
return bm.GetMulti(keys)
}
func Put(key string, val interface{}, timeout time.Duration) error {
return bm.Put(key, val, timeout)
}
func Delete(key string) error {
return bm.Delete(key)
}
func Incr(key string) error {
return bm.Incr(key)
}
func Decr(key string) error {
return bm.Decr(key)
}
func IsExist(key string) bool {
return bm.IsExist(key)
}
func ClearAll() error{
return bm.ClearAll()
}
func StartAndGC(config string) error {
return bm.StartAndGC(config)
}
//初始化缓存
func Init(c cache.Cache) {
bm = c
}

39
cache/cache_null.go vendored 100644
View File

@ -0,0 +1,39 @@
package cache
import "time"
type NullCache struct {
}
func (bm *NullCache)Get(key string) interface{} {
return nil
}
func (bm *NullCache)GetMulti(keys []string) []interface{} {
return nil
}
func (bm *NullCache)Put(key string, val interface{}, timeout time.Duration) error {
return nil
}
func (bm *NullCache)Delete(key string) error {
return nil
}
func (bm *NullCache)Incr(key string) error {
return nil
}
func (bm *NullCache)Decr(key string) error {
return nil
}
func (bm *NullCache)IsExist(key string) bool {
return false
}
func (bm *NullCache)ClearAll() error{
return nil
}
func (bm *NullCache)StartAndGC(config string) error {
return nil
}

View File

@ -20,10 +20,15 @@ import (
"github.com/lifei6671/mindoc/conf"
"github.com/lifei6671/mindoc/models"
"github.com/lifei6671/mindoc/utils"
"github.com/lifei6671/mindoc/cache"
beegoCache "github.com/astaxie/beego/cache"
_ "github.com/astaxie/beego/cache/memcache"
_ "github.com/astaxie/beego/cache/redis"
)
// RegisterDataBase 注册数据库
func RegisterDataBase() {
beego.Info("正在初始化数据库配置.")
adapter := beego.AppConfig.String("db_adapter")
if adapter == "mysql" {
@ -37,13 +42,18 @@ func RegisterDataBase() {
dataSource := fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4&parseTime=true&loc=%s", username, password, host, port, database, url.QueryEscape(timezone))
orm.RegisterDataBase("default", "mysql", dataSource)
err := orm.RegisterDataBase("default", "mysql", dataSource)
if err != nil {
beego.Error("注册默认数据库失败:",err)
os.Exit(1)
}
location, err := time.LoadLocation(timezone)
if err == nil {
orm.DefaultTimeLoc = location
} else {
log.Fatalln(err)
beego.Error("加载时区配置信息失败,请检查是否存在ZONEINFO环境变量:",err)
os.Exit(1)
}
} else if adapter == "sqlite3" {
database := beego.AppConfig.String("db_database")
@ -54,8 +64,17 @@ func RegisterDataBase() {
dbPath := filepath.Dir(database)
os.MkdirAll(dbPath, 0777)
orm.RegisterDataBase("default", "sqlite3", database)
err := orm.RegisterDataBase("default", "sqlite3", database)
if err != nil {
beego.Error("注册默认数据库失败:",err)
os.Exit(1)
}
}else{
beego.Error("不支持的数据库类型.")
os.Exit(1)
}
beego.Info("数据库初始化完成.")
}
// RegisterModel 注册Model
@ -120,7 +139,7 @@ func RegisterCommand() {
migrate.RunMigration()
}
}
//注册模板函数
func RegisterFunction() {
beego.AddFuncMap("config", models.GetOptionValue)
@ -179,6 +198,7 @@ func RegisterFunction() {
})
}
//解析命令
func ResolveCommand(args []string) {
flagSet := flag.NewFlagSet("MinDoc command: ", flag.ExitOnError)
flagSet.StringVar(&conf.ConfigurationFile, "config", "", "MinDoc configuration file.")
@ -226,10 +246,95 @@ func ResolveCommand(args []string) {
gocaptcha.ReadFonts(filepath.Join(conf.WorkingDirectory, "static", "fonts"), ".ttf")
RegisterDataBase()
RegisterCache()
RegisterModel()
RegisterLogger(conf.LogFile)
}
//注册缓存管道
func RegisterCache() {
isOpenCache := beego.AppConfig.DefaultBool("cache",false)
if !isOpenCache {
cache.Init(&cache.NullCache{})
}
beego.Info("正常初始化缓存配置.")
cacheProvider := beego.AppConfig.String("cache_provider")
if cacheProvider == "file" {
cacheFilePath := beego.AppConfig.DefaultString("cache_file_path","./runtime/")
if strings.HasPrefix(cacheFilePath, "./") {
cacheFilePath = filepath.Join(conf.WorkingDirectory, string(cacheFilePath[1:]))
}
fileCache := beegoCache.NewFileCache()
beegoCache.FileCachePath = cacheFilePath
beegoCache.FileCacheDirectoryLevel = beego.AppConfig.DefaultInt("cache_file_dir_level",2)
beegoCache.FileCacheEmbedExpiry = time.Duration(beego.AppConfig.DefaultInt64("cache_file_expiry",120))
beegoCache.FileCacheFileSuffix = beego.AppConfig.DefaultString("cache_file_suffix",".bin")
fileCache.StartAndGC("")
cache.Init(fileCache)
}else if cacheProvider == "memory" {
cacheInterval := beego.AppConfig.DefaultInt("cache_memory_interval",60)
memory := beegoCache.NewMemoryCache()
beegoCache.DefaultEvery = cacheInterval
cache.Init(memory)
}else if cacheProvider == "redis"{
var redisConfig struct{
Conn string `json:"conn"`
Password string `json:"password"`
DbNum int `json:"dbNum"`
}
redisConfig.DbNum = 0
redisConfig.Conn = beego.AppConfig.DefaultString("cache_redis_host","")
if pwd := beego.AppConfig.DefaultString("cache_redis_password","");pwd != "" {
redisConfig.Password = pwd
}
if dbNum := beego.AppConfig.DefaultInt("cache_redis_db",0); dbNum > 0 {
redisConfig.DbNum = dbNum
}
bc,err := json.Marshal(&redisConfig)
if err != nil {
beego.Error("初始化Redis缓存失败:",err)
os.Exit(1)
}
beego.Info(string(bc))
redisCache,err := beegoCache.NewCache("redis",string(bc))
if err != nil {
beego.Error("初始化Redis缓存失败:",err)
os.Exit(1)
}
cache.Init(redisCache)
}else if cacheProvider == "memcache" {
var memcacheConfig struct{
Conn string `json:"conn"`
}
memcacheConfig.Conn = beego.AppConfig.DefaultString("cache_memcache_host","")
bc,err := json.Marshal(&memcacheConfig)
if err != nil {
beego.Error("初始化Redis缓存失败:",err)
os.Exit(1)
}
memcache,err := beegoCache.NewCache("memcache",string(bc))
if err != nil {
beego.Error("初始化Memcache缓存失败:",err)
os.Exit(1)
}
cache.Init(memcache)
}else {
cache.Init(&cache.NullCache{})
beego.Warn("不支持的缓存管道,缓存将禁用.")
return
}
beego.Info("缓存初始化完成.")
}
func init() {
if configPath ,err := filepath.Abs(conf.ConfigurationFile); err == nil {

View File

@ -105,7 +105,29 @@ ldap_user_role=2
ldap_filter=objectClass=posixAccount
######################缓存配置###############################
#是否开启缓存true 开启/false 不开启
cache=false
#缓存方式:memory/memcache/redis/file
cache_provider=memory
#当配置缓存方式为memory时,内存回收时间,单位是秒
cache_memory_interval=120
#当缓存方式配置为file时,缓存的储存目录
cache_file_path=./runtime/
#缓存文件后缀
cache_file_suffix=
#文件缓存目录层级
cache_file_dir_level=2
#文件缓存的默认过期时间
cache_file_expiry=3600
#memcache缓存服务器地址
cache_memcache_host=127.0.0.1:11211
#redis服务器地址
cache_redis_host=127.0.0.1:6379
#redis数据库索引
cache_redis_db=0
#redis服务器密码
cache_redis_password=

View File

@ -834,23 +834,23 @@ func (c *DocumentController) Content() {
c.JsonResult(0, "ok", doc)
}
func (c *DocumentController) GetDocumentById(id string) (doc *models.Document, err error) {
doc = models.NewDocument()
if doc_id, err := strconv.Atoi(id); err == nil {
doc, err = doc.Find(doc_id)
if err != nil {
return nil, err
}
} else {
doc, err = doc.FindByFieldFirst("identify", id)
if err != nil {
return nil, err
}
}
return doc, nil
}
//
//func (c *DocumentController) GetDocumentById(id string) (doc *models.Document, err error) {
// doc = models.NewDocument()
// if doc_id, err := strconv.Atoi(id); err == nil {
// doc, err = doc.Find(doc_id)
// if err != nil {
// return nil, err
// }
// } else {
// doc, err = doc.FindByFieldFirst("identify", id)
// if err != nil {
// return nil, err
// }
// }
//
// return doc, nil
//}
// 导出
func (c *DocumentController) Export() {

View File

@ -2,7 +2,6 @@ package models
import (
"time"
"bytes"
"fmt"
"github.com/astaxie/beego"
@ -13,6 +12,9 @@ import (
"path/filepath"
"strconv"
"github.com/PuerkitoBio/goquery"
"github.com/lifei6671/mindoc/cache"
"encoding/json"
"qiniupkg.com/x/errors.v7"
)
// Document struct.
@ -63,6 +65,9 @@ func (m *Document) Find(id int) (*Document, error) {
if id <= 0 {
return m, ErrInvalidParameter
}
if m,err := m.FromCacheById(id); err == nil {
return m,nil
}
o := orm.NewOrm()
err := o.QueryTable(m.TableNameWithPrefix()).Filter("document_id", id).One(m)
@ -70,55 +75,85 @@ func (m *Document) Find(id int) (*Document, error) {
if err == orm.ErrNoRows {
return m, ErrDataNotExist
}
m.PutToCache()
return m, nil
}
//插入和更新文档.
func (m *Document) InsertOrUpdate(cols ...string) error {
o := orm.NewOrm()
var err error
if m.DocumentId > 0 {
_, err := o.Update(m)
return err
_, err = o.Update(m)
} else {
_, err := o.Insert(m)
_, err = o.Insert(m)
NewBook().ResetDocumentNumber(m.BookId)
}
if err != nil {
return err
}
m.PutToCache()
return nil
}
//根据指定字段查询一条文档.
func (m *Document) FindByFieldFirst(field string, v interface{}) (*Document, error) {
if field == "identify" {
if identify,ok := v.(string);ok {
if m,err := m.FromCacheByIdentify(identify);err == nil{
return m,nil
}
}
}else if field == "document_id" {
if id,ok := v.(int); ok && id > 0 {
if m,err := m.FromCacheById(id);err == nil{
return m,nil
}
}
}
o := orm.NewOrm()
err := o.QueryTable(m.TableNameWithPrefix()).Filter(field, v).One(m)
if err == nil {
m.PutToCache()
}
return m, err
}
//递归删除一个文档.
func (m *Document) RecursiveDocument(doc_id int) error {
func (m *Document) RecursiveDocument(docId int) error {
o := orm.NewOrm()
if doc, err := m.Find(doc_id); err == nil {
if doc, err := m.Find(docId); err == nil {
o.Delete(doc)
NewDocumentHistory().Clear(doc.DocumentId)
}
//
//var docs []*Document
//
//_, err := o.QueryTable(m.TableNameWithPrefix()).Filter("parent_id", doc_id).All(&docs)
var docs []*Document
_, err := o.QueryTable(m.TableNameWithPrefix()).Filter("parent_id", doc_id).All(&docs)
var maps []orm.Params
_, err := o.Raw("SELECT document_id FROM " + m.TableNameWithPrefix() + " WHERE parent_id=" + strconv.Itoa(docId)).Values(&maps)
if err != nil {
beego.Error("RecursiveDocument => ", err)
return err
}
for _, item := range docs {
doc_id := item.DocumentId
o.QueryTable(m.TableNameWithPrefix()).Filter("document_id", doc_id).Delete()
m.RecursiveDocument(doc_id)
for _, item := range maps {
if docId,ok := item["document_id"].(string); ok{
id,_ := strconv.Atoi(docId)
o.QueryTable(m.TableNameWithPrefix()).Filter("document_id", id).Delete()
m.RecursiveDocument(id)
}
}
return nil
@ -178,16 +213,54 @@ func (m *Document) ReleaseContent(bookId int) {
if err != nil {
beego.Error(fmt.Sprintf("发布失败 => %+v", item), err)
}else {
//当文档发布后,需要清除已缓存的转换文档和文档缓存
item.PutToCache()
os.RemoveAll(filepath.Join(conf.WorkingDirectory,"uploads","books",strconv.Itoa(bookId)))
}
}
}
//将文档写入缓存
func (m *Document) PutToCache(){
if v,err := json.Marshal(m);err == nil {
if m.Identify == "" {
if err := cache.Put("Document.Id."+strconv.Itoa(m.DocumentId), v, time.Second*3600); err != nil {
beego.Info("文档缓存失败:", m)
}
}else{
if err := cache.Put("Document.Identify."+ m.Identify, v, time.Second*3600); err != nil {
beego.Info("文档缓存失败:", m)
}
}
}
}
//从缓存获取
func (m *Document) FromCacheById(id int) (*Document,error) {
b := cache.Get("Document.Id." + strconv.Itoa(id))
if v,ok := b.([]byte); ok {
beego.Info("从缓存中获取文档信息成功")
if err := json.Unmarshal(v,m);err == nil{
return m,nil
}
}
return nil,errors.New("Cache not exists")
}
func (m *Document) FromCacheByIdentify(identify string) (*Document,error) {
b := cache.Get("Document.Identify." + identify)
if v,ok := b.([]byte); ok {
beego.Info("从缓存中获取文档信息成功")
if err := json.Unmarshal(v,m);err == nil{
return m,nil
}
}
return nil,errors.New("Cache not exists")
}
//根据项目ID查询文档列表.
func (m *Document) FindListByBookId(book_id int) (docs []*Document, err error) {
func (m *Document) FindListByBookId(bookId int) (docs []*Document, err error) {
o := orm.NewOrm()
_, err = o.QueryTable(m.TableNameWithPrefix()).Filter("book_id", book_id).OrderBy("order_sort").All(&docs)
_, err = o.QueryTable(m.TableNameWithPrefix()).Filter("book_id", bookId).OrderBy("order_sort").All(&docs)
return
}