Cache đa tầng
Dependency cung cấp tầng cache nhiều chiến lược (embedded, external, composite) với annotation @GCacheable, @GCachePut, @GCacheEvict có TTL, cache giá trị null, lock theo key và thống kê hit/miss. Dùng khi service cần cache dữ liệu đọc nhiều mà không muốn tự viết logic cache thủ công.
Khi nào sử dụng
- Cache kết quả truy vấn DB hoặc gọi API ngoài theo phương thức khai báo (annotation) thay vì tự quản lý map/Redis.
- Cần TTL khác nhau cho từng method hoặc từng vùng cache mà Spring Cache tiêu chuẩn không đáp ứng.
- Cần cache hai tầng (local + phân tán) để giảm round-trip tới Redis nhưng vẫn đồng bộ giữa các instance.
- Cần lock theo key (tránh cache stampede) hoặc
computeIfAbsentcó loader tự nạp lại dữ liệu.
Cài đặt
Thêm dependency vào pom.xml; version do BOM vn.govex.cloud:dependencies quản lý nên không khai version:
<dependency>
<groupId>vn.govex.cloud</groupId>
<artifactId>govex-cloud-cache</artifactId>
</dependency>
Nếu chưa cấu hình BOM/parent, xem Cài đặt.
Cấu hình
| Property | Mô tả | Mặc định |
|---|---|---|
govex.cache.enabled | Bật/tắt toàn bộ cache của dependency. | true |
govex.cache.type | Loại cache toàn cục: EMBEDDED, EXTERNAL, COMPOSITE hoặc NONE. | NONE |
govex.cache.dynamic | Cho phép tạo cache động theo cacheNames chưa khai báo trong cache-configs. | true |
govex.cache.penetration-protect | Bảo vệ cache penetration toàn cục. | false |
govex.cache.embedded.type | Kiểu embedded cache. | CAFFEINE |
govex.cache.embedded.caffeine.limit | Số entry tối đa của cache local. | 100 |
govex.cache.embedded.caffeine.expire-after-write-in-millis | TTL tính từ lúc ghi (ms); giá trị <= 0 là không hết hạn. | -1 |
govex.cache.embedded.caffeine.expire-after-access-in-millis | TTL tính từ lần truy cập (ms). | 0 |
govex.cache.embedded.caffeine.cache-null-value | Cho phép cache giá trị null. | false |
govex.cache.embedded.caffeine.enable-stat | Bật thống kê hit/miss cho cache local. | false |
govex.cache.external.type | Kiểu external cache: REDIS hoặc REDISSON. | REDISSON |
govex.cache.cache-configs.<cache-name>.type | Loại cache riêng cho từng cache name. | EMBEDDED |
govex.cache.cache-configs.<cache-name>.expire-after-write-in-millis | TTL ghi riêng cho cache name. | -1 |
govex.cache.cache-configs.<cache-name>.cache-null-value | Cho phép cache null riêng cho cache name. | false |
govex.cache.stat.enabled | Bật tổng hợp thống kê cache. | true |
govex.cache.stat.verbose-log | Log chi tiết từng thao tác get/put/remove thay vì bản tóm tắt. | false |
govex.cache.stat.interval | Chu kỳ tổng hợp và ghi log thống kê. | 1m |
Backend external cache (Redis / Redisson)
Cache EXTERNAL/COMPOSITE cần thêm một implementation bên cạnh govex-cloud-cache (version do BOM quản lý):
| Dependency | Backend | Khi dùng |
|---|---|---|
govex-cloud-redis | Spring Data Redis, kèm bean redisTemplate serialize JSON | Chỉ cần cache phân tán, không cần lock/rate limit của Redisson |
govex-cloud-redisson | RedissonClient | Cần cache phân tán kèm khoá phân tán/giới hạn tần suất trên cùng một client |
Chọn backend qua govex.cache.external.type (REDIS hoặc REDISSON, mặc định REDISSON). Kết nối dùng cấu hình chuẩn spring.data.redis.* của Spring Boot:
spring:
data:
redis:
host: localhost
port: 6379
govex:
cache:
enabled: true
type: EXTERNAL
external:
type: REDIS
redis:
key-prefix: "myapp:"
sync-topic-channel: "myapp:cache-sync"
expire-after-write-in-millis: 86400000
Property riêng của backend Redis (govex.cache.external.redis.*); backend Redisson có bộ property tương ứng dưới govex.cache.external.redisson.*:
| Property | Mô tả | Mặc định |
|---|---|---|
govex.cache.type | Phải là EXTERNAL hoặc COMPOSITE để backend được kích hoạt. | NONE |
govex.cache.external.type | REDIS hoặc REDISSON — đặt đúng backend đã thêm dependency. | REDISSON |
govex.cache.external.redis.key-prefix | Tiền tố gắn trước key của mọi cache trên Redis, tách không gian key giữa các ứng dụng. | — |
govex.cache.external.redis.sync-topic-channel | Kênh pub/sub đồng bộ thay đổi cache giữa các instance. | — |
govex.cache.external.redis.expire-after-write-in-millis | TTL tính từ lúc ghi (ms); chỉ áp dụng khi lớn hơn 0. | -1 |
govex.cache.external.redis.expire-after-access-in-millis | TTL tính từ lần truy cập (ms). | 0 |
govex.cache.external.redis.cache-null-value | Cho phép cache giá trị null. | false |
govex.cache.external.redis.cache-penetration-protect | Bảo vệ cache penetration. | false |
govex.cache.external.redis.enable-stat | Bật thống kê hit/miss cho backend. | false |
govex.cache.external.redis.refresh-policy.refresh-millis | Chu kỳ refresh trước khi entry hết hạn (ms). | 0 |
govex.cache.external.redis.refresh-policy.refresh-lock-timeout-millis | Thời gian giữ lock tối đa khi load dữ liệu refresh (ms). | 60000 |
Annotation cache dùng cacheType = CacheType.EXTERNAL để trỏ đúng cache manager phân tán:
@GCacheable(cacheNames = "products", key = "#id", cacheType = CacheType.EXTERNAL,
expireTime = 1, timeUnit = TimeUnit.DAYS)
public Product getProductById(Long id) {
return productRepository.findById(id).orElse(null);
}
Lưu ý riêng của backend external:
govex-cloud-redisluôn đăng ký beanredisTemplate(không dùng@ConditionalOnMissingBean): nếu ứng dụng tự khai báo bean cùng tên, kiểm tra xung đột bean khi khởi động.govex-cloud-redissonchỉ tạo bean khi ứng dụng chưa có.- Đồng bộ cache giữa các instance chỉ hoạt động khi
sync-topic-channelđược cấu hình và các instance dùng chung Redis. govex-cloud-redissonnạp thêm fileconfig/redisson.ymltrên classpath như property source bổ sung nếu cần tinh chỉnhRedissonClient.- Muốn lock/rate limit dùng Redisson, thêm
govex-cloud-redissonthay vì hoặc bổ sung cho Redis thuần. - Cần Redis đang chạy và
RedisConnectionFactorydo Spring Boot auto-configuration tạo (spring-boot-starter-data-redis).
Sử dụng
Bước 1 — bật cache bằng @EnableGCaching trên một class @Configuration (bắt buộc, xem Lưu ý):
@Configuration
@EnableGCaching
public class CacheConfig {
}
Bước 2 — cấu hình loại cache. Ví dụ cache hai tầng composite (cần thêm govex-cloud-redisson hoặc govex-cloud-redis):
govex:
cache:
enabled: true
type: COMPOSITE
dynamic: true
embedded:
type: CAFFEINE
caffeine:
limit: 1000
expire-after-write-in-millis: 3600000
external:
type: REDISSON
cache-configs:
product-cache:
type: COMPOSITE
expire-after-write-in-millis: 86400000
cache-null-value: true
Bước 3 — khai báo annotation trên service. Luôn đặt cacheType khớp với loại cache đã cấu hình:
@GCacheable(cacheNames = "products", key = "#id", cacheType = CacheType.EMBEDDED,
expireTime = 10, timeUnit = TimeUnit.MINUTES)
public Product getProductById(Long id) {
return productRepository.findById(id).orElse(null);
}
@GCachePut(cacheNames = "products", key = "#product.id", expireTime = 1, timeUnit = TimeUnit.DAYS)
public Product updateProduct(Product product) {
return productRepository.save(product);
}
@GCacheEvict(cacheNames = "products", key = "#id", condition = "#id > 0")
public void deleteProduct(Long id) {
productRepository.deleteById(id);
}
Khi cần thao tác thủ công, inject GCacheManager và dùng GCache:
GCache cache = cacheManager.getCache("products");
cache.put("product:123", product, 30, TimeUnit.MINUTES);
Product product = cache.computeIfAbsent("product:123", key -> fetchProductFromDb(key),
true, 30, TimeUnit.MINUTES);
try (AutoReleaseLock lock = cache.tryLock("product:123", 30, TimeUnit.SECONDS)) {
if (lock != null) {
refreshProduct("product:123");
}
}
Lưu ý
- Bắt buộc có
@EnableGCaching: dependency không đăng ký auto-configuration quaAutoConfiguration.imports, thiếu annotation thì cache không hoạt động. cacheTypecủa@GCacheablephải trỏ tới cache manager đã đăng ký. Nếu chọn sai (ví dụ đặtEMBEDDEDkhigovex.cache.type=NONE), ứng dụng lỗi khi parse annotation vớiCacheConfigException.- Cache
EXTERNAL/COMPOSITEcần thêm implementationgovex-cloud-redishoặcgovex-cloud-redisson;govex.cache.external.typemặc định làREDISSON. - Cache embedded chỉ nằm trong bộ nhớ từng instance; khi chạy nhiều instance cần cache phân tán hoặc cấu hình đồng bộ qua topic của dependency external.
govex.cache.stat.intervaldùng định dạngDurationcủa Spring Boot (ví dụ30s,1m).- Chi tiết chọn backend external, đồng bộ cache qua topic và lưu ý xung đột bean
redisTemplate— xem mục Backend external cache (Redis / Redisson) ở trên.