溫馨提示×

溫馨提示×

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

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

Spring Boot中如何配置多個TTL緩存

發(fā)布時間:2022-02-28 11:20:49 來源:億速云 閱讀:244 作者:小新 欄目:開發(fā)技術(shù)

這篇文章主要介紹了Spring Boot中如何配置多個TTL緩存,具有一定借鑒價值,感興趣的朋友可以參考下,希望大家閱讀完這篇文章之后大有收獲,下面讓小編帶著大家一起了解一下。

1. 研究案例

讓我們從問題的定義開始。我們想象中的應用程序需要緩存兩個不同的 REST 端點,但其中一個應該比另一個更頻繁地過期??紤]以下外觀實現(xiàn):

@Service
class ForeignEndpointGateway {
 
    private RestTemplate restTemplate;
 
    ForeignEndpointGateway(RestTemplate restTemplate) {
        this.restTemplate = restTemplate;
    }
     
    @Cacheable("messages")
    public Message findMessage(long id) {
        String url ="http://somedomain.com/messages/" + id;
        return restTemplate.getForObject(url, Message.class);
    }
 
    @Cacheable("notifications")
    public Notification findNotification(long id) {
        String url ="http://somedomain.com/notifications/" + id;
        return restTemplate.getForObject(url, Notification.class);
    }
 
}

@Cacheable注釋標記方法Spring的緩存機制。值得一提的是,緩存的方法必須是公開的。每個注解都指定了應該用于特定方法的相應緩存的名稱。

緩存實例只不過是一個簡單的鍵值容器。在我們的例子中,鍵是基于輸入?yún)?shù)創(chuàng)建的,值是方法的結(jié)果,但它不必那么簡單。Spring 提供的緩存抽象允許更多,但這是另一篇文章的主題。如果你對細節(jié)感興趣,我推薦你參考文檔。讓我們堅持我們的主要目標,即為兩個聲明的緩存定義不同的 TTL 值。

2. 常用緩存設置

@Cacheable注釋放在方法上并不是在應用程序中運行緩存機械化所需的唯一內(nèi)容。根據(jù)所選的提供商,可能會有幾個額外的步驟。

2.1. 開啟 Spring 緩存

無論您選擇哪個提供程序,設置的起點始終是將@EnableCaching注釋添加到您的配置類之一,通常是主應用程序類。這會在您的 Spring 上下文中注冊所有必需的組件。

@SpringBootApplication
@EnableCaching
public class TtlCacheApplication {
    // content omitted for clarity
}

2.2. 必需的依賴項

在使用@EnableCaching注釋的常規(guī) Spring 應用程序中,需要開發(fā)人員提供CacheManager類型的 bean 。幸運的是,Spring Boot 緩存啟動器提供了默認管理器,并根據(jù)類路徑上可用的依賴項創(chuàng)建了一個適當?shù)木彺嫣峁┏绦?,在我們的例子中?Caffeine 庫。

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
</dependency>

2.3. 基本配置

Spring Boot 支持的大多數(shù)緩存提供程序可以使用專用的應用程序?qū)傩赃M行調(diào)整。要為演示應用程序所需的兩個緩存設置 TTL,我們可以使用以下值:

spring.cache.cache-names=messages,notifications
spring.cache.caffeine.spec=maximumSize=100,expireAfterAccess=1800s

以一種非常簡單的方式,我們將緩存的 TTL 設置為 30 分鐘,并將它們的容量設置為 100。但是,這種配置的主要問題是所有緩存都使用相同的設置。不可能為每個緩存設置不同的規(guī)范。他們都需要共享一個全局的。如果您不介意此類限制,則可以進行基本設置。否則,您應該繼續(xù)閱讀下一部分。

3.區(qū)分緩存

Spring Boot 有效地處理流行的配置,但我們的場景不屬于這個幸運組。為了根據(jù)我們的需要自定義緩存,我們需要超越預定義的 bean 并編寫一些自定義初始化代碼。

3.1. 自定義緩存管理器

無需禁用 Spring Boot 提供的默認配置,因為我們只能覆蓋一個必要的對象。通過定義名為cacheManager的 bean,我們替換了 Spring Boot 提供的 bean 。下面我們創(chuàng)建兩個緩存。第一個稱為消息,其過期時間等于 30 分鐘。另一個名為通知的值存儲 60 分鐘。當您創(chuàng)建自定義緩存管理器時,application.properties 中的設置(之前在基本示例中介紹過)不再使用,可以安全地刪除。

@Bean
public CacheManager cacheManager(Ticker ticker) {
    CaffeineCache messageCache = buildCache("messages", ticker,30);
    CaffeineCache notificationCache = buildCache("notifications", ticker,60);
    SimpleCacheManager manager =new SimpleCacheManager();
    manager.setCaches(Arrays.asList(messageCache, notificationCache));
    return manager;
}
 
private CaffeineCache buildCache(String name, Ticker ticker,int minutesToExpire) {
    return new CaffeineCache(name, Caffeine.newBuilder()
                .expireAfterWrite(minutesToExpire, TimeUnit.MINUTES)
                .maximumSize(100)
                .ticker(ticker)
                .build());
}
 
@Bean
public Ticker ticker() {
    return Ticker.systemTicker();
}

Caffeine 庫帶有一個方便的緩存構(gòu)建器。在我們的演示中,我們只關(guān)注不同的 TTL 值,但也可以根據(jù)需要自定義其他選項(例如容量或訪問后非常有用的到期時間)。

