В моем приложении я использую 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? Пожалуйста, предложите.
Существует несколько вариантов кэширования реактивных издателей.
cache
API для кэширования Mono
в течение определенного времени.employeeService.getAllEmployee()
.cache(Duration.ofMinutes(60))
.flatMap(employees -> {
// process data
})
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))
)
);
}
Первый пример не имеет отношения к механизму загрузочного кэша Spring. это чистый реактивный API. Ключевым моментом здесь является повторное использование кэшированных Mono
. например, вы можете сохранить его в каком-то поле класса Mono<...> cachedList = employeeService.getAllEmployee().cache(Duration.ofMinutes(60));
, а затем использовать в своем потоке. В противном случае новый издатель будет создан и возвращен из метода.
в моем случае мне нужно передать идентификатор моей организации во время выполнения, и значение поступает от контроллера. Это означает, что я должен использовать эту переменную кеша в своем классе контроллера.
Для 2-го варианта, поскольку мне нужно вернуть список объектов Mono, он говорит, что неверный возвращаемый тип в лямбда-выражении. Mono<List<Employee>> нельзя преобразовать в Mono<String>.
Обновил пример. Вам нужно определить кеш гуавы как Cache<String, List<Employee>> cache
, но он будет разрешен асинхронно как Mono<List<Employee>>
Спасибо @Alex, теперь это работает. Я выбрал второй вариант.
Привет, @Alex, спасибо за ответ. Для 1-го варианта вы ссылаетесь на встроенный механизм кэширования загрузки spring. Я пробовал это, но не получил результата из кеша. Для второго не могли бы вы поделиться ссылкой.