快取使用範例
位置:
app-server模組 目的: 展示如何在實際 Spring Boot 應用程式中使用 AppFuse Cache
📋 目錄
快取配置
配置類別
位置: io.leandev.app.config.CacheConfig
@Configuration
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
return CacheManagerBuilder
.newCacheManager()
.withPersistence(Paths.get("./data/cache")) // 支援持久化
.build();
}
// 定義各種快取 Bean...
}
已定義的快取
| 快取名稱 | 類型 | 用途 | 過期策略 |
|---|---|---|---|
sessions | DualLayerCache | Session 管理 | 快速層 30 分鐘 TTL |
users | Cache | 使用者資訊 | 30 分鐘 TTL |
rateLimit | Cache | API 限流 | 5 分鐘 TTI |
verificationCodes | Cache | 驗證碼 | 5 分鐘 TTL |
configs | Cache | 系統配置 | 永不過期 |
tokenBlacklist | Cache | Token 黑名單 | 15 分鐘 TTL |
loginAttempts | Cache | Login Lockout | 30 分鐘 TTI |
Session 快取
使用場景
展示如何使用雙層快取管理 Session,提供高效能與容錯能力。
服務類別
位置: io.leandev.app.service.cache.SessionCacheService
使用範例
@RestController
@RequestMapping("/api/sessions")
@RequiredArgsConstructor
public class SessionController {
private final SessionCacheService sessionCacheService;
// 建立 Session
@PostMapping("/login")
public String login(@RequestBody LoginRequest request) {
String userId = authenticateUser(request);
String sessionId = sessionCacheService.createSession(userId);
return sessionId;
}
// 取得 Session(自動降級)
@GetMapping("/{sessionId}")
public Optional<String> getSession(@PathVariable String sessionId) {
return sessionCacheService.getSession(sessionId);
}
// 延長 Session
@PostMapping("/{sessionId}/touch")
public void touchSession(@PathVariable String sessionId) {
sessionCacheService.touchSession(sessionId);
}
// 登出
@DeleteMapping("/{sessionId}")
public void logout(@PathVariable String sessionId) {
sessionCacheService.removeSession(sessionId);
}
}
關鍵特性
1. 雙層架構
// 快速層:存放熱資料,30 分鐘過期
// 持久層:存放所有資料,永不過期
sessionCache.put(sessionId, userId); // 同時寫入兩層
2. 自動降級
// 優先從快速層讀取,若不存在則自動降級到持久層
String userId = sessionCache.get(sessionId);
3. 不降級模式
// 只從快速層讀取,用於確認 session 是否仍在有效期內
String userId = sessionCache.get(sessionId, false);
4. 統計監控
CacheStatistics stats = sessionCacheService.getStatistics();
System.out.println("Hit ratio: " + stats.getHitRatio());
System.out.println("Fallback count: " + sessionCacheService.getFallbackCount());
使用者快取
使用場景
展示如何使用標準快取實作 Cache-Aside Pattern,減少資料庫查詢。
服務類別
位置: io.leandev.app.service.cache.UserCacheService
使用範例
@Service
@RequiredArgsConstructor
public class UserService {
private final UserCacheService userCacheService;
private final UserRepository userRepository;
// Cache-Aside Pattern
public Optional<User> getUser(Long userId) {
return userCacheService.getUser(userId,
// 資料庫查詢 Lambda
id -> userRepository.findById(id)
.map(User::toJson)
);
}
// Write-Through Pattern
public void updateUser(Long userId, String userData) {
userCacheService.updateUser(userId, userData,
// 資料庫更新 Lambda
(id, data) -> {
User user = User.fromJson(data);
userRepository.save(user);
}
);
}
// 預熱快取
@PostConstruct
public void warmUpCache() {
List<Long> hotUserIds = userRepository.findHotUserIds();
userCacheService.warmUp(hotUserIds,
id -> userRepository.findById(id).map(User::toJson)
);
}
}
快取模式
1. Cache-Aside(旁路快取)
// 1. 先查快取
String user = cache.get(userId);
// 2. 若 miss,查資料庫
if (user == null) {
user = database.findById(userId);
cache.put(userId, user); // 3. 寫入快取
}
2. Write-Through(寫穿)
// 同步更新資料庫和快取
database.update(userId, userData);
cache.put(userId, userData);
3. Cache Invalidation(失效)
// 資料庫直接更新後,手動失效快取
database.update(userId, userData);
userCacheService.invalidate(userId);
API 限流
使用場景
展示如何使用 Time-to-Idle 快取實作 API 限流,防止濫用。
服務類別
位置: io.leandev.app.service.cache.RateLimitService
使用範例
@Component
@RequiredArgsConstructor
public class RateLimitInterceptor implements HandlerInterceptor {
private final RateLimitService rateLimitService;
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response,
Object handler) throws Exception {
String apiKey = request.getHeader("X-API-Key");
// 檢查限流
if (!rateLimitService.allowRequest(apiKey)) {
response.setStatus(429); // Too Many Requests
response.getWriter().write("Rate limit exceeded");
return false;
}
// 設定剩餘次數 Header
int remaining = rateLimitService.getRemainingRequests(apiKey);
response.setHeader("X-RateLimit-Remaining", String.valueOf(remaining));
return true;
}
}
自訂限制
// 不同等級的限制
public class ApiTier {
public static final int FREE_TIER = 100; // 免費:100 次/5分鐘
public static final int BASIC_TIER = 500; // 基礎:500 次/5分鐘
public static final int PREMIUM_TIER = 5000; // 進階:5000 次/5分鐘
}
// 使用
boolean allowed = rateLimitService.allowRequest(apiKey, ApiTier.PREMIUM_TIER);
Time-to-Idle 特性
- 每次存取後重新計時:持續活動的用戶不會被清除
- 5 分鐘無活動則過期:自動清理不活躍的記錄
- 節省記憶體:只保留活躍用戶的記錄
監控與管理
快取統計
@GetMapping("/admin/cache/stats")
public Map<String, CacheStatistics> getCacheStatistics() {
return Map.of(
"sessions", sessionCacheService.getStatistics(),
"users", userCacheService.getStatistics(),
"rateLimit", rateLimitService.getStatistics()
);
}
統計資訊包含
- hitCount: 命中次數
- missCount: 未命中次數
- hitRatio: 命中率(0.0 ~ 1.0)
- size: 當前項目數
- putCount: 寫入次數
- removeCount: 移除次數
- evictionCount: 淘汰次數
快取管理操作
// 停用快取(維護模式)
sessionCacheService.disableCache();
// 啟用快取
sessionCacheService.enableCache();
// 清空快取
userCacheService.clearAll();
// 手動失效
userCacheService.invalidate(userId);
雙層快取特殊操作
// 只清除快速層(保留持久層作為降級)
sessionCacheService.clearExpiredSessions();
// 查看降級次數
long fallbackCount = sessionCacheService.getFallbackCount();
// 查看統計(包含兩層的聚合統計)
CacheStatistics stats = sessionCacheService.getStatistics();
最佳實踐
1. 選擇合適的快取類型
| 場景 | 推薦類型 | 原因 |
|---|---|---|
| Session 管理 | DualLayerCache | 需要降級能力 |
| 使用者資訊 | Cache (TTL) | 定期過期避免髒資料 |
| API 限流 | Cache (TTI) | 自動清理不活躍記錄 |
| 系統配置 | Cache (NoExpiration) | 手動控制更新 |
2. 合理設定過期時間
// 短期資料:5-15 分鐘
.ttl(5)
// 中期資料:30-60 分鐘
.ttl(30)
// 永久資料:不過期
.noExpiration()
3. 監控快取效能
// 定期檢查命中率
CacheStatistics stats = cache.getStatistics();
if (stats.getHitRatio() < 0.8) {
log.warn("Low cache hit ratio: {}", stats.getHitRatio());
}
4. 處理快取穿透
// 快取空值,避免重複查詢不存在的資料
if (userFromDb.isEmpty()) {
cache.put(userId, "NULL"); // 使用特殊值表示不存在
}
5. 快取預熱
@PostConstruct
public void warmUp() {
// 在系統啟動時預先載入熱門資料
List<Long> hotIds = repository.findHotIds();
cacheService.warmUp(hotIds, repository::findById);
}
相關文檔
- Cache API 參考 - Cache 模組 API 文檔
- Cache 使用指南 - Cache 模組使用指南