溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

SpringBoot怎么使用Caffeine實現(xiàn)緩存

發(fā)布時間:2022-07-05 11:39:16 來源:億速云 閱讀:285 作者:iii 欄目:開發(fā)技術

這篇文章主要介紹“SpringBoot怎么使用Caffeine實現(xiàn)緩存”,在日常操作中,相信很多人在SpringBoot怎么使用Caffeine實現(xiàn)緩存問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”SpringBoot怎么使用Caffeine實現(xiàn)緩存”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!

為什么要在應用程序中添加緩存

在深入探討如何向應用程序添加緩存之前,首先想到的問題是為什么我們需要在應用程序中使用緩存。

假設有一個包含客戶數(shù)據(jù)的應用程序,用戶發(fā)出兩個請求來獲取客戶的數(shù)據(jù)(id=100)。

這就是沒有緩存時的情況。

SpringBoot怎么使用Caffeine實現(xiàn)緩存

如您所見,對于每個請求,應用程序都會轉到數(shù)據(jù)庫獲取數(shù)據(jù)。從數(shù)據(jù)庫獲取數(shù)據(jù)是一項成本高昂的操作,因為它涉及IO。

但是,如果中間有一個緩存存儲,可以在其中臨時存儲短時間的數(shù)據(jù),則可以將這些往返保存到數(shù)據(jù)庫并在IO時間保存。

這就是使用緩存時上述交互的樣子。

SpringBoot怎么使用Caffeine實現(xiàn)緩存

在Spring Boot應用程序中實現(xiàn)緩存

SpringBoot提供了什么緩存支持?

  • SpringBoot只提供了一個緩存抽象,您可以使用它將緩存透明、輕松地添加到Spring應用程序中。

  • 它不提供實際的緩存存儲。

  • 但是,它可以與不同類型的緩存提供程序一起工作,如Ehcache、Hazelcast、Redis、Caffee等。

  • SpringBoot的緩存抽象可以添加到方法中(使用注釋)

  • 基本上,在執(zhí)行方法之前,Spring框架將檢查方法數(shù)據(jù)是否已經(jīng)緩存

  • 如果是,則它將從緩存中獲取數(shù)據(jù)。

  • 否則它將執(zhí)行該方法并緩存數(shù)據(jù)

  • 它還提供了從緩存中更新或刪除數(shù)據(jù)的抽象。

  • 在我們當前的博客中,我們將了解如何使用Caffeine添加緩存,Caffeine是一種基于Java8的高性能、接近最優(yōu)的緩存庫。

您可以在 application.yaml 文件中指定使用哪個緩存提供程序來設置 spring.cache.type 屬性。

但是,如果沒有提供屬性,Spring將根據(jù)添加的庫自動檢測緩存提供程序。

添加生成依賴項

現(xiàn)在假設您已經(jīng)啟動并運行了基本的Spring boot應用程序,讓我們添加緩存依賴項。

打開 build.gradle 文件,并添加以下依賴項以啟用Spring Boot的緩存

compile('org.springframework.boot:spring-boot-starter-cache')

接下來我們將添加對Caffeine的依賴

compile group: 'com.github.ben-manes.caffeine', name: 'caffeine', version: '2.8.5'

緩存配置

現(xiàn)在我們需要在Spring Boot應用程序中啟用緩存。

為此,我們需要創(chuàng)建一個配置類并提供注釋 @EnableCaching 。

@Configuration
@EnableCaching
public class CacheConfig {
     
}

現(xiàn)在這個類是一個空類,但是我們可以向它添加更多配置(如果需要)。

現(xiàn)在我們已經(jīng)啟用了緩存,讓我們提供緩存名稱和緩存屬性的配置,如緩存大小、緩存過期時間等

最簡單的方法是在 application.yaml 中添加配置

spring:
  cache:
    cache-names: customers, users, roles
    caffeine:
      spec: maximumSize=500, expireAfterAccess=60s

上述配置執(zhí)行以下操作

  • 將可用緩存名稱限制為客戶、用戶和角色。將最大緩存大小設置為500。

  • 當緩存中的對象數(shù)達到此限制時,將根據(jù)緩存逐出策略從緩存中刪除對象。將緩存過期時間設置為1分鐘。

  • 這意味著項目將在添加到緩存1分鐘后從緩存中刪除。

還有另一種配置緩存的方法,而不是在 application.yaml 文件中配置緩存。

您可以在緩存配置類中添加并提供一個 CacheManager Bean,該Bean可以完成與上面在 application.yaml 中的配置完全相同的工作

@Bean
public CacheManager cacheManager() {
    Caffeine<Object, Object> caffeineCacheBuilder =
        Caffeine.newBuilder()
            .maximumSize(500)
            .expireAfterAccess(
                    1, TimeUnit.MINUTES);
     
    CaffeineCacheManager cacheManager = 
            new CaffeineCacheManager(
            "customers", "roles", "users");
    cacheManager.setCaffeine(caffeineCacheBuilder);
    return cacheManager;
}

