您好,登錄后才能下訂單哦!
這篇文章主要介紹“SpringBoot怎么使用Caffeine實現(xiàn)緩存”,在日常操作中,相信很多人在SpringBoot怎么使用Caffeine實現(xiàn)緩存問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”SpringBoot怎么使用Caffeine實現(xiàn)緩存”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
在深入探討如何向應用程序添加緩存之前,首先想到的問題是為什么我們需要在應用程序中使用緩存。
假設有一個包含客戶數(shù)據(jù)的應用程序,用戶發(fā)出兩個請求來獲取客戶的數(shù)據(jù)(id=100)。
這就是沒有緩存時的情況。
如您所見,對于每個請求,應用程序都會轉到數(shù)據(jù)庫獲取數(shù)據(jù)。從數(shù)據(jù)庫獲取數(shù)據(jù)是一項成本高昂的操作,因為它涉及IO。
但是,如果中間有一個緩存存儲,可以在其中臨時存儲短時間的數(shù)據(jù),則可以將這些往返保存到數(shù)據(jù)庫并在IO時間保存。
這就是使用緩存時上述交互的樣子。
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}
來檢索客戶記錄。
我們將向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調用之后,您將在日志中看到以下行—“ Fetching customer by id
”。
但是,如果再次點擊API,您將不會在日志中看到任何內(nèi)容。這意味著該方法沒有得到執(zhí)行,并且從緩存返回客戶記錄。
現(xiàn)在等待一分鐘(因為緩存過期時間設置為1分鐘)。
一分鐘后再次點擊GETAPI,您將看到下面的語句再次被記錄——“通過id獲取客戶”。
這意味著客戶記錄在1分鐘后從緩存中刪除,必須再次從數(shù)據(jù)庫中獲取。
通常我們緩存 GET
調用,以提高性能。
但我們需要非常小心的是緩存對象的更新/刪除。
@CachePut @cacheexecute
如果未將 @CachePut/@cacheexecute
放入更新/刪除方法中,GET調用中緩存返回的對象將與數(shù)據(jù)庫中存儲的對象不同??紤]下面的示例場景。
如您所見,第二個請求已將人名更新為“ John Smith
”。但由于它沒有更新緩存,因此從此處開始的所有請求都將從緩存中獲取過時的個人記錄(“ John Doe
”),直到該項在緩存中被刪除/更新。
大多數(shù)現(xiàn)代web應用程序通常有多個應用程序節(jié)點,并且在大多數(shù)情況下都有一個負載平衡器,可以將用戶請求重定向到一個可用的應用程序節(jié)點。
這種類型的部署為應用程序提供了可伸縮性,任何用戶請求都可以由任何一個可用的應用程序節(jié)點提供服務。
在這些分布式環(huán)境(具有多個應用服務器節(jié)點)中,緩存可以通過兩種方式實現(xiàn)
應用服務器中的嵌入式緩存(正如我們現(xiàn)在看到的)
遠程緩存服務器
嵌入式緩存駐留在應用程序服務器中,它隨應用程序服務器啟動/停止。由于每臺服務器都有自己的緩存副本,因此對其緩存的任何更改/更新都不會自動反映在其他應用程序服務器的緩存中。
考慮具有嵌入式緩存的多節(jié)點應用服務器的下面場景,其中用戶可以根據(jù)應用服務器為其請求服務而得到不同的結果。
正如您在上面的示例中所看到的,更新請求更新了 Application Node2
的數(shù)據(jù)庫和嵌入式緩存。
但是, Application Node1
的嵌入式緩存未更新,并且包含過時數(shù)據(jù)。因此, Application Node1
的任何請求都將繼續(xù)服務于舊數(shù)據(jù)。
要解決這個問題,您需要實現(xiàn) CACHE REPLICATION
—其中任何一個緩存中的任何更新都會自動復制到其他緩存(下圖中顯示為藍色虛線)
解決上述問題的另一種方法是使用遠程緩存服務器(如下所示)。
然而,這種方法的最大缺點是增加了響應時間——這是由于從遠程緩存服務器獲取數(shù)據(jù)時的網(wǎng)絡延遲(與內(nèi)存緩存相比)
到目前為止,我們看到的緩存示例是向應用程序添加基本緩存所需的唯一代碼。
然而,現(xiàn)實世界的場景可能不是那么簡單,可能需要進行一些定制。在本節(jié)中,我們將看到幾個這樣的例子
我們知道緩存是密鑰、值對的存儲。
示例1:默認緩存鍵–具有單參數(shù)的方法
最簡單的緩存鍵是當方法只有一個參數(shù),并且該參數(shù)成為緩存鍵時。在下面的示例中, Long customerId
是緩存鍵
示例2:默認緩存鍵–具有多個參數(shù)的方法
在下面的示例中,緩存鍵是所有三個參數(shù)的SimpleKey– countryId
、 regionId
、 personId
。
示例3:自定義緩存密鑰
在下面的示例中,我們將此人的 emailAddress
指定為緩存的密鑰
示例4:使用 KeyGenerator
的自定義緩存密鑰
讓我們看看下面的示例–如果要緩存當前登錄用戶的所有角色,該怎么辦。
該方法中沒有提供任何參數(shù),該方法在內(nèi)部獲取當前登錄用戶并返回其角色。
為了實現(xiàn)這個需求,我們需要創(chuàng)建一個如下所示的自定義密鑰生成器
然后我們可以在我們的方法中使用這個鍵生成器,如下所示。
在某些用例中,我們只希望在滿足某些條件的情況下緩存結果
示例1(支持 java.util.Optional
–僅當存在時才緩存)
僅當結果中存在 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)
我們已經(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>
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權內(nèi)容。