在上面的例子中,我們還創(chuàng)建了ticker bean,我們的緩存共享它。自動收報機負責跟蹤時間的流逝。實際上,將Ticker類型的實例傳遞給緩存構(gòu)建器并不是強制性的,如果沒有提供,Caffeine 會創(chuàng)建一個。但是,如果我們想為我們的解決方案編寫測試,單獨的 bean 將更容易存根。

3.2. TTL緩存測試

我們在集成測試中需要的第一件事是一個帶有假代碼的配置類,它允許模擬時間流逝。Caffeine 庫本身不提供這樣的代碼,但文檔中提到了 guava-testlib,我們需要將其聲明為我們項目的依賴項。

<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava-testlib</artifactId>
    <version>20.0</version>
    <scope>test</scope>
</dependency>

如果測試類中存在一個內(nèi)部靜態(tài)配置類,則 Spring Boot 1.4.0 中添加的@SpringBootTest注釋會自動檢測并利用內(nèi)部靜態(tài)配置類。通過導入主配置類,我們保留了原始項目設置,并僅用假的替換了股票代碼實例。

@RunWith(SpringRunner.class)
@SpringBootTest
public class MessageRepositoryTest {
 
    @Configuration
    @Import(TtlCacheApplication.class)
    public static class TestConfig {
 
        static FakeTicker fakeTicker =new FakeTicker();
 
        @Bean
        public Ticker ticker() {
            return fakeTicker::read;
        }
 
    }
}

我們將在緩存網(wǎng)關(guān)類使用的RestTemplate實例上使用監(jiān)控,以觀察對真實 REST 端點的可能調(diào)用數(shù)量。監(jiān)控應該返回一些存根值以防止實際調(diào)用發(fā)生。

private static final long MESSAGE_ID =1;
private static final long NOTIFICATION_ID =2;
 
@SpyBean
private RestTemplate restTemplate;
@Autowired
private ForeignEndpointGateway gateway;
 
@Before
public void setUp()throws Exception {
    Message message = stubMessage(MESSAGE_ID);
    Notification notification = stubNotification(NOTIFICATION_ID);
    doReturn(message)
            .when(restTemplate)
            .getForObject(anyString(), eq(Message.class));
    doReturn(notification)
            .when(restTemplate)
            .getForObject(anyString(), eq(Notification.class));
}

最后,我們可以用我們的快樂路徑場景編寫一個測試,以確認 TTL 配置是否符合我們的預期。

@Test
public void shouldUseCachesWithDifferentTTL()throws Exception {
    // 0 minutes
    foreignEndpointGateway.findMessage(MESSAGE_ID);
    foreignEndpointGateway.findNotification(NOTIFICATION_ID);
    verify(restTemplate, times(1)).getForObject(anyString(), eq(Message.class));
    verify(restTemplate, times(1)).getForObject(anyString(), eq(Notification.class));
    // after 5 minutes
    TestConfig.fakeTicker.advance(5, TimeUnit.MINUTES);
    foreignEndpointGateway.findMessage(MESSAGE_ID);
    verify(restTemplate, times(1)).getForObject(anyString(), eq(Message.class));
    // after 35 minutes
    TestConfig.fakeTicker.advance(30, TimeUnit.MINUTES);
    foreignEndpointGateway.findMessage(MESSAGE_ID);
    foreignEndpointGateway.findNotification(NOTIFICATION_ID);
    verify(restTemplate, times(2)).getForObject(anyString(), eq(Message.class));
    verify(restTemplate, times(1)).getForObject(anyString(), eq(Notification.class));
    // after 65 minutes
    TestConfig.fakeTicker.advance(30, TimeUnit.MINUTES);
    foreignEndpointGateway.findNotification(NOTIFICATION_ID);
    verify(restTemplate, times(2)).getForObject(anyString(), eq(Notification.class));
}

一開始,MessageNotification對象都是從端點獲取并放置在緩存中。5 分鐘后,將再次調(diào)用Message對象。由于消息緩存 TTL 配置為 30 分鐘,我們預計將從緩存中獲取該值,并且不會調(diào)用端點。再過 30 分鐘后,我們預計緩存的消息已過期,我們通過對端點的另一次調(diào)用來確認這一點。但是,通知緩存已配置為將值保留 60 分鐘。通過再次嘗試獲取通知,我們確認另一個緩存仍然有效。最后,自動收報機再前進 30 分鐘,從測試開始算起總共 65 分鐘。我們驗證通知也已過期并從緩存中刪除。

3. 與其他緩存提供者的 TTL

如前所述,Caffeine 的主要缺點是無法區(qū)分所有緩存。spring.cache.caffeine.spec 中的規(guī)范適用于全球。希望在未來的版本中可以簡化多個緩存的設置,但現(xiàn)在我們需要堅持手動配置。

對于其他緩存提供者,幸運的是情況要容易得多。EhCacheHazelcast Infinitspan 使用專用的 XML 配置文件,其中每個緩存都可以單獨配置。

感謝你能夠認真閱讀完這篇文章,希望小編分享的“Spring Boot中如何配置多個TTL緩存”這篇文章對大家有幫助,同時也希望大家多多支持億速云,關(guān)注億速云行業(yè)資訊頻道,更多相關(guān)知識等著你來學習!

向AI問一下細節(jié)

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

AI