在我們的代碼示例中,我們將使用Java配置。

我們可以在Java中做更多的事情,比如配置 RemovalListener ,當一個項從緩存中刪除時執(zhí)行 RemovalListener ,或者啟用緩存統(tǒng)計記錄,等等。

緩存方法結果

在我們使用的示例Spring boot應用程序中,我們已經(jīng)有了以下API GET /API/v1/customer/{id} 來檢索客戶記錄。

SpringBoot怎么使用Caffeine實現(xiàn)緩存

我們將向CustomerService類的 getCustomerByd(longCustomerId) 方法添加緩存。

要做到這一點,我們只需要做兩件事

1. 將注釋 @CacheConfig(cacheNames=“customers”) 添加到 CustomerService 類

提供此選項將確保 CustomerService 的所有可緩存方法都將使用緩存名稱“customers”

2. 向方法 Optional getCustomerById(Long customerId) 添加注釋 @Cacheable

@Service
@Log4j2
@CacheConfig(cacheNames = "customers")
public class CustomerService {
 
    @Autowired
    private CustomerRepository customerRepository;
 
    @Cacheable
    public Optional<Customer> getCustomerById(Long customerId) {
        log.info("Fetching customer by id: {}", customerId);
        return customerRepository.findById(customerId);
    }
}

另外,在方法 getCustomerById() 中添加一個 LOGGER 語句,以便我們知道服務方法是否得到執(zhí)行,或者值是否從緩存返回。

 代碼如下:log.info("Fetching customer by id: {}", customerId);

測試緩存是否正常工作

這就是緩存工作所需的全部內(nèi)容?,F(xiàn)在是測試緩存的時候了。

啟動您的應用程序,并點擊客戶獲取url

http://localhost:8080/api/v1/customer/

在第一次API調用之后,您將在日志中看到以下行&mdash;“ Fetching customer by id ”。

但是,如果再次點擊API,您將不會在日志中看到任何內(nèi)容。這意味著該方法沒有得到執(zhí)行,并且從緩存返回客戶記錄。

現(xiàn)在等待一分鐘(因為緩存過期時間設置為1分鐘)。

一分鐘后再次點擊GETAPI,您將看到下面的語句再次被記錄&mdash;&mdash;“通過id獲取客戶”。

這意味著客戶記錄在1分鐘后從緩存中刪除,必須再次從數(shù)據(jù)庫中獲取。

為什么緩存有時會很危險

緩存更新/失效

通常我們緩存 GET 調用,以提高性能。

但我們需要非常小心的是緩存對象的更新/刪除。

@CachePut
@cacheexecute

如果未將 @CachePut/@cacheexecute 放入更新/刪除方法中,GET調用中緩存返回的對象將與數(shù)據(jù)庫中存儲的對象不同??紤]下面的示例場景。

SpringBoot怎么使用Caffeine實現(xiàn)緩存

如您所見,第二個請求已將人名更新為“ John Smith ”。但由于它沒有更新緩存,因此從此處開始的所有請求都將從緩存中獲取過時的個人記錄(“ John Doe ”),直到該項在緩存中被刪除/更新。

緩存復制

大多數(shù)現(xiàn)代web應用程序通常有多個應用程序節(jié)點,并且在大多數(shù)情況下都有一個負載平衡器,可以將用戶請求重定向到一個可用的應用程序節(jié)點。

SpringBoot怎么使用Caffeine實現(xiàn)緩存

這種類型的部署為應用程序提供了可伸縮性,任何用戶請求都可以由任何一個可用的應用程序節(jié)點提供服務。

在這些分布式環(huán)境(具有多個應用服務器節(jié)點)中,緩存可以通過兩種方式實現(xiàn)

  • 應用服務器中的嵌入式緩存(正如我們現(xiàn)在看到的)

  • 遠程緩存服務器

嵌入式緩存

嵌入式緩存駐留在應用程序服務器中,它隨應用程序服務器啟動/停止。由于每臺服務器都有自己的緩存副本,因此對其緩存的任何更改/更新都不會自動反映在其他應用程序服務器的緩存中。

考慮具有嵌入式緩存的多節(jié)點應用服務器的下面場景,其中用戶可以根據(jù)應用服務器為其請求服務而得到不同的結果。

SpringBoot怎么使用Caffeine實現(xiàn)緩存

正如您在上面的示例中所看到的,更新請求更新了 Application Node2 的數(shù)據(jù)庫和嵌入式緩存。

但是, Application Node1 的嵌入式緩存未更新,并且包含過時數(shù)據(jù)。因此, Application Node1 的任何請求都將繼續(xù)服務于舊數(shù)據(jù)。

要解決這個問題,您需要實現(xiàn) CACHE REPLICATION &mdash;其中任何一個緩存中的任何更新都會自動復制到其他緩存(下圖中顯示為藍色虛線)

SpringBoot怎么使用Caffeine實現(xiàn)緩存

