refactor(cache): 重构 CacheMemory 实现

- 使用 sync.Map替代自定义 Map 结构- 优化缓存过期处理逻辑
- 改进通配符删除功能
- 随机触发缓存清理机制
This commit is contained in:
hoteas 2025-04-10 22:09:53 +08:00
parent 2cf20e7206
commit 9766648536
5 changed files with 210 additions and 117 deletions

View File

@ -285,7 +285,7 @@ func (that *Application) SetConnectListener(lis func(that *Context) (isFinished
// //
//} //}
//序列化链接 // 序列化链接
func (that *Application) urlSer(url string) (string, []string) { func (that *Application) urlSer(url string) (string, []string) {
q := strings.Index(url, "?") q := strings.Index(url, "?")
if q == -1 { if q == -1 {
@ -467,6 +467,7 @@ func (that *Application) crossDomain(context *Context, sessionId string) {
if context.Config.GetString("crossDomain") == "" { if context.Config.GetString("crossDomain") == "" {
if sessionId != "" { if sessionId != "" {
http.SetCookie(context.Resp, &http.Cookie{Name: that.Config.GetString("sessionName"), Value: sessionId, Path: "/"}) http.SetCookie(context.Resp, &http.Cookie{Name: that.Config.GetString("sessionName"), Value: sessionId, Path: "/"})
//context.Resp.Header().Set("Set-Cookie", that.Config.GetString("sessionName")+"="+sessionId+"; Path=/; SameSite=None; Secure")
} }
return return
@ -496,7 +497,7 @@ func (that *Application) crossDomain(context *Context, sessionId string) {
header.Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE") header.Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE")
header.Set("Access-Control-Allow-Credentials", "true") header.Set("Access-Control-Allow-Credentials", "true")
header.Set("Access-Control-Expose-Headers", "*") header.Set("Access-Control-Expose-Headers", "*")
header.Set("Access-Control-Allow-Headers", "X-Requested-With,Content-Type,Access-Token") header.Set("Access-Control-Allow-Headers", "X-Requested-With,Content-Type,Access-Token,Authorization,Cookie,Set-Cookie")
if sessionId != "" { if sessionId != "" {
//跨域允许需要设置cookie的允许跨域https才有效果 //跨域允许需要设置cookie的允许跨域https才有效果
@ -511,7 +512,8 @@ func (that *Application) crossDomain(context *Context, sessionId string) {
if (origin != "" && strings.Contains(origin, remoteHost)) || strings.Contains(refer, remoteHost) { if (origin != "" && strings.Contains(origin, remoteHost)) || strings.Contains(refer, remoteHost) {
if sessionId != "" { if sessionId != "" {
http.SetCookie(context.Resp, &http.Cookie{Name: that.Config.GetString("sessionName"), Value: sessionId, Path: "/"}) //http.SetCookie(context.Resp, &http.Cookie{Name: that.Config.GetString("sessionName"), Value: sessionId, Path: "/"})
context.Resp.Header().Set("Set-Cookie", that.Config.GetString("sessionName")+"="+sessionId+"; Path=/; SameSite=None; Secure")
} }
return return
@ -542,7 +544,7 @@ func (that *Application) crossDomain(context *Context, sessionId string) {
header.Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE") header.Set("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE")
header.Set("Access-Control-Allow-Credentials", "true") header.Set("Access-Control-Allow-Credentials", "true")
header.Set("Access-Control-Expose-Headers", "*") header.Set("Access-Control-Expose-Headers", "*")
header.Set("Access-Control-Allow-Headers", "X-Requested-With,Content-Type,Access-Token") header.Set("Access-Control-Allow-Headers", "X-Requested-With,Content-Type,Access-Token,Authorization,Cookie,Set-Cookie")
if sessionId != "" { if sessionId != "" {
//跨域允许需要设置cookie的允许跨域https才有效果 //跨域允许需要设置cookie的允许跨域https才有效果
@ -551,7 +553,7 @@ func (that *Application) crossDomain(context *Context, sessionId string) {
} }
//Init 初始化application // Init 初始化application
func Init(config string) *Application { func Init(config string) *Application {
appIns := Application{} appIns := Application{}
//手动模式, //手动模式,

173
cache/cache_memory.go vendored
View File

@ -7,14 +7,13 @@ import (
"time" "time"
) )
// CacheMemory 基于 sync.Map 的缓存实现
type CacheMemory struct { type CacheMemory struct {
TimeOut int64 TimeOut int64
DbSet bool DbSet bool
SessionSet bool SessionSet bool
Map
*Error *Error
ContextBase cache sync.Map // 替代传统的 Map
mutex *sync.RWMutex
} }
func (that *CacheMemory) GetError() *Error { func (that *CacheMemory) GetError() *Error {
@ -26,133 +25,91 @@ func (that *CacheMemory) GetError() *Error {
func (that *CacheMemory) SetError(err *Error) { func (that *CacheMemory) SetError(err *Error) {
that.Error = err that.Error = err
} }
func (c *CacheMemory) get(key string) (res *Obj) {
//获取Cache键只能为string类型 res = &Obj{
func (that *CacheMemory) get(key string) interface{} { Error: *c.Error,
that.Error.SetError(nil) }
if that.Map == nil { value, ok := c.cache.Load(key)
that.Map = Map{} if !ok {
return res // 缓存不存在
} }
if that.Map[key] == nil { data := value.(cacheData)
return nil
}
data := that.Map.Get(key, that.Error).(cacheData)
if that.Error.GetError() != nil {
return nil
}
// 检查是否过期
if data.time < time.Now().Unix() { if data.time < time.Now().Unix() {
delete(that.Map, key) c.cache.Delete(key) // 删除过期缓存
return nil return res
} }
return data.data res.Data = data.data
return res
} }
func (c *CacheMemory) set(key string, value interface{}, expireAt int64) {
func (that *CacheMemory) refreshMap() { data := cacheData{
data: value,
time: expireAt,
}
c.cache.Store(key, data)
}
func (c *CacheMemory) delete(key string) {
if strings.Contains(key, "*") {
// 通配符删除
prefix := strings.TrimSuffix(key, "*")
c.cache.Range(func(k, v interface{}) bool {
if strings.HasPrefix(k.(string), prefix) {
c.cache.Delete(k)
}
return true
})
} else {
// 精确删除
c.cache.Delete(key)
}
}
func (c *CacheMemory) refreshMap() {
go func() { go func() {
that.mutex.Lock() now := time.Now().Unix()
defer that.mutex.Unlock() c.cache.Range(func(key, value interface{}) bool {
for key, v := range that.Map { data := value.(cacheData)
data := v.(cacheData) if data.time <= now {
if data.time <= time.Now().Unix() { c.cache.Delete(key) // 删除过期缓存
delete(that.Map, key)
} }
} return true
})
}() }()
} }
func (c *CacheMemory) Cache(key string, data ...interface{}) *Obj {
now := time.Now().Unix()
//key value ,时间为时间戳 // 随机触发刷新
func (that *CacheMemory) set(key string, value interface{}, time int64) { if x := RandX(1, 100000); x > 99950 {
that.Error.SetError(nil) c.refreshMap()
var data cacheData
if that.Map == nil {
that.Map = Map{}
} }
dd := that.Map[key]
if dd == nil {
data = cacheData{}
} else {
data = dd.(cacheData)
}
data.time = time
data.data = value
that.Map.Put(key, data)
}
func (that *CacheMemory) delete(key string) {
del := strings.Index(key, "*")
//如果通配删除
if del != -1 {
key = Substr(key, 0, del)
for k, _ := range that.Map {
if strings.Index(k, key) != -1 {
delete(that.Map, k)
}
}
} else {
delete(that.Map, key)
}
}
func (that *CacheMemory) Cache(key string, data ...interface{}) *Obj {
x := RandX(1, 100000)
if x > 99950 {
that.refreshMap()
}
if that.mutex == nil {
that.mutex = &sync.RWMutex{}
}
reData := &Obj{Data: nil}
if len(data) == 0 { if len(data) == 0 {
that.mutex.RLock() // 读操作
reData.Data = that.get(key) return c.get(key)
that.mutex.RUnlock()
return reData
} }
tim := time.Now().Unix()
if len(data) == 1 && data[0] == nil { if len(data) == 1 && data[0] == nil {
that.mutex.Lock() // 删除操作
that.delete(key) c.delete(key)
that.mutex.Unlock() return nil
return reData
} }
if len(data) == 1 { // 写操作
expireAt := now + c.TimeOut
tim = tim + that.TimeOut
}
if len(data) == 2 { if len(data) == 2 {
that.Error.SetError(nil) if customExpire, ok := data[1].(int64); ok {
tempt := ObjToInt64(data[1], that.Error) if customExpire > now {
expireAt = customExpire
if tempt > tim { } else {
expireAt = now + customExpire
tim = tempt }
} else if that.Error.GetError() == nil {
tim = tim + tempt
} }
} }
that.mutex.Lock()
that.set(key, data[0], tim)
that.mutex.Unlock()
return reData
c.set(key, data[0], expireAt)
return nil
} }

View File

@ -158,7 +158,7 @@ func (that *MakeCode) Db2JSON(db *db.HoTimeDB, config Map) {
tableInfo := make([]Map, 0) tableInfo := make([]Map, 0)
if db.Type == "mysql" { if db.Type == "mysql" {
tableInfo = db.Select("INFORMATION_SCHEMA.COLUMNS", "COLUMN_NAME AS name,COLUMN_TYPE AS type,COLUMN_COMMENT AS label,IS_NULLABLE AS must,COLUMN_DEFAULT AS dflt_value", Map{"AND": Map{"TABLE_SCHEMA": db.DBName, "TABLE_NAME": v.GetString("name")}}) tableInfo = db.Select("INFORMATION_SCHEMA.COLUMNS", "COLUMN_NAME AS name,COLUMN_TYPE AS type,COLUMN_COMMENT AS label,IS_NULLABLE AS must,COLUMN_DEFAULT AS dflt_value", Map{"AND": Map{"TABLE_SCHEMA": db.DBName, "TABLE_NAME": v.GetString("name")}, "ORDER": "ORDINAL_POSITION"})
} }
if db.Type == "sqlite" { if db.Type == "sqlite" {
tableInfo = db.Query("pragma table_info([" + v.GetString("name") + "]);") tableInfo = db.Query("pragma table_info([" + v.GetString("name") + "]);")

View File

@ -276,7 +276,10 @@ func (that *HoTimeDB) Action(action func(db HoTimeDB) (isSuccess bool)) (isSucce
that.Prefix, that.LastQuery, that.LastData, that.Prefix, that.LastQuery, that.LastData,
that.ConnectFunc, that.LastErr, that.limit, that.Tx, that.ConnectFunc, that.LastErr, that.limit, that.Tx,
that.SlaveDB, that.Mode} that.SlaveDB, that.Mode}
//tx, err := db.BeginTx(context.Background(), &sql.TxOptions{Isolation: sql.LevelReadCommitted})
tx, err := db.Begin() tx, err := db.Begin()
if err != nil { if err != nil {
that.LastErr.SetError(err) that.LastErr.SetError(err)
return isSuccess return isSuccess
@ -559,6 +562,24 @@ func (that *HoTimeDB) Query(query string, args ...interface{}) []Map {
that.LastErr.SetError(err) that.LastErr.SetError(err)
return nil return nil
} }
for key, _ := range args {
arg := args[key]
argType := reflect.ValueOf(arg).Type().String()
if strings.Contains(argType, "[]") || strings.Contains(argType, "Slice") {
argLis := ObjToSlice(arg)
//将slice转为分割字符串
argStr := ""
for i := 0; i < len(argLis); i++ {
if i == len(argLis)-1 {
argStr += ObjToStr(argLis[i])
} else {
argStr += ObjToStr(argLis[i]) + ","
}
}
args[key] = argStr
}
}
if that.Tx != nil { if that.Tx != nil {
resl, err = that.Tx.Query(query, args...) resl, err = that.Tx.Query(query, args...)
@ -601,6 +622,29 @@ func (that *HoTimeDB) Exec(query string, args ...interface{}) (sql.Result, *Erro
return nil, that.LastErr return nil, that.LastErr
} }
for key, _ := range args {
arg := args[key]
argType := ""
if arg != nil {
argType = reflect.ValueOf(arg).Type().String()
}
if strings.Contains(argType, "[]") || strings.Contains(argType, "Slice") {
argLis := ObjToSlice(arg)
//将slice转为分割字符串
argStr := ""
for i := 0; i < len(argLis); i++ {
if i == len(argLis)-1 {
argStr += ObjToStr(argLis[i])
} else {
argStr += ObjToStr(argLis[i]) + ","
}
}
args[key] = argStr
}
}
if that.Tx != nil { if that.Tx != nil {
resl, e = that.Tx.Exec(query, args...) resl, e = that.Tx.Exec(query, args...)
} else { } else {
@ -874,7 +918,7 @@ func (that *HoTimeDB) Sum(table string, column string, qu ...interface{}) float6
} }
//where语句解析 // where语句解析
func (that *HoTimeDB) where(data Map) (string, []interface{}) { func (that *HoTimeDB) where(data Map) (string, []interface{}) {
where := "" where := ""
@ -1368,7 +1412,7 @@ func (that *HoTimeDB) varCond(k string, v interface{}) (string, []interface{}) {
return where, res return where, res
} }
// that.Db.Update("user",hotime.Map{"ustate":"1"},hotime.Map{"AND":hotime.Map{"OR":hotime.Map{"uid":4,"uname":"dasda"}},"ustate":1}) // that.Db.Update("user",hotime.Map{"ustate":"1"},hotime.Map{"AND":hotime.Map{"OR":hotime.Map{"uid":4,"uname":"dasda"}},"ustate":1})
func (that *HoTimeDB) notIn(k string, v interface{}, where string, res []interface{}) (string, []interface{}) { func (that *HoTimeDB) notIn(k string, v interface{}, where string, res []interface{}) (string, []interface{}) {
//where:="" //where:=""
//fmt.Println(reflect.ValueOf(v).Type().String()) //fmt.Println(reflect.ValueOf(v).Type().String())
@ -1504,7 +1548,7 @@ func (that *HoTimeDB) Update(table string, data Map, where Map) int64 {
func (that *HoTimeDB) Delete(table string, data map[string]interface{}) int64 { func (that *HoTimeDB) Delete(table string, data map[string]interface{}) int64 {
query := "DELETE FROM " + that.Prefix + table + " " query := "DELETE FROM `" + that.Prefix + table + "` "
temp, resWhere := that.where(data) temp, resWhere := that.where(data)
query += temp + ";" query += temp + ";"

View File

@ -1,6 +1,7 @@
package baidu package baidu
import ( import (
. "co
"fmt" "fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
@ -21,6 +22,95 @@ func (that *baiduMap) Init(Ak string) {
//query //query
} }
// from 源坐标类型:
// 1GPS标准坐标
// 2搜狗地图坐标
// 3火星坐标gcj02即高德地图、腾讯地图和MapABC等地图使用的坐标
// 43中列举的地图坐标对应的墨卡托平面坐标;
// 5百度地图采用的经纬度坐标bd09ll
// 6百度地图采用的墨卡托平面坐标bd09mc;
// 7图吧地图坐标
// 851地图坐标
// int 1 1 否
// to
// 目标坐标类型:
// 3火星坐标gcj02即高德地图、腾讯地图及MapABC等地图使用的坐标
// 5百度地图采用的经纬度坐标bd09ll
// 6百度地图采用的墨卡托平面坐标bd09mc
func (that *baiduMap) Geoconv(latlngs []Map, from, to int) (Slice, error) {
client := &http.Client{}
latlngsStr := ""
for _, v := range latlngs {
if latlngsStr != "" {
latlngsStr = latlngsStr + ";" + v.GetString("lng") + "," + v.GetString("lat")
} else {
latlngsStr = v.GetString("lng") + "," + v.GetString("lat")
}
}
url := "https://api.map.baidu.com/geoconv/v1/?from=" + ObjToStr(from) + "&to=" + ObjToStr(to) + "&ak=" + that.Ak + "&coords=" + latlngsStr
reqest, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Println("Fatal error ", err.Error())
return nil, err
}
response, err := client.Do(reqest)
defer response.Body.Close()
if err != nil {
fmt.Println("Fatal error ", err.Error())
return nil, err
}
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return nil, err
}
//fmt.Println(string(body))
data := ObjToMap(string(body))
if data.GetCeilInt64("status") != 0 {
return nil, err
}
return data.GetSlice("result"), err
}
func (that *baiduMap) GetAddress(lat string, lng string) (string, error) {
client := &http.Client{}
url := "https://api.map.baidu.com/reverse_geocoding/v3/?ak=" + that.Ak + "&output=json&location=" + lat + "," + lng
reqest, err := http.NewRequest("GET", url, nil)
if err != nil {
fmt.Println("Fatal error ", err.Error())
return "", err
}
response, err := client.Do(reqest)
defer response.Body.Close()
if err != nil {
fmt.Println("Fatal error ", err.Error())
return "", err
}
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return "", err
}
//fmt.Println(string(body))
return string(body), err
}
dy))
return string(b
// GetPosition 获取定位列表 // GetPosition 获取定位列表
func (that *baiduMap) GetPosition(name string, region string) (string, error) { func (that *baiduMap) GetPosition(name string, region string) (string, error) {