Improve the performance of the getCloudUser interface to resolve the lag issue during startup (#16647)

问题:
- /api/setting/getCloudUser 接口在启动时卡顿数百毫秒
- 根本原因:GetUser()、SetUser() 和 Conf.Save() 使用同一个锁,存在锁竞争

解决方案:
1. 将 GetUser() 改为使用读锁(RLock),允许并发读取
2. 将用户数据与配置数据分离,使用独立的 userLock 锁
   - m 锁:保护配置数据的读写(Conf.Save() 使用)
   - userLock 锁:保护用户数据的读写(GetUser() 和 SetUser() 使用)
3. 优化 RefreshUser 函数,先尝试从内存获取用户,避免不必要的文件读取

相关文件:
- kernel/model/conf.go: 添加 userLock,修改 GetUser/SetUser 使用独立锁
- kernel/model/cloud_service.go: 优化 RefreshUser 逻辑
This commit is contained in:
Jeffrey Chen 2025-12-22 17:30:40 +08:00 committed by GitHub
parent 33175cad7c
commit 64514b4fab
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 23 additions and 12 deletions

View file

@ -330,23 +330,29 @@ func refreshAnnouncement() {
func RefreshUser(token string) {
threeDaysAfter := util.CurrentTimeMillis() + 1000*60*60*24*3
if "" == token {
if "" != Conf.UserData {
Conf.SetUser(loadUserFromConf())
// 先尝试从内存中获取用户,避免不必要的文件读取和锁竞争
user := Conf.GetUser()
if nil == user && "" != Conf.UserData {
user = loadUserFromConf()
if nil != user {
// 只有在用户数据存在且与当前用户不同时才设置用户,避免不必要的锁竞争
Conf.SetUser(user)
}
}
if nil == Conf.GetUser() {
if nil == user {
return
}
var tokenExpireTime int64
tokenExpireTime, err := strconv.ParseInt(Conf.GetUser().UserTokenExpireTime+"000", 10, 64)
tokenExpireTime, err := strconv.ParseInt(user.UserTokenExpireTime+"000", 10, 64)
if err != nil {
logging.LogErrorf("convert token expire time [%s] failed: %s", Conf.GetUser().UserTokenExpireTime, err)
logging.LogErrorf("convert token expire time [%s] failed: %s", user.UserTokenExpireTime, err)
util.PushErrMsg(Conf.Language(19), 5000)
return
}
if threeDaysAfter > tokenExpireTime {
token = Conf.GetUser().UserToken
token = user.UserToken
goto Net
}
return

View file

@ -83,11 +83,16 @@ type AppConf struct {
Snippet *conf.Snpt `json:"snippet"` // 代码片段
DataIndexState int `json:"dataIndexState"` // 数据索引状态0已索引1未索引
m *sync.Mutex
m *sync.RWMutex // 配置数据锁
userLock *sync.RWMutex // 用户数据独立锁,避免与配置保存操作竞争
}
func NewAppConf() *AppConf {
return &AppConf{LogLevel: "debug", m: &sync.Mutex{}}
return &AppConf{
LogLevel: "debug",
m: &sync.RWMutex{},
userLock: &sync.RWMutex{},
}
}
func (conf *AppConf) GetUILayout() *conf.UILayout {
@ -103,14 +108,14 @@ func (conf *AppConf) SetUILayout(uiLayout *conf.UILayout) {
}
func (conf *AppConf) GetUser() *conf.User {
conf.m.Lock()
defer conf.m.Unlock()
conf.userLock.RLock()
defer conf.userLock.RUnlock()
return conf.User
}
func (conf *AppConf) SetUser(user *conf.User) {
conf.m.Lock()
defer conf.m.Unlock()
conf.userLock.Lock()
defer conf.userLock.Unlock()
conf.User = user
}