遠程緩存服務器

解決上述問題的另一種方法是使用遠程緩存服務器(如下所示)。

SpringBoot怎么使用Caffeine實現(xiàn)緩存

然而,這種方法的最大缺點是增加了響應時間&mdash;&mdash;這是由于從遠程緩存服務器獲取數(shù)據(jù)時的網(wǎng)絡延遲(與內(nèi)存緩存相比)

緩存自定義

到目前為止,我們看到的緩存示例是向應用程序添加基本緩存所需的唯一代碼。

然而,現(xiàn)實世界的場景可能不是那么簡單,可能需要進行一些定制。在本節(jié)中,我們將看到幾個這樣的例子

緩存密鑰

我們知道緩存是密鑰、值對的存儲。

示例1:默認緩存鍵&ndash;具有單參數(shù)的方法

最簡單的緩存鍵是當方法只有一個參數(shù),并且該參數(shù)成為緩存鍵時。在下面的示例中, Long customerId 是緩存鍵

SpringBoot怎么使用Caffeine實現(xiàn)緩存

示例2:默認緩存鍵&ndash;具有多個參數(shù)的方法

在下面的示例中,緩存鍵是所有三個參數(shù)的SimpleKey&ndash; countryId 、 regionId 、 personId 。

SpringBoot怎么使用Caffeine實現(xiàn)緩存

示例3:自定義緩存密鑰

在下面的示例中,我們將此人的 emailAddress 指定為緩存的密鑰

SpringBoot怎么使用Caffeine實現(xiàn)緩存

示例4:使用 KeyGenerator 的自定義緩存密鑰

讓我們看看下面的示例&ndash;如果要緩存當前登錄用戶的所有角色,該怎么辦。

SpringBoot怎么使用Caffeine實現(xiàn)緩存

該方法中沒有提供任何參數(shù),該方法在內(nèi)部獲取當前登錄用戶并返回其角色。

為了實現(xiàn)這個需求,我們需要創(chuàng)建一個如下所示的自定義密鑰生成器

SpringBoot怎么使用Caffeine實現(xiàn)緩存

然后我們可以在我們的方法中使用這個鍵生成器,如下所示。

SpringBoot怎么使用Caffeine實現(xiàn)緩存

條件緩存

在某些用例中,我們只希望在滿足某些條件的情況下緩存結果

示例1(支持 java.util.Optional &ndash;僅當存在時才緩存)

僅當結果中存在 person 對象時,才緩存 person 對象。

@Cacheable( value = "persons", unless = "#result?.id")
public Optional<Person> getPerson(Long personId)

示例2(如果需要,by-pass緩存)

@Cacheable(value = "persons", condition="#fetchFromCache")
public Optional<Person> getPerson(long personId, boolean fetchFromCache)

僅當方法參數(shù)“ fetchFromCache ”為true時,才從緩存中獲取人員。通過這種方式,方法的調用方有時可以決定繞過緩存并直接從數(shù)據(jù)庫獲取值。

示例3(基于對象屬性的條件計算)

僅當價格低于500且產(chǎn)品有庫存時,才緩存產(chǎn)品。

@Cacheable( 
   value="products", 
   condition="#product.price<500",
   unless="#result.outOfStock")
public Product findProduct(Product product)

@CachePut

我們已經(jīng)看到 @Cacheable 用于將項目放入緩存。

但是,如果該對象被更新,并且我們想要更新緩存,該怎么辦?

我們已經(jīng)在前面的一節(jié)中看到,不更新緩存post任何更新操作都可能導致從緩存返回錯誤的結果。

@CachePut(key = "#person.id")
public Person update(Person person)

但是如果 @Cacheable 和 @CachePut 都將一個項目放入緩存,它們之間有什么區(qū)別?

主要區(qū)別在于實際的方法執(zhí)行

@Cacheable
@CachePut

緩存失效

緩存失效與將對象放入緩存一樣重要。

當我們想要從緩存中刪除一個或多個對象時,有很多場景。讓我們看一些例子。

例1

假設我們有一個用于批量導入個人記錄的API。

我們希望在調用此方法之前,應該清除整個 person 緩存(因為大多數(shù) person 記錄可能會在導入時更新,而緩存可能會過時)。我們可以這樣做如下

@CacheEvict(
   value = "persons", 
   allEntries = true, 
   beforeInvocation = true)
public void importPersons()

例2

我們有一個Delete Person API,我們希望它在刪除時也能從緩存中刪除 Person 記錄。

@CacheEvict(
   value = "persons", 
   key = "#person.emailAddress")
public void deletePerson(Person person)

默認情況下 @CacheEvict 在方法調用后運行。

到此,關于“SpringBoot怎么使用Caffeine實現(xiàn)緩存”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續(xù)學習更多相關知識,請繼續(xù)關注億速云網(wǎng)站,小編會繼續(xù)努力為大家?guī)砀鄬嵱玫奈恼拢?/p>

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。

AI