Реактивное кэширование Spring Boot

В моем приложении я использую Spring WebFlux, и я использую веб-клиент для получения сведений из какого-либо стороннего API. Теперь я хочу сохранить первый ответ веб-клиента в каком-то кеше памяти, чтобы во второй раз я мог получить этот ответ непосредственно из кеша. Я пытаюсь использовать Spring boot в механизме кэширования памяти, а также «caffine». Но никто не работает так, как ожидалось. приложение.yml:

spring:
 cache:
  cache-names: employee
 caffiene:
  spec: maximumSize=200, expireAfterAccess=5m

Приложение Сотрудника.java:

@SpringBootApplication
@EnableCaching
public class EmployeeApplication{
   public static void main(String[] args){
    
}
}

СотрудникКонтроллер.java: У него есть конечная точка отдыха employee/all, которая извлекает всех сотрудников из стороннего API. СотрудникСервис.java:

@Service
@Slf4j
public class EmployeeService{
  @Autowired
  private WebClient webClient;
  @Autowired
  private CacheManager cacheManager;
  @Cacheable("employee")
  public Mono<List<Employee>> getAllEmployee(){
    log.info("inside employee service {}");
    return webClient.get()
        .uri("/employees/")
        .retrieve()
        .bodyToMono(Employee.class);
}
}

Хотя я настроил имя кеша, во второй раз, когда я нажимаю URL-адрес, он вызывает метод службы. Какой механизм кэширования необходимо использовать для кэширования ответа Mono? Пожалуйста, предложите.

0
0
56
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Существует несколько вариантов кэширования реактивных издателей.

  1. Используйте реактивный cache API для кэширования Mono в течение определенного времени.
employeeService.getAllEmployee()
    .cache(Duration.ofMinutes(60))
    .flatMap(employees -> {
        // process data
    })
  1. Используйте внешний кэш (гуава, кофеин) с CacheMono из reactor-extra. Этот вариант больше подходит, если вам нужно кэшировать результаты на основе разных входных данных (например, мультитенантная среда).

Вот пример для гуавы, но вы можете адаптировать его для CacheManager

Cache<String, List<Employee>> cache = CacheBuilder.newBuilder()
        .expireAfterWrite(cacheTtl)
        .build();


Mono<List<Employee>> getEmployee(String tenant) {
    return CacheMono.lookup(key -> Mono.justOrEmpty(cache.getIfPresent(key)).map(Signal::next), tenant)
            .onCacheMissResume(() -> employeeService.getAllEmployee(tenant))
            .andWriteWith((key, signal) -> Mono.fromRunnable(() ->
                            Optional.ofNullable(signal.get())
                                    .ifPresent(value -> cache.put(key, value))
                    )
            );
}

Привет, @Alex, спасибо за ответ. Для 1-го варианта вы ссылаетесь на встроенный механизм кэширования загрузки spring. Я пробовал это, но не получил результата из кеша. Для второго не могли бы вы поделиться ссылкой.

stumbler 19.03.2022 19:16

Первый пример не имеет отношения к механизму загрузочного кэша Spring. это чистый реактивный API. Ключевым моментом здесь является повторное использование кэшированных Mono. например, вы можете сохранить его в каком-то поле класса Mono<...> cachedList = employeeService.getAllEmployee().cache(Duration.ofMinutes(60‌​));, а затем использовать в своем потоке. В противном случае новый издатель будет создан и возвращен из метода.

Alex 19.03.2022 19:54

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

stumbler 19.03.2022 20:03

Для 2-го варианта, поскольку мне нужно вернуть список объектов Mono, он говорит, что неверный возвращаемый тип в лямбда-выражении. Mono<List<Employee>> нельзя преобразовать в Mono<String>.

stumbler 19.03.2022 20:23

Обновил пример. Вам нужно определить кеш гуавы как Cache<String, List<Employee>> cache, но он будет разрешен асинхронно как Mono<List<Employee>>

Alex 20.03.2022 00:48

Спасибо @Alex, теперь это работает. Я выбрал второй вариант.

stumbler 20.03.2022 04:07

Другие вопросы по теме