В этой статье демонстрируется эффективная методика реализации LoadingCache в службе Java Spring Boot с использованием Google Guava CacheAPI.

Задний план

В моей предыдущей статье я объяснил важность использования кэша, а затем продемонстрировал способ реализации кэша Guava в службе Java Spring Boot.

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

Guava предоставляет еще один простой способ управления кешем через LoadingCache. LoadingCache – это кэш, созданный с помощью присоединенного CacheLoader. Мы настраиваем CacheLoader, указав его на серверную службу, которую затем можно использовать для загрузки значений с помощью KEY, если его еще нет в Cache.

Управляя извлечением и хранением записей самостоятельно, LoadingCache упрощает реализацию всего механизма кэширования. Нам нужно только вызвать метод get(K), и он либо вернет уже кэшированное значение, либо атомарно загрузит новое значение в кэш с помощью CacheLoader кэша.

Пример реализации LoadingCache

Давайте посмотрим, как мы можем реализовать хранилище кэшей с помощью Guava LoadingCache.

В оставшейся части статьи подробно описаны все шаги, необходимые для реализации LoadingCache. Если это будет полезно, вы также можете загрузить окончательный исходный код с Ссылка на GitHub — Реализация Guava LoadingCache.

Примечание.Шаги с 1 по 3 аналогичны описанным в моей предыдущей статье.

Шаг 1: Создайте проект Spring Boot

Нам нужен один проект Spring Boot. Базовый скелет можно быстро создать с помощью https://start.spring.io/, как показано ниже:

Шаг 2. Напишите HTTP GET REST API

Напишите один пример REST API, как описано ниже. В этом API мы моделируем вызов серверной службы, добавляя преднамеренное время ожидания. На следующем шаге мы представим Guava LoadingCache для кэширования ответа API и уменьшения общей задержки.

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

Шаг 3: Добавьте зависимость от гуавы

В файле build.gradle добавьте библиотеку Guava в список зависимостей как,

compile group: 'com.google.guava', name: 'guava', version: '30.0-jre'

Шаг 4. Определите класс для предоставления хранилища LoadingCache для любого универсального типа ‹T›.

Назовем этот класс LoadingCacheStore, что поможет нам построить LoadingCache для любого универсального типа. LoadingCache настроен с помощью CacheLoader, который внутренне использует серверную службу для загрузки значений с использованием ключа кеша, если его еще нет в кеше.

Обратите внимание:

  1. Использование интерфейса ICacheLoaderServiceпредоставляет способ абстрагирования базовой серверной службы, заставляя их реализовывать один общий метод: public T getBackendData(key). Это позволяет повторно использовать Class LoadingCacheStoreдля реализации механизма кэширования для нескольких серверных API, когда это необходимо.
  2. Настроенный CacheLoader значительно упрощает реализацию, устраняя необходимость в явном сохранении пар KEY-Value в кэше.
  3. Как показано позже в шаге № 6: с помощью LoadingCacheStore‹T› мы можем создать несколько Bean-компонентов для реализации Cache для любого необходимого нам количества типов.
  4. expireAfterWrite() предоставляет возможность аннулировать записи кэша в соответствии с предоставленными параметрами.

Шаг 5. Добавьте реализацию в EmployeeService и реализуйте унаследованный метод.

Использование интерфейса ICacheLoaderServiceпредоставляет способ абстрагирования базовой серверной службы, заставляя их реализовывать унаследованный метод: public T getBackendData(key).

Шаг 6: Определите Java Bean для создания нового хранилища LoadingCache с типом записи Employee

Шаг 7: Измените класс ApiController, чтобы реализовать использование LoadingCache для записей сотрудников.

На этом этапе мы будем использовать экземпляр LoadingCacheStore‹Employee›, как если бы мы напрямую использовали внутренний API.

LoadingCacheStore‹Employee› будет управлять всеми шагами, необходимыми для реализации кэша, например:

  1. пропуск вызова внутреннего API, когда запись присутствует в кеше,
  2. вызов внутреннего API, когда запись отсутствует в кеше,
  3. сохранение записи в кэше для будущего использования,
  4. и аннулирование или удаление сохраненной записи в соответствии с конфигурациями хранения в кэше.

Обратите внимание:

  1. Для первого запроса запись о сотруднике отсутствует в кеше. Следовательно, LoadingCacheStore‹Employee›будет вызывать внутренний API для получения записи о сотруднике. (Здесь внутренний API имеет преднамеренную задержку для имитации HTTP-вызова по сети.)
  2. Извлеченная запись сотрудника будет сначала сохранена в экземпляре Cache, а затем возвращена потребителю.
  3. Для последующих запросов LoadingCacheStore‹Employee› найдет запись Employee в кэше. LoadingCache немедленно вернет кэшированную запись сотрудника в ответе, пропустив вызов внутреннего API.
  4. На шаге №6 мы создали Cache-Store со сроком действия 120 секунд. Поэтому LoadingCache будет хранить запись о сотруднике только в течение 120 секунд. Guava Cache будет отслеживать время и аннулировать/удалять запись о сотруднике из кеша самостоятельно через 120 секунд.

Шаг 8: Демонстрация

Создайте и запустите приложение Api на локальном компьютере. Чтобы проверить кэширование, перейдите по URL-адресу:

http://localhost:8080/employee/1

Поскольку это будет первый вызов, ключ сотрудника «1» не будет найден в кэше. Поэтому LoadingCache вызовет вызов внутренней службы, и мы увидим некоторую задержку в ответе.

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

Затем проверьте идентификатор другого сотрудника:

http://localhost:8080/employee/2

Этот новый ключ сотрудника «2» отсутствует в кэше. Следовательно, LoadingCache вызовет внутреннюю службу для получения сведений.

См. последовательные журналы ниже для всех 3 тестов:

Шаг 9. Повторное использование класса LoadingCacheStore‹T› для создания другого LoadingCache с другим типом записи

После того, как мы выполним шаги с 1 по 8, у нас будет возможность быстро добавить новое хранилище кэша с любым количеством типов записей.

Обратитесь к приведенному ниже примеру, чтобы узнать, какие изменения необходимы для создания любого нового Cache-Store:

Обратите внимание: здесь Product.java и ProductService.Java соответствуют тому же шаблону, что и ранее предоставленный Employee.Java. и EmployeeService.Java. Если вы заинтересованы, вы можете загрузить полный исходный код с GitHub Link — Implementing Guava LoadingCache.

9a. Добавьте реализацию ICacheLoaderService‹T› для любого серверного API, для которого вы хотите реализовать функции кэширования.

Обратите внимание: метод getBackendData(KEY) здесь извлекает значительную запись из серверного API, но возвращает только усеченную запись, необходимую для нашего кэша. Это позволяет избежать ненужной перегрузки памяти сервера приложений.

9b. Определите Bean-компонент для нового LoadingCache, используя требуемый внутренний API-интерфейс: например. LoadingCacheStore‹String›.

9c. Используйте только что определенный LoadingCacheStore‹String› productNameLoadingCache для извлечения значений.

Резюме

В этой статье мы увидели эффективную технику реализации Guava LoadingCache в Spring Boot. LoadingCache абстрагирует большинство функций управления Cache, таких как извлечение, загрузка и аннулирование, что значительно упрощает реализацию с точки зрения потребителя Cache.

Загрузите полный исходный код для этой статьи по адресу Ссылка на GitHub — Реализация Guava LoadingCache.

Удачного кодирования!