web-dev-qa-db-ja.com

Spring Webfluxおよび@Cacheable-Mono / Fluxタイプの結果をキャッシュする適切な方法

私はSpring WebFluxを学習しています。サンプルアプリケーションの作成中に、Spring Cacheと組み合わせたReactiveタイプ(Mono/Flux)に関連する懸念を見つけました。

次のコードスニペット(Kotlin)を検討してください。

@Repository
interface TaskRepository : ReactiveMongoRepository<Task, String>

@Service
class TaskService(val taskRepository: TaskRepository) {

    @Cacheable("tasks")
    fun get(id: String): Mono<Task> = taskRepository.findById(id)
}

これは、MonoまたはFluxを返すメソッド呼び出しをキャッシュする有効かつ安全な方法ですか?多分これを行うには他のいくつかの原則がありますか?

次のコードはSimpleCacheResolverで動作しますが、Monoはシリアル化可能ではないため、デフォルトではRedisで失敗します。それらを機能させるには、例えばKryoシリアライザーを使用する必要があります。

15
Tomek Zaremba

ハックウェイ

現時点では、_@Cacheable_とReactor 3の滑らかな統合はありません。ただし、.cache()演算子を返されたMonoに追加することで、そのことをバイパスできます。

_@Repository
interface TaskRepository : ReactiveMongoRepository<Task, String>

@Service
class TaskService(val taskRepository: TaskRepository) {

    @Cacheable("tasks")
    fun get(id: String): Mono<Task> = taskRepository.findById(id).cache()
}
_

そのhackキャッシュと共有がtaskRepositoryデータから返されます。次に、Spring Cacheableは、返されたMonoの参照をキャッシュしてから、その参照を返します。つまり、キャッシュを保持するのはモノのキャッシュです:)。

Reactor Addons Way

Reactor 3には addition があり、 caffeinejcache などの最新のインメモリキャッシュとスムーズに統合できます。この手法を使用すると、データを簡単にキャッシュできる:

_@Repository
interface TaskRepository : ReactiveMongoRepository<Task, String>

@Service
class TaskService(val taskRepository: TaskRepository) {

    @Autowire
    CacheManager manager;


    fun get(id: String): Mono<Task> = CacheMono.lookup(reader(), id)
                                               .onCacheMissResume(() -> taskRepository.findById(id))
                                               .andWriteWith(writer());

    fun reader(): CacheMono.MonoCacheReader<String, Task> = key -> Mono.<Signal<Task>>justOrEmpty((Signal) manager.getCache("tasks").get(key).get())
    fun writer(): CacheMono.MonoCacheWriter<String, Task> = (key, value) -> Mono.fromRunnable(() -> manager.getCache("tasks").put(key, value));
} 
_

注:_Signal<T>_である独自の抽象化をキャッシュするReactorアドオンなので、心配せず、その規則に従ってください

22
Oleh Dokuka

私はOleh Dokukaのハッキーソリューションを使用しましたが、うまくいきましたが、落とし穴があります。 Fachキャッシュでは、Cachableキャッシュのtimetolive値よりも長い期間を使用する必要があります。 Fluxキャッシュの期間を使用しない場合、それは無効になりません(Fluxのドキュメントには、「このFluxをホットソースに変換し、最後に放出された信号をさらにサブスクライバーにキャッシュする」と記載されています)。したがって、Fluxキャッシュを2分、timetoliveを30秒にすることは有効な構成です。 ehcahceタイムアウトが最初に発生した場合、新しいFluxキャッシュ参照が生成され、それが使用されます。

1
ilker Kopan

//ファサード内:

public Mono<HybrisResponse> getProducts(HybrisRequest request) {
    return Mono.just(HybrisResponse.builder().build());
}

//サービス層で:

@Cacheable(cacheNames = "embarkations")
public HybrisResponse cacheable(HybrisRequest request) {
    LOGGER.info("executing cacheable");
    return null;
}

@CachePut(cacheNames = "embarkations")
public HybrisResponse cachePut(HybrisRequest request) {
    LOGGER.info("executing cachePut");
    return hybrisFacade.getProducts(request).block();
}

//コントローラ内:

HybrisResponse hybrisResponse = null;

try {
   // get from cache
   hybrisResponse = productFeederService.cacheable(request);

} catch (Throwable e) {
   // if not in cache then cache it
   hybrisResponse = productFeederService.cachePut(request);
}

return Mono.just(hybrisResponse)
    .map(result -> ResponseBody.<HybrisResponse>builder()
        .payload(result).build())
    .map(ResponseEntity::ok);
1
Developer