В течение десятилетий разработчики и дизайнеры пытались оптимизировать производительность систем и услуг, которые они предоставляют:
- Операционные системы в поисках самого быстрого времени запуска.
- Браузеры пытаются превзойти своих конкурентов.
- API делают свою работу максимально быстро.
Помимо использования большей вычислительной мощности и памяти для решения или оптимизации дизайна исходного кода. Использование некоторой формы кэша часто вводится для дополнительного увеличения скорости. Чем больше реализовано кэширование, тем выше вероятность того, что эти кэши нужно очистить или удалить.
Вам также может понравиться: Кэширование в Java с политикой исключения LRU
В этой статье я расскажу об одном подходе, который обеспечит столь необходимое повышение производительности, не беспокоясь об устаревании кэша.
Сценарий шрифта
В качестве примера рассмотрим работу в издательской индустрии — создание клиента, который даст конечным пользователям возможность создавать публикуемый контент. Чтобы клиенты могли выполнять свою работу, они будут полагаться на шрифты, которые контролируются приложением.
В зависимости от конкретного проекта список доступных шрифтов будет различаться. В этом случае давайте предположим, что с каждым используемым шрифтом связаны определенные затраты.
Несмотря на то, что список шрифтов часто остается статичным после настройки проекта, вероятность того, что каждый проект увидит изменение в списке шрифтов, составляет 20%, что может произойти в любой момент в течение жизненного цикла проекта.
Зная, что полезная нагрузка, связанная с информацией о шрифтах, очень статична, эти данные стали очевидным выбором для кеширования в API.
Вдохновение Рассела
Говоря об этой ситуации с моим коллегой Расселом (« Я хочу, чтобы мой код был скучным »), мы добрались до ситуации, когда потребуется устаревший кэш для заданного набора шрифтов, используемых в проекте. Стратегия кеширования, которую мы использовали в Spring Boot, была Ehcache, а концепция удаления кеша была тем подходом, который я планировал использовать.
К тому времени, когда я говорил с Расселом, уже были элементы кэша для заданной полезной нагрузки шрифта по уникальному идентификатору, связанному с каждым шрифтом. Я уже кешировал реальные двоичные файлы шрифтов, которые нужно было распространять каждому клиенту, использующему приложение. В обоих случаях влияние на время отклика было весьма впечатляющим — сокращение от 500 миллисекунд до 10 миллисекунд.
В ранних тестах количество вызовов шрифтов по вызовам API проекта уменьшилось с 1700 миллисекунд до <20 миллисекунд, поскольку требуемые данные подавались непосредственно из кэша. Кэш, который нужно было удалить, как только конфигурация проекта была обновлена.
Рассел сформулировал идею: «что произойдет, если мы не кэшируем шрифты с помощью API проекта, но используем ли этот API вместо существующих кэшей?»
Использование существующего кэша для избежания управления кэшем
Используемый подход иллюстрируется в упрощенном примере исходного кода ниже:
Джава
1
List<Font> fonts = new ArrayList<>();
2
List<Long> fontIds = fontRepository.getFontIdByProjectId(projectId);
3
if (CollectionUtils.isNotEmpty(fontIds)) {
5
Cache cache;
6
for (Long id : fontIds) {
8
cache = cacheManager.getCache("fontById");
9
Cache.ValueWrapper valueWrapper = cache.get(id);
10
if (valueWrapper != null) {
12
log.debug("Using cache id={} in fontById cache", id);
13
fonts.add((Font) valueWrapper.get());
14
} else {
15
Font font = getFontById(projectId, id); // Get font not in cache
16
log.debug("Could not locate id={} in fontById cache, adding {}", id, font);
17
cache.put(id, font); // Add font into existing cache
18
fonts.add(font);
19
}
20
}
21
}
22
return fonts;
При запуске шрифтов по методу проекта список текущих идентификаторов шрифтов, связанных с проектом, возвращается в виде List <Long>. В этот момент каждый идентификатор используется для проверки кэша «fontById», чтобы найти совпадение. Если совпадение найдено, статический кеш шрифта просто добавляется в список <Font>, который будет возвращен клиенту.
Если совпадение не найдено, шрифт извлекается с использованием того же метода, который заполняет этот кеш, и полученные данные добавляются в кеш - готовый для использования в будущем. Конечно, эти вновь полученные данные шрифта добавляются в список <Font>, который также будет возвращен клиенту.
При таком подходе производительность была не в диапазоне <20 миллисекунд, а в диапазоне <35 миллисекунд - все еще значительное улучшение по сравнению с исходными 1700 миллисекундами (улучшение на 98%).
Кроме того, следующие преимущества также признаются здесь:
- Необходимость создания дополнительного кэша исключается.
- Нет необходимости вводить прослушиватель сущностей и / или пользовательский код для удаления (или очистки) кэша.
- Подход очень поддерживается - избегая использования аспектно-ориентированного программирования (AOP).
Заключение
В моей статье « Три вещи, которые мы делаем в ИТ, которые не соответствуют действительности », я говорил о том, как изменение вашего шаблона мышления может привести к повышению производительности во время отклика приложения. Подход, о котором говорилось, заключался в том, чтобы заглянуть в будущее и извлечь всю необходимую информацию до запуска логики программы. Затем, используя некоторую форму сбора, данные, необходимые для цикла обработки, уже будут доступны - без необходимости дальнейшего запроса к базе данных.
Этот подход кажется прямо противоположным, с подходом, который запрашивает данные на лету, когда известен список идентификаторов. В этом случае также наблюдается повышение производительности, поскольку реальность такова, что большая часть этой информации уже будет в кеше и приведет к уменьшению времени отклика с одной до двух секунд до десяти-двадцати миллисекунд.
В обоих случаях два совершенно разных подхода в конечном итоге приводят к повышению производительности приложения.
Я продолжаю ценить мои разговоры с Расселом, поскольку его подход обеспечивает положительные результаты для API, сохраняя при этом минимальные затраты на поддержку решения.
Хорошего дня!
Дальнейшее чтение
Spring: удовлетворение нескольких запросов с помощью Singleton Bean