Рубрики
Без рубрики

Введение в cacheck

Узнайте, как использовать cache2k, легкую библиотеку кэширования Java в памяти.

Автор оригинала: Kamlesh Kumar.

1. Обзор

В этом уроке мы рассмотрим cache2k — легкую, высокопроизводительную библиотеку кэширования Java в памяти.

2. О cache2k

Библиотека cache2k обеспечивает быстрое время доступа благодаря неблокирующему и свободному от ожидания доступу к кэшированным значениям. Он также поддерживает интеграцию с Spring Framework, Scala Cache, Datanucleus и Hibernate.

Библиотека поставляется со многими функциями, включая набор потокобезопасных атомарных операций , загрузчик кэша с блокировкой |/чтения , автоматическое истечение срока действия , обновление вперед, прослушиватели событий и поддержку реализации JCache API JSR107. Мы обсудим некоторые из этих функций в этом уроке.

Важно отметить, что cache2k не является распределенным решением для кэширования, таким как Infispan или Hazelcast .

3. Зависимость Maven

Чтобы использовать cache2k, нам нужно сначала добавить cache 2k-base-bom зависимость в ваш pom.xml :


    org.cache2k
    cache2k-base-bom
    1.2.3.Final
    pom

4. Простой пример cache2k

Теперь давайте посмотрим, как мы можем использовать cache2k в Java-приложении с помощью простого примера.

Давайте рассмотрим пример сайта онлайн-покупок. Предположим, что веб-сайт предлагает двадцатипроцентную скидку на все спортивные товары и десятипроцентную скидку на другие товары. Наша цель здесь-кэшировать скидку, чтобы мы не вычисляли ее каждый раз.

Итак, сначала мы создадим класс ProductHelper и создадим простую реализацию кэша:

public class ProductHelper {

    private Cache cachedDiscounts;
    private int cacheMissCount = 0;

    public ProductHelper() {
        cachedDiscounts = Cache2kBuilder.of(String.class, Integer.class)
          .name("discount")
          .eternal(true)
          .entryCapacity(100)
          .build();
    }

    public Integer getDiscount(String productType) {
        Integer discount = cachedDiscounts.get(productType);
        if (Objects.isNull(discount)) {
            cacheMissCount++;
            discount = "Sports".equalsIgnoreCase(productType) ? 20 : 10;
            cachedDiscounts.put(productType, discount);
        }
        return discount;
    }

    // Getters and setters

}

Как мы видим, мы использовали переменную cache Miss Count для подсчета количества раз, когда скидка не найдена в кэше. Таким образом, если метод GetDiscount использует кэш для получения скидки, количество пропусков кэша не изменится.

Затем мы напишем тестовый случай и проверим нашу реализацию:

@Test
public void whenInvokedGetDiscountTwice_thenGetItFromCache() {
    ProductHelper productHelper = new ProductHelper();
    assertTrue(productHelper.getCacheMissCount() == 0);
    
    assertTrue(productHelper.getDiscount("Sports") == 20);
    assertTrue(productHelper.getDiscount("Sports") == 20);
    
    assertTrue(productHelper.getCacheMissCount() == 1);
}

Наконец, давайте быстро взглянем на конфигурации, которые мы использовали.

Первый-это метод name , который задает уникальное имя нашего кэша . Имя кэша является необязательным и генерируется, если мы его не предоставляем.

Затем мы установили eternal в true , чтобы указать, что кэшированные значения не истекают со временем. Таким образом, в этом случае мы можем выбрать явное удаление элементов из кэша. В противном случае элементы будут автоматически удалены, как только кэш достигнет своей емкости.

Кроме того, мы использовали метод entry Capacity , чтобы указать максимальное количество записей , хранящихся в кэше. Когда кэш достигнет максимального размера, алгоритм удаления кэша удалит одну или несколько записей для поддержания заданной емкости.

Мы можем дополнительно изучить другие доступные конфигурации в классе Cachebuilder .

5. Особенности cache2k

Теперь давайте улучшим наш пример, чтобы изучить некоторые функции кэша 2k.

5.1. Настройка истечения срока действия Кэша

До сих пор мы разрешали фиксированную скидку на все спортивные товары. Однако наш веб-сайт теперь хочет, чтобы скидка была доступна только в течение фиксированного периода времени.

