簡體   English   中英

Spring @Cacheable 和@Async 注解

[英]Spring @Cacheable and @Async annotation

我需要緩存一些異步計算的結果。 具體來說,為了克服這個問題,我正在嘗試使用 Spring 4.3 緩存和異步計算功能。

例如,讓我們看下面的代碼:

@Service
class AsyncService {
    @Async
    @Cacheable("users")
    CompletableFuture<User> findById(String usedId) {
        // Some code that retrieves the user relative to id userId
        return CompletableFuture.completedFuture(user);
    }
}

是否可以? 我的意思是, Spring 的緩存抽象是否會正確處理CompletableFuture<User>類型的對象? 我知道Caffeine Cache有類似的東西,但如果配置正確,我無法理解 Spring 是否使用它。

編輯:我對User object 本身不感興趣,但對代表計算的CompletableFuture感興趣。

社區要求我做一些實驗,所以我做了它們。 我發現我的問題的答案很簡單:如果@Cacheable@Async放在同一個方法之上,它們就不能一起工作。

需要明確的是,我並不是在尋求一種方法來直接讓緩存返回CompletableFuture擁有的對象。 這是不可能的,如果不是這樣,就會破壞CompletableFuture類的異步計算契約。

正如我所說,這兩個注釋不能在同一個方法上一起工作。 如果你仔細想想,那就很明顯了。 @Async標記也是@Cacheable手段,將整個緩存管理委托給不同的異步線程。 如果CompletableFuture的值的計算需要很長時間才能完成,那么 Spring Proxy 將在該時間之后放置緩存中的值。

顯然,有一個解決方法。 解決方法使用CompletableFuturepromise的事實。 讓我們看看下面的代碼。

@Component
public class CachedService {
    /* Dependecies resolution code */
    private final AsyncService service;

    @Cacheable(cacheNames = "ints")
    public CompletableFuture<Integer> randomIntUsingSpringAsync() throws InterruptedException {
        final CompletableFuture<Integer> promise = new CompletableFuture<>();
        // Letting an asynchronous method to complete the promise in the future
        service.performTask(promise);
        // Returning the promise immediately
        return promise;
    }
}

@Component
public class AsyncService {
    @Async
    void performTask(CompletableFuture<Integer> promise) throws InterruptedException {
        Thread.sleep(2000);
        // Completing the promise asynchronously
        promise.complete(random.nextInt(1000));
    }
}

訣竅是創建一個不完整的承諾並立即從標有@Cacheable注釋的方法中返回它。 承諾將由另一個擁有用@Async注釋標記的方法的 bean 異步完成。

作為獎勵,我還實現了一個不使用 Spring @Async注釋的解決方案,但它直接使用CompletableFuture類中可用的工廠方法。

@Cacheable(cacheNames = "ints1")
public CompletableFuture<Integer> randomIntNativelyAsync() throws
        InterruptedException {
    return CompletableFuture.supplyAsync(this::getAsyncInteger, executor);
}

private Integer getAsyncInteger() {
    logger.info("Entering performTask");
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return random.nextInt(1000);
}

無論如何,我分享了我的 GitHub 問題的完整解決方案spring-cacheable-async

最后,以上是對 Jira SPR-12967所指內容的詳細描述。

我希望它有幫助。 干杯。

根據SPR- ListenableFutureListenableFuture ( CompletableFuture ) 不受支持。

添加@Async在一類和方法的注釋@Cacheable在方法級別批注在不同的類。

然后從服務或任何不同層調用@Async方法。

它對我有用,Redis 緩存和異步,這極大地提高了性能。

理論上,只要

  • @Cacheable后面的 CacheManager 實現不是序列化緩存對象(如 Hazelcast 支持的緩存)

  • 由於CompletableFuture持有一個狀態,它可以通過調用例如cancel()方法進行修改,重要的是 API 的所有用戶都不會亂用緩存的對象。 否則,可能存在無法再檢索Future中的緩存對象的風險,並且需要進行緩存驅逐

  • 值得驗證調用注釋背后的代理的順序。 @Cacheable代理總是在@Async之前@Async嗎? 或者反過來? 還是看情況? 例如,如果之前調用了@Async ,它將在ForkJoinPool觸發一個Callable ,然后從緩存中檢索另一個對象。

我嘗試了以下方法,它似乎有效。

  • 使用@Cachable創建一個方法,它執行實際的業務邏輯
  • 使用@Async創建一個方法,它調用上面的@Cachable方法並返回一個CompletableFuture
  • 在主執行流程中使用@Async調用方法

例子:

public class Main {
    public void cachedAsyncData() {
        try {
            asyncFetcher.getData().get();
        } catch(Exception e){}
    }
}

public class AsyncFetcher {
    @Async
    public CompletableFuture<String> getData() {
        return CompletableFuture.completedFuture(cacheFetcher.getData());
    }
}

public class CacheFetcher {
    @Cacheable
    public String getData() {
        return "DATA";
    }
}

請在@Component 或@Serice class 級別添加注解@EnableAsync。 經驗值:

@Service
@Slf4j
@EnableAsync //Add it to here
public class CachingServiceImpl implements CachingService {

希望能幫到你!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM