Khoá phân tán & giới hạn request
Cung cấp khóa phân tán và giới hạn tần suất request qua annotation @Lock, @RateLimit cùng LockTemplate cho khóa thủ công. Dependency chỉ định nghĩa abstraction; muốn chạy thực tế cần thêm một implementation backend (trong workspace là govex-cloud-redisson).
Khi nào sử dụng
- Tránh chạy trùng tác vụ định kỳ giữa nhiều instance của cùng một service.
- Bảo vệ thao tác đồng thời trên cùng một bản ghi/tài nguyên nghiệp vụ.
- Giới hạn tần suất gọi API (ví dụ gửi OTP, gửi thông báo) theo toàn hệ thống hoặc theo IP.
- Cần khóa thủ công trong code với API
LockTemplatethay vì annotation.
Cài đặt
Thêm dependency govex-cloud-lock và backend Redisson (không cần khai version vì đã được quản lý qua BOM):
<dependency>
<groupId>vn.govex.cloud</groupId>
<artifactId>govex-cloud-lock</artifactId>
</dependency>
<dependency>
<groupId>vn.govex.cloud</groupId>
<artifactId>govex-cloud-redisson</artifactId>
</dependency>
Version do BOM vn.govex.cloud:dependencies quản lý — xem Cài đặt.
Cấu hình
Cả hai tính năng mặc định tắt — annotation chưa có tác dụng cho tới khi bật và có backend tương ứng:
| Property | Mô tả | Mặc định |
|---|---|---|
govex.lock.enabled | Bật tính năng khóa phân tán (LockTemplate và annotation @Lock). | false |
govex.lock.type | Loại LockExecutor sử dụng; đặt redisson để dùng backend Redisson. | — |
govex.lock.delimiter | Ký tự phân tách khi build lock key. | : |
govex.lock.expire | Thời gian hết hạn của khóa (ms). | 30000 |
govex.lock.acquire-timeout | Thời gian chờ tối đa để lấy khóa (ms). | 3000 |
govex.lock.retry-interval | Khoảng nghỉ giữa các lần thử lấy khóa (ms). | 100 |
govex.lock.primary-executor | Class LockExecutor mặc định; bỏ trống thì lấy executor đầu tiên. | executor đầu tiên |
govex.lock.lock-key-prefix | Tiền tố lock key. | lock: |
govex.ratelimit.enabled | Bật tính năng giới hạn tần suất request (annotation @RateLimit). | false |
govex.ratelimit.type | Loại RateLimiter sử dụng; đặt redisson để dùng backend Redisson. | — |
govex.ratelimit.prefix | Tiền tố ghép vào ratelimit key. | rỗng |
govex.ratelimit.delimiter | Ký tự phân tách khi build ratelimit key. | : |
govex.ratelimit.limit | Số permit tối đa trong một cửa sổ; dùng khi annotation không khai báo limit. | 60 |
govex.ratelimit.duration | Độ dài cửa sổ thời gian (theo time unit của annotation). | 10 |
govex.ratelimit.retry-time | Thời gian chờ tối đa thêm để lấy permit; 0 nghĩa là không chờ. | 0 |
Backend Redisson cho @Lock/@RateLimit
Backend Redisson nằm ở govex-cloud-redisson, dùng một RedissonClient chung cho cả khoá phân tán, giới hạn tần suất và external cache. RedissonClient được tạo từ redisson-spring-boot-starter theo cấu hình spring.data.redis.*:
spring:
data:
redis:
host: localhost
port: 6379
govex:
lock:
enabled: true
type: redisson
ratelimit:
enabled: true
type: redisson
RedissonLockExecutorchỉ được tạo khigovex.lock.enabled=truevàgovex.lock.type=redisson;RateLimitertương tự khigovex.ratelimit.enabled=truevàgovex.ratelimit.type=redisson.- Cần Redis đang chạy; dependency nạp thêm file
config/redisson.ymltrên classpath như property source bổ sung nếu cần tinh chỉnhRedissonClient. - Bean
redisTemplatechỉ được tạo khi ứng dụng chưa có bean cùng tên.
Sử dụng
Bước 1 — bật tính năng và chọn backend:
govex:
lock:
enabled: true
type: redisson
ratelimit:
enabled: true
type: redisson
limit: 60
duration: 10
Bước 2 — khóa phân tán bằng annotation. Key được build từ lock-key-prefix + delimiter + name (hoặc tên class/method) + các tham số SpEL trong keys:
@Lock(keys = "#id")
public void rebuildIndex(String id) {
// ...
}
Ví dụ thực tế cho tác vụ cron — tránh chạy trùng giữa các instance:
@Trace
@Lock
@Scheduled(cron = "0 0 0 * * ?")
public void execute() {
// ...
}
Bước 3 — giới hạn tần suất request; mặc định đếm theo toàn hệ thống, dùng limitType để đếm theo IP:
@RateLimit(name = "send-otp", limit = 5, duration = 60,
unit = TimeUnit.SECONDS, limitType = RateLimit.LimitType.IP)
public void sendOtp(String phone) {
// ...
}
Bước 4 — khóa thủ công bằng LockTemplate khi không dùng annotation:
lockTemplate.execute("job:daily", () -> {
doDailyJob();
return null;
});
lockTemplate.executeWithoutResult("job:daily", this::doDailyJob);
Lưu ý
- Phải có ít nhất một bean
LockExecutor/RateLimitertrong context. Bậtgovex.lock.enabledmà không có executor sẽ lỗi ngay lúc khởi tạo (executors must have at least one). - Với
govex-cloud-redisson, cầnRedissonClienttrong context và đặtgovex.lock.type=redisson/govex.ratelimit.type=redisson. - Khi không lấy được khóa, chiến lược mặc định (
DefaultLockFailureStrategy) némLockException. - Annotation hoạt động qua AOP proxy: gọi method có annotation từ một method khác trong cùng class (self-invocation) sẽ không được chặn — dùng
LockTemplatecho trường hợp này. - Giá trị
expire/acquireTimeoutbằng0trên annotation nghĩa là lấy giá trị mặc định từ propertygovex.lock.*. - Toàn bộ trạng thái khóa/ratelimit nằm ở backend của implementation.