perf(auth): JWT 权限缓存 RwLock 替换为 DashMap
USER_SCOPE_CACHE 从 LazyLock<RwLock<HashMap>> 改为 LazyLock<DashMap>, 消除读写锁竞争,提升高并发场景下的认证中间件吞吐量。 过期条目淘汰逻辑改用 DashMap::retain,无需手动获取 write lock。
This commit is contained in:
@@ -26,3 +26,4 @@ cbc.workspace = true
|
||||
hex.workspace = true
|
||||
base64 = "0.22"
|
||||
redis.workspace = true
|
||||
dashmap.workspace = true
|
||||
|
||||
@@ -2,6 +2,7 @@ use axum::body::Body;
|
||||
use axum::http::Request;
|
||||
use axum::middleware::Next;
|
||||
use axum::response::Response;
|
||||
use dashmap::DashMap;
|
||||
use erp_core::error::AppError;
|
||||
use erp_core::request_info::REQUEST_INFO;
|
||||
use erp_core::request_info::RequestInfo;
|
||||
@@ -12,11 +13,11 @@ use crate::service::token_service::TokenService;
|
||||
type DeptIds = Vec<uuid::Uuid>;
|
||||
type DataScopes = std::collections::HashMap<String, DataScope>;
|
||||
type ScopeCacheEntry = (DeptIds, DataScopes, std::time::Instant);
|
||||
type ScopeCacheMap = std::collections::HashMap<uuid::Uuid, ScopeCacheEntry>;
|
||||
|
||||
/// 用户权限数据缓存(user_id -> (department_ids, data_scopes, cached_at))
|
||||
static USER_SCOPE_CACHE: std::sync::LazyLock<std::sync::RwLock<ScopeCacheMap>> =
|
||||
std::sync::LazyLock::new(|| std::sync::RwLock::new(std::collections::HashMap::new()));
|
||||
/// DashMap 分片并发,读写无锁竞争
|
||||
static USER_SCOPE_CACHE: std::sync::LazyLock<DashMap<uuid::Uuid, ScopeCacheEntry>> =
|
||||
std::sync::LazyLock::new(DashMap::new);
|
||||
|
||||
const SCOPE_CACHE_TTL: std::time::Duration = std::time::Duration::from_secs(60);
|
||||
|
||||
@@ -76,16 +77,17 @@ pub async fn jwt_auth_middleware_fn(
|
||||
}
|
||||
|
||||
// 查询用户所属部门 ID 列表 + 权限数据范围(带 60 秒缓存)
|
||||
let cached = {
|
||||
let cache = USER_SCOPE_CACHE.read().unwrap();
|
||||
cache.get(&claims.sub).and_then(|(depts, scopes, at)| {
|
||||
if at.elapsed() < SCOPE_CACHE_TTL {
|
||||
Some((depts.clone(), scopes.clone()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
};
|
||||
let cached = USER_SCOPE_CACHE.get(&claims.sub).and_then(|entry| {
|
||||
let (_, _, at) = entry.value();
|
||||
if at.elapsed() < SCOPE_CACHE_TTL {
|
||||
let (depts, scopes, _) = entry.value();
|
||||
Some((depts.clone(), scopes.clone()))
|
||||
} else {
|
||||
drop(entry);
|
||||
USER_SCOPE_CACHE.remove(&claims.sub);
|
||||
None
|
||||
}
|
||||
});
|
||||
let (department_ids, permission_data_scopes) = match cached {
|
||||
Some(hit) => hit,
|
||||
None => fetch_and_cache_scopes(claims.sub, claims.tid, &db).await,
|
||||
@@ -207,15 +209,14 @@ async fn fetch_and_cache_scopes(
|
||||
Some(conn) => fetch_permission_data_scopes(user_id, tenant_id, conn).await,
|
||||
None => std::collections::HashMap::new(),
|
||||
};
|
||||
let mut cache = USER_SCOPE_CACHE.write().unwrap();
|
||||
cache.insert(
|
||||
USER_SCOPE_CACHE.insert(
|
||||
user_id,
|
||||
(depts.clone(), scopes.clone(), std::time::Instant::now()),
|
||||
);
|
||||
// 惰性淘汰过期条目,防止 HashMap 无限增长
|
||||
if cache.len() > 500 {
|
||||
// 惰性淘汰过期条目,防止 DashMap 无限增长
|
||||
if USER_SCOPE_CACHE.len() > 500 {
|
||||
let now = std::time::Instant::now();
|
||||
cache.retain(|_, (_, _, at)| now.duration_since(*at) < SCOPE_CACHE_TTL);
|
||||
USER_SCOPE_CACHE.retain(|_, (_, _, at)| now.duration_since(*at) < SCOPE_CACHE_TTL);
|
||||
}
|
||||
(depts, scopes)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user