Кэширование
Модуль для создания кешей на основе Caffeine или Redis с помощью аннотаций в декларативном стиле, так и использование их императивном стиле.
Caffeine¶
Реализация на основе библиотеки Caffeine для кэша внутри памяти приложения.
Подключение¶
Зависимость build.gradle:
Модуль:
Зависимость build.gradle.kts:
Модуль:
Конфигурация¶
Пример полной конфигурации для mycache.config кэша, параметры описаны в классе CaffeineCacheConfig (указаны примеры значений или значения по умолчанию):
mycache {
config {
expireAfterWrite = "10s" //(1)!
expireAfterAccess = "10s" //(2)!
initialSize = 10 //(3)!
maximumSize = 100000 //(4)!
}
}
- Время по истечении которого значение для ключа будет удалено, отчитывается после добавления значения (необязательно)
- Время по истечении которого значение для ключа будет удалено, отчитывается после операции чтения (необязательно)
- Начальный размер кэша (помогает избежать расширения кэша в случае активного набухания) (необязательно)
- Максимальный размер кэша (При достижении границы или чуть ранее будет исключать из кэша наименее актуальные значения) (по умолчанию
100000)
mycache:
config:
expireAfterWrite: "10s" #(1)!
expireAfterAccess: "10s" #(2)!
initialSize: 10 #(3)!
maximumSize: 100000 #(4)!
- Время по истечении которого значение для ключа будет удалено, отчитывается после добавления значения (необязательно)
- Время по истечении которого значение для ключа будет удалено, отчитывается после операции чтения (необязательно)
- Начальный размер кэша (помогает избежать расширения кэша в случае активного набухания) (необязательно)
- Максимальный размер кэша (При достижении границы или чуть ранее будет исключать из кэша наименее актуальные значения) (по умолчанию
100000)
Redis¶
Реализация на основе базы данных в памяти Redis и драйвера подключения Lettuce.
Подключение¶
Зависимость build.gradle:
Модуль:
Зависимость build.gradle.kts:
Модуль:
Конфигурация¶
Требуется отдельно сконфигурировать Lettuce драйвер для подключения к Redis. Используется одно подключение для всех кешей.
Пример полной конфигурации для lettuce драйвера, параметры описаны в классе LettuceConfig (указаны примеры значений или значения по умолчанию):
lettuce {
uri = "redis://locahost:6379" //(1)!
user = "admin" //(2)!
password = "12345" //(3)!
database = 0 //(4)!
protocol = "RESP3" //(5)!
socketTimeout = "10s" //(6)!
commandTimeout = "60s" //(7)!
forceCluster = "false" //(8)!
ssl {
ciphers = [ "TLS_CHACHA20_POLY1305_SHA256" ] //(9)!
handshakeTimeout = "10s" //(10)!
}
}
- URI для подключения к Redis (обязательный)
Подключение для 1 сервера:
redis://locahost:6379, Подключение для N серверов:redis://locahost:6379,locahost:6380, Подключение для c SSL:rediss://locahost:6380Подключение для c TLS:redis+tls://locahost:6380 - Имя пользователя для подключения (необязательно)
- Пароль пользователя для подключения (необязательно)
- Номер базы для подключения (необязательно)
- Протокол для подключения (необязательно)
- Таймаут времени подключения (необязательно)
- Таймаут времени выполнения команды (необязательно)
- Форсировать кластерное подключение даже если указан 1 URI для подключения (необязательно)
- Алгоритмы шифрования, используемые для безопасного соединения между клиентом и сервером (необязательно)
- Таймаут времени установки безопасного соединения с сервером (необязательно)
lettuce:
uri: "redis://locahost:6379" #(1)!
user: "admin" #(2)!
password: "12345" #(3)!
database: 0 #(4)!
protocol: "RESP3" #(5)!
socketTimeout: "10s" #(6)!
commandTimeout: "60s" #(7)!
forceCluster: false #(8)!
ssl:
ciphers:
- "TLS_CHACHA20_POLY1305_SHA256" #(9)!
handshakeTimeout: "10s" #(10)!
- URI для подключения к Redis (обязательный)
Подключение для 1 сервера:
redis://locahost:6379, Подключение для N серверов:redis://locahost:6379,locahost:6380, Подключение для c SSL:rediss://locahost:6380Подключение для c TLS:redis+tls://locahost:6380 - Имя пользователя для подключения (необязательно)
- Пароль пользователя для подключения (необязательно)
- Номер базы для подключения (необязательно)
- Протокол для подключения (необязательно)
- Таймаут времени подключения (необязательно)
- Таймаут времени выполнения команды (необязательно)
- Форсировать кластерное подключение даже если указан 1 URI для подключения (необязательно)
- Алгоритмы шифрования, используемые для безопасного соединения между клиентом и сервером (необязательно)
- Таймаут времени установки безопасного соединения с сервером (необязательно)
Конфигурации Redis кэша настраивает именно поведение конкретного кэша.
Пример полной конфигурации для mycache.config кэша, параметры описаны в классе RedisCacheConfig (указаны примеры значений):
mycache {
config {
expireAfterWrite = "10s" //(1)!
expireAfterAccess = "10s" //(2)!
keyPrefix = "mykey" //(3)!
}
}
- При записи устанавливает время expiration
- При чтении устанавливает время expiration
- Префикс ключа в определенном кеше для избежания коллизий ключе в рамках Redis базы данных, может быть пустой строкой тогда ключи будут без префикса (обязательный)
mycache:
config:
expireAfterWrite: "10s" #(1)!
expireAfterAccess: "10s" #(2)!
keyPrefix: "mykey" //(3)!
- При записи устанавливает время expiration
- При чтении устанавливает время expiration
- Префикс ключа в определенном кеше для избежания коллизий ключе в рамках Redis базы данных, может быть пустой строкой тогда ключи будут без префикса (обязательный)
Донастройка¶
Можно зарегистрировать LettuceConfigurator который позволит до настроить Lettuce клиент перед созданием.
@Component
public final class MyLettuceConfigurator implements LettuceConfigurator {
@Override
public DefaultClientResources.Builder configure(DefaultClientResources.Builder resouceBuilder) {
return resouceBuilder;
}
@Override
public ClusterClientOptions.Builder configure(ClusterClientOptions.Builder clusterBuilder) {
return clusterBuilder;
}
@Override
public ClientOptions.Builder configure(ClientOptions.Builder clientBuilder) {
return clientBuilder;
}
}
class MyLettuceConfigurator : LettuceConfigurator {
override fun configure(resouceBuilder: DefaultClientResources.Builder): DefaultClientResources.Builder {
return resouceBuilder
}
override fun configure(clusterBuilder: ClusterClientOptions.Builder): ClusterClientOptions.Builder {
return clusterBuilder
}
override fun configure(clientBuilder: ClientOptions.Builder): ClientOptions.Builder {
return clientBuilder
}
}
Использование¶
Для создания кеша потребуется зарегистрировать типизированный @Cache контракт.
Интерфейс контракта должен наследоваться только от предоставляемых Kora'ой реализаций: CaffeineCache / RedisCache.
Для такого @Cache будет создана и добавлена в граф реализация, ее можно будет использовать для внедрения зависимостей.
Для регистрации @Cache и указания конфига требуется проаннотировать аннотацией @Cache где аргумент value означает полный путь к конфига.
Императивный подход¶
Кеши доступны для внедрения как зависимости по интерфейсу и могут использовать вкупе с декларативными операциями.
Реализация CaffeineCache предоставляет базовые контракты интерфейса Cache для синхронных операций,
а RedisCache предоставляет как Cache так и AsyncCache для асинхронных операций с CompletionStage сигнатурами.
Интерфейсы предоставляют операции получения, удаления, обновления, пакетных операций и тп. Также реализации кешей могут предоставлять специфичные для себя контракты.
Декларативный подход¶
Все примеры использования аспектов будут подразумевать реализацию кэша выше.
Получение¶
Для кэширования и получения значения из кэша для метода get() следует проаннотировать его аннотацией @Cacheable.
Ключ для кэша составляет из аргументов метода, порядок аргументов имеет значение, в данном случае он будет составляться из значения arg1.
Сохранение¶
Для добавления значений в кэш через метод put() следует проаннотировать его аннотацией @CachePut.
Метод проаннотированный @CachePut будет вызван и его значение положено в кэш определенный в value.
Ключ для кэша составляет из аргументов метода, порядок аргументов имеет значение, в данном случае он будет составляться из значения arg1.
Удаление¶
Для удаления значения по ключу из кэша через метод evict() следует проаннотировать его аннотацией @CacheInvalidate.
Метод проаннотированный @CacheInvalidate будет вызван и затем по ключу для кэша определенного в value будут удалены значения по ключу.
Ключ для кэша составляет из аргументов метода, порядок аргументов имеет значение, в данном случае он будет составляться из значения arg1.
Полное удаление¶
Для удаления всех значений из кэша через метод evictAll() следует проаннотировать его аннотацией @CacheInvalidate и указать параметр invalidateAll = true.
Метод проаннотированный @CacheInvalidate будет вызван и затем будут удалены все из кэша определенного в value.
Композитный кэш¶
В случае если у вас есть несколько кешей то требуется подключить оба модуля и указать соответствующее количество аннотаций над методом.
А сам проаннотированный класс так:
Порядок вызова аспектов соответствует порядку аннотаций над методом, сверху внизу.
Ключ¶
В случае если ключ кэша представляет собой 1 аргумент, то требуется зарегистрировать Cache с сигнатурой соответствующей типам ключа и значения.
Преобразование¶
В случае если аргумент не может быть преобразован в ключ кеша, то реализация кеша затребует соответствующий преобразователь
с интерфейсом CacheKeyMapper, в случае если аргументов для ключа будет 2 то потребуется CacheKeyMapper2 и так далее.
Такой преобразователь можно также предоставить вручную с помощью аннотации @Mapping,
пример преобразования сложного объекта в простой ключ кеша:
@Component
public class SomeService {
public record UserContext(String userId, String traceId) { }
public static final class UserContextMapping implements CacheKeyMapper<String, UserContext> {
@Nonnull
@Override
public String map(UserContext arg) {
return arg.userId();
}
}
@Mapping(UserContextMapping.class)
@Cacheable(MyCache.class)
public String get(UserContext context) {
// do something
}
}
Композитный ключ¶
В случае если ключ кэша представляет собой N аргументов, то требуется зарегистрировать Cache с использованием
собственного класса который бы описывал такой ключ.
Пример для Cache где композитный ключ состоит из 2 элементов:
Предполагается создавать собственный record класс который бы описывал композитный ключ.
Если используется RedisCache то подразумевается что по умолчанию все аргументы композитного ключа будут не null,
либо потребуется использовать собственный преобразователь ключа.
Порядок аргументов¶
В случае если метод принимает аргументы которые хочется исключить из композитного ключа,
либо же порядок аргументов не соответствует порядку аргументов конструктора композитного ключа,
следует использовать атрибут аннотации parameters и определить какие именно аргументы метода использовать и в каком порядке.
Подгружаемый кэш¶
Библиотека предоставляет компонент для построения сущности, которая объединяет операции GET и PUT, без использования аспектов - LoadableCache
@Cache("mycache.config")
public interface MyCache extends CaffeineCache<String, String> { }
@KoraApp
public interface Application : CaffeineCacheModule {
default LoadableCache<String, String> loadableCache(MyCache cache, SomeService someService) {
return cache.asLoadable(someService::loadEntity);
}
}
Сигнатуры¶
Доступные сигнатуры для методов которые поддерживают аннотации из коробки:
Класс не должен быть final, чтобы аспекты работали.
Под T подразумевается тип возвращаемого значения.
T myMethod()Optional<T> myMethod()CompletionStage<T> myMethod()CompletionStageMono<T> myMethod()Project Reactor (надо подключить зависимость)
Класс должен быть open, чтобы аспекты работали.
Под T подразумевается тип возвращаемого значения, либо T?, либо Unit.
myMethod(): Tsuspend myMethod(): TKotlin Coroutine (надо подключить зависимость какimplementation)