From 64514b4fabc9daa1af2ac0b15dcd1c313b1c6176 Mon Sep 17 00:00:00 2001 From: Jeffrey Chen <78434827+TCOTC@users.noreply.github.com> Date: Mon, 22 Dec 2025 17:30:40 +0800 Subject: [PATCH] :zap: Improve the performance of the `getCloudUser` interface to resolve the lag issue during startup (#16647) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题: - /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 逻辑 --- kernel/model/cloud_service.go | 18 ++++++++++++------ kernel/model/conf.go | 17 +++++++++++------ 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/kernel/model/cloud_service.go b/kernel/model/cloud_service.go index 281d9029d..ea67eb611 100644 --- a/kernel/model/cloud_service.go +++ b/kernel/model/cloud_service.go @@ -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 diff --git a/kernel/model/conf.go b/kernel/model/conf.go index 29ef45f38..9d8873b02 100644 --- a/kernel/model/conf.go +++ b/kernel/model/conf.go @@ -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 }