Чтобы выполнить это новое требование, мы настроим срок действия кэша с помощью метода expireAfterWrite :

cachedDiscounts = Cache2kBuilder.of(String.class, Integer.class)
  // other configurations
  .expireAfterWrite(10, TimeUnit.MILLISECONDS)
  .build();

Теперь давайте напишем тестовый случай, чтобы проверить срок действия кэша:

@Test
public void whenInvokedGetDiscountAfterExpiration_thenDiscountCalculatedAgain() 
  throws InterruptedException {
    ProductHelper productHelper = new ProductHelper();
    assertTrue(productHelper.getCacheMissCount() == 0);
    assertTrue(productHelper.getDiscount("Sports") == 20);
    assertTrue(productHelper.getCacheMissCount() == 1);

    Thread.sleep(20);

    assertTrue(productHelper.getDiscount("Sports") == 20);
    assertTrue(productHelper.getCacheMissCount() == 2);
}

В нашем тестовом примере мы попытались снова получить скидку после истечения заданного срока действия. Мы видим, что в отличие от нашего предыдущего примера, количество пропусков кэша было увеличено. Это связано с тем, что срок действия элемента в кэше истек, и скидка рассчитывается заново.

Для расширенной конфигурации истечения срока действия кэша мы также можем настроить политику истечения срока действия .

5.2. Загрузка или чтение кэша

В нашем примере мы использовали шаблон “Кэш в сторону” для загрузки кэша. Это означает, что мы рассчитали и добавили скидку в кэш по требованию в методе GetDiscount .

В качестве альтернативы мы можем просто использовать поддержку кэша 2k для операции чтения . В этой операции кэш загрузит отсутствующее значение самостоятельно с помощью загрузчика . Это также известно как загрузка кэша.

Теперь давайте еще больше расширим наш пример, чтобы автоматически вычислить и загрузить кэш:

cachedDiscounts = Cache2kBuilder.of(String.class, Integer.class)
  // other configurations
  .loader((key) -> {
      cacheMissCount++;
      return "Sports".equalsIgnoreCase(key) ? 20 : 10;
  })
  .build();

Кроме того, мы удалим логику расчета и обновления скидки из get Discount :

public Integer getDiscount(String productType) {
    return cachedDiscounts.get(productType);
}

После этого давайте напишем тестовый пример, чтобы убедиться, что загрузчик работает должным образом:

@Test
public void whenInvokedGetDiscount_thenPopulateCacheUsingLoader() {
    ProductHelper productHelper = new ProductHelper();
    assertTrue(productHelper.getCacheMissCount() == 0);

    assertTrue(productHelper.getDiscount("Sports") == 20);
    assertTrue(productHelper.getCacheMissCount() == 1);

    assertTrue(productHelper.getDiscount("Electronics") == 10);
    assertTrue(productHelper.getCacheMissCount() == 2);
}

5.3. Прослушиватели событий

Мы также можем настроить прослушиватели событий для различных операций кэша, таких как вставка, обновление, удаление и истечение срока действия элементов кэша.

Предположим, мы хотим зарегистрировать все записи, добавленные в кэш. Итак, давайте добавим конфигурацию прослушивателя событий в построитель кэша:

.addListener(new CacheEntryCreatedListener() {
    @Override
    public void onEntryCreated(Cache cache, CacheEntry entry) {
        LOGGER.info("Entry created: [{}, {}].", entry.getKey(), entry.getValue());
    }
})

Теперь мы можем выполнить любой из созданных нами тестовых случаев и проверить журнал:

Entry created: [Sports, 20].

Важно отметить, что прослушиватели событий выполняются синхронно, за исключением событий истечения срока действия . Если нам нужен асинхронный прослушиватель, мы можем использовать метод addAsyncListener .

5.4. Атомарные операции

Класс Cache имеет множество методов, поддерживающих атомарные операции. Эти методы предназначены только для операций с одной записью.

К числу таких методов относятся contains И Remove , /putIfAbsent , | remove If Equals , /replaceIfEquals , peek And Replace и peekAndPut .

6. Заключение

В этом уроке мы рассмотрели библиотеку cache 2k и некоторые ее полезные функции. Мы можем обратиться к руководству пользователя cache 2k для дальнейшего изучения библиотеки.

Как всегда, полный код для этого урока доступен на GitHub .