1. Обзор
В этой статье мы познакомимся с Ehcache , широко используемым кэшем с открытым исходным кодом на основе Java. Он включает в себя хранилища памяти и диска, прослушиватели, загрузчики кеша, API-интерфейсы RESTful и SOAP и другие очень полезные функции.
Чтобы показать, как кэширование может оптимизировать наше приложение, мы создадим простой метод, который будет вычислять квадратные значения предоставленных чисел. При каждом вызове метод вызывает метод calculateSquareOfNumber(int number)
и выводит информационное сообщение на консоль.
На этом простом примере мы хотим показать, что вычисление квадратов значений выполняется только один раз, и каждый второй вызов с одним и тем же входным значением возвращает результат из кеша.
Важно отметить, что мы полностью сосредоточены на самом Ehcache (без Spring); если вы хотите увидеть, как Ehcache работает со Spring, прочтите эту статью .
2. Зависимости Maven
Чтобы использовать Ehcache, нам нужно добавить эту зависимость Maven:
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.1.3</version>
</dependency>
Последнюю версию артефакта Ehcache можно найти здесь .
3. Конфигурация кэша
Ehcache можно настроить двумя способами:
- Первый способ — через Java POJO, где все параметры конфигурации настраиваются через Ehcache API.
- Второй способ — настройка через файл XML, где мы можем настроить Ehcache в соответствии с предоставленным определением схемы .
В этой статье мы покажем оба подхода — конфигурацию Java и XML.
3.1. Конфигурация Java
В этом подразделе показано, как легко настроить Ehcache с помощью POJO. Кроме того, мы создадим вспомогательный класс для упрощения настройки и доступности кеша:
public class CacheHelper {
private CacheManager cacheManager;
private Cache<Integer, Integer> squareNumberCache;
public CacheHelper() {
cacheManager = CacheManagerBuilder
.newCacheManagerBuilder().build();
cacheManager.init();
squareNumberCache = cacheManager
.createCache("squaredNumber", CacheConfigurationBuilder
.newCacheConfigurationBuilder(
Integer.class, Integer.class,
ResourcePoolsBuilder.heap(10)));
}
public Cache<Integer, Integer> getSquareNumberCacheFromCacheManager() {
return cacheManager.getCache("squaredNumber", Integer.class, Integer.class);
}
// standard getters and setters
}
Чтобы инициализировать наш кеш, сначала нам нужно определить объект Ehcache CacheManager
. В этом примере мы создаем «квадратный номер»
кеша по умолчанию с помощью API newCacheManagerBuilder()
.
Кэш просто сопоставит целочисленные
ключи с целочисленными
значениями.
Обратите внимание, что перед тем, как мы начнем использовать определенный кеш, нам нужно инициализировать объект CacheManager
с помощью метода init() .
Наконец, чтобы получить наш кеш, мы можем просто использовать API getCache()
с предоставленным именем, ключом и типами значений нашего кеша.
С помощью этих нескольких строк мы создали наш первый кеш, который теперь доступен нашему приложению.
3.2. XML-конфигурация
Объект конфигурации из подраздела 3.1. эквивалентно использованию этой конфигурации XML:
<cache-template name="squaredNumber">
<key-type>java.lang.Integer</key-type>
<value-type>java.lang.Integer</value-type>
<heap unit="entries">10</heap>
</cache-template>
И чтобы включить этот кэш в наше приложение Java, нам нужно прочитать файл конфигурации XML в Java:
URL myUrl = getClass().getResource(xmlFile);
XmlConfiguration xmlConfig = new XmlConfiguration(myUrl);
CacheManager myCacheManager = CacheManagerBuilder
.newCacheManager(xmlConfig);
4. Тест кэша
В разделе 3 мы показали, как вы можете определить простой кэш для своих целей. Чтобы показать, что кеширование действительно работает, мы создадим класс SquaredCalculator
, который будет вычислять квадратное значение предоставленного ввода и сохранять вычисленное значение в кеше.
Конечно, если кеш уже содержит вычисленное значение, мы вернем кешированное значение и избежим лишних вычислений:
public class SquaredCalculator {
private CacheHelper cache;
public int getSquareValueOfNumber(int input) {
if (cache.getSquareNumberCache().containsKey(input)) {
return cache.getSquareNumberCache().get(input);
}
System.out.println("Calculating square value of " + input +
" and caching result.");
int squaredValue = (int) Math.pow(input, 2);
cache.getSquareNumberCache().put(input, squaredValue);
return squaredValue;
}
//standard getters and setters;
}
Для завершения нашего тестового сценария нам также понадобится код, который будет вычислять квадратные значения:
@Test
public void whenCalculatingSquareValueAgain_thenCacheHasAllValues() {
for (int i = 10; i < 15; i++) {
assertFalse(cacheHelper.getSquareNumberCache().containsKey(i));
System.out.println("Square value of " + i + " is: "
+ squaredCalculator.getSquareValueOfNumber(i) + "\n");
}
for (int i = 10; i < 15; i++) {
assertTrue(cacheHelper.getSquareNumberCache().containsKey(i));
System.out.println("Square value of " + i + " is: "
+ squaredCalculator.getSquareValueOfNumber(i) + "\n");
}
}
Если мы запустим наш тест, мы получим этот результат в нашей консоли:
Calculating square value of 10 and caching result.
Square value of 10 is: 100
Calculating square value of 11 and caching result.
Square value of 11 is: 121
Calculating square value of 12 and caching result.
Square value of 12 is: 144
Calculating square value of 13 and caching result.
Square value of 13 is: 169
Calculating square value of 14 and caching result.
Square value of 14 is: 196
Square value of 10 is: 100
Square value of 11 is: 121
Square value of 12 is: 144
Square value of 13 is: 169
Square value of 14 is: 196
Как вы можете заметить, метод calculate()
выполнял вычисления только при первом вызове. При втором вызове все значения были найдены в кеше и возвращены из него.
5. Другие параметры конфигурации Ehcache
Когда мы создали наш кеш в предыдущем примере, это был простой кеш без каких-либо специальных опций. В этом разделе будут показаны другие параметры, полезные при создании кеша.
5.1. Сохранение диска
Если в кэше слишком много значений, мы можем сохранить некоторые из этих значений на жестком диске.
PersistentCacheManager persistentCacheManager =
CacheManagerBuilder.newCacheManagerBuilder()
.with(CacheManagerBuilder.persistence(getStoragePath()
+ File.separator
+ "squaredValue"))
.withCache("persistent-cache", CacheConfigurationBuilder
.newCacheConfigurationBuilder(Integer.class, Integer.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(10, EntryUnit.ENTRIES)
.disk(10, MemoryUnit.MB, true))
)
.build(true);
persistentCacheManager.close();
Вместо CacheManager
по умолчанию мы теперь используем PersistentCacheManager
, который будет сохранять все значения, которые не могут быть сохранены в памяти.
Из конфигурации видно, что кэш сохранит 10 элементов в памяти и выделит 10 МБ на жестком диске для сохранения.
5.2. Срок действия данных
Если мы кэшируем много данных, естественно, что мы сохраняем кэшированные данные в течение некоторого периода времени, чтобы избежать большого использования памяти.
Ehcache контролирует актуальность данных через интерфейс Expiry :
CacheConfiguration<Integer, Integer> cacheConfiguration
= CacheConfigurationBuilder
.newCacheConfigurationBuilder(Integer.class, Integer.class,
ResourcePoolsBuilder.heap(100))
.withExpiry(Expirations.timeToLiveExpiration(Duration.of(60,
TimeUnit.SECONDS))).build();
В этом кеше все данные будут жить в течение 60 секунд, а по истечении этого периода времени они будут удалены из памяти.
6. Заключение
В этой статье мы показали, как использовать простое кэширование Ehcache в приложении Java.
На нашем примере мы увидели, что даже просто настроенный кеш может избавить от множества ненужных операций. Кроме того, мы показали, что можем настраивать кеши с помощью POJO и XML и что Ehcache имеет несколько приятных функций, таких как сохранение и истечение срока действия данных.
Как всегда, код из этой статьи можно найти на GitHub .