Spring Boot – 使用动态TTL周期驱逐缓存

huangapple go评论59阅读模式
英文:

Spring boot - Evicting Cache with dynamic TTL period

问题

从我的微服务(SERVICE-A),我向另一个微服务(SERVICE-B)发出REST API调用,用于登录并获取访问令牌,该API将用返回的令牌TTL进行响应。
我需要将令牌缓存,直到由SERVICE-B响应的TTL(秒)结束。所以我的实现如下,

@Cacheable("USERTOKEN")
public String getUserToken() {
  // 调用 Service-B
  // 从 Service-B 获取令牌和TTL作为响应
  // 设置"USERTOKEN"缓存的过期时间   <-- 这部分需要添加
  // 返回令牌或带有TTL的令牌
}

我需要将上述方法更改为以下内容:

@Cacheable("USERTOKEN")
public String getUserToken() {
  // 调用 Service-B
  // 从 Service-B 获取令牌和TTL作为响应
  // 为"USERTOKEN"缓存设置过期时间   <-- 需要添加这部分
  // 返回令牌或带有TTL的令牌
}

即使从getUserToken()返回后,如果可以使用getUserToken()返回的TTL来设置"USERTOKEN"缓存的过期时间,那也可以。我们可以为逐出设置计划,但时间间隔将是静态的。但在这里,我希望它能够根据Service-B的响应设置为动态值。如何实现这一点呢?

英文:

From my microservice(SERVICE-A) I hit a rest api call to another microservice(SERVICE-B) for login and getting a access token and that API will respond with TTL of that token.
I need to cache the token till that TTL(seconds) that is responded by SERVICE-B. So my implementation is as below,

@Cacheable(&quot;USERTOKEN&quot;)
public String getUserToken()
{
  //Hits Service-B
  //Gets token and TTL as a response from Service-B
  //Returns Token or Token with TTL
}

I need to change the above method something as

@Cacheable(&quot;USERTOKEN&quot;)
public String getUserToken()
{
  //Hits Service-B
  //Gets token and TTL as a response from Service-B
  //Sets expiry time for &quot;USERTOKEN&quot; cache   &lt;-- this needs to be added
  //Returns Token or Token with TTL
}

Even after returning from getUserToken() if it is possible to setExpiryTime for the "USERTOKEN" cache using the TTL returned by getUserToken() it will be fine. We can set Scheduled for eviction but it is going to be a static time period. But here I need it to be set as dynamic value based on the response from Service-B. How can I achieve this.

答案1

得分: 3

如果您使用咖啡因缓存,您可以使用不同的过期策略:

来自咖啡因维基页面

// 基于不同的过期策略进行驱逐
LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .expireAfter(new Expiry<Key, Graph>() {
      public long expireAfterCreate(Key key, Graph graph, long currentTime) {
        // 如果来自外部资源,请使用壁挂钟时间,而不是纳秒时间
        long seconds = graph.creationDate().plusHours(5)
            .minus(System.currentTimeMillis(), MILLIS)
            .toEpochSecond();
        return TimeUnit.SECONDS.toNanos(seconds);
      }
      public long expireAfterUpdate(Key key, Graph graph, 
          long currentTime, long currentDuration) {
        return currentDuration;
      }
      public long expireAfterRead(Key key, Graph graph,
          long currentTime, long currentDuration) {
        return currentDuration;
      }
    })
    .build(key -> createExpensiveGraph(key));
英文:

If you use caffeine cache, you can use varying expiration policy:

From caffeine wiki page:

// Evict based on a varying expiration policy
LoadingCache&lt;Key, Graph&gt; graphs = Caffeine.newBuilder()
    .expireAfter(new Expiry&lt;Key, Graph&gt;() {
      public long expireAfterCreate(Key key, Graph graph, long currentTime) {
        // Use wall clock time, rather than nanotime, if from an external resource
        long seconds = graph.creationDate().plusHours(5)
            .minus(System.currentTimeMillis(), MILLIS)
            .toEpochSecond();
        return TimeUnit.SECONDS.toNanos(seconds);
      }
      public long expireAfterUpdate(Key key, Graph graph, 
          long currentTime, long currentDuration) {
        return currentDuration;
      }
      public long expireAfterRead(Key key, Graph graph,
          long currentTime, long currentDuration) {
        return currentDuration;
      }
    })
    .build(key -&gt; createExpensiveGraph(key));

答案2

得分: 1

如果您使用Spring的默认实现,将无法设置缓存的过期时间(Cache TTL)。但是对于其他提供者,如EhCache、Gemfire和Guava,您可以在配置缓存管理器时设置过期时间。有关详细信息,请参阅EhCache文档Spring Gemfire文档。有关Guava的信息请参阅

就像这样 - 对于EhCache。

CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
  .with(CacheManagerBuilder.persistence(tmpDir.newFile("myData")))
  .withCache("threeTieredCache",
    CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
      ResourcePoolsBuilder.newResourcePoolsBuilder()
        .heap(10, EntryUnit.ENTRIES)
        .offheap(1, MemoryUnit.MB)
        .disk(20, MemoryUnit.MB, true))
      .withExpiry(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofSeconds(20)))
  ).build(false);

Configuration configuration = cacheManager.getRuntimeConfiguration();
XmlConfiguration xmlConfiguration = new XmlConfiguration(configuration);  
String xml = xmlConfiguration.toString();

对于Gemfire

<gfe:region-template id="BaseRegionTemplate" initial-capacity="51" load-factor="0.85" persistent="false" statistics="true"
      key-constraint="java.lang.Long" value-constraint="java.lang.String">
    <gfe:cache-listener>
      <bean class="example.CacheListenerOne"/>
      <bean class="example.CacheListenerTwo"/>
    </gfe:cache-listener>
    <gfe:entry-ttl timeout="600" action="DESTROY"/>
    <gfe:entry-tti timeout="300" action="INVALIDATE"/>
  </gfe:region-template>

对于Caffeine或Guava的内部实现

LoadingCache<Key, Graph> graphs = Caffeine.newBuilder()
    .maximumSize(10_000)
    .expireAfterWrite(5, TimeUnit.MINUTES)
    .refreshAfterWrite(1, TimeUnit.MINUTES)
    .build(key -> createExpensiveGraph(key));
英文:

If you are using Spring's default implementation you won't be able to set Cache TTL. But with other providers such as EhCache, Gemfire & Guava you can do that but only during configuring Cache Managers. See ehcache Documentation and Spring's Gemfire's Documentation. For Guava see

Like this - For Ehcache.

CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
  .with(CacheManagerBuilder.persistence(tmpDir.newFile(&quot;myData&quot;)))
  .withCache(&quot;threeTieredCache&quot;,
    CacheConfigurationBuilder.newCacheConfigurationBuilder(Long.class, String.class,
      ResourcePoolsBuilder.newResourcePoolsBuilder()
        .heap(10, EntryUnit.ENTRIES)
        .offheap(1, MemoryUnit.MB)
        .disk(20, MemoryUnit.MB, true))
      .withExpiry(ExpiryPolicyBuilder.timeToIdleExpiration(Duration.ofSeconds(20)))
  ).build(false);

Configuration configuration = cacheManager.getRuntimeConfiguration();
XmlConfiguration xmlConfiguration = new XmlConfiguration(configuration);  
String xml = xmlConfiguration.toString(); 

For - Gemfire

&lt;gfe:region-template id=&quot;BaseRegionTemplate&quot; initial-capacity=&quot;51&quot; load-factor=&quot;0.85&quot; persistent=&quot;false&quot; statistics=&quot;true&quot;
      key-constraint=&quot;java.lang.Long&quot; value-constraint=&quot;java.lang.String&quot;&gt;
    &lt;gfe:cache-listener&gt;
      &lt;bean class=&quot;example.CacheListenerOne&quot;/&gt;
      &lt;bean class=&quot;example.CacheListenerTwo&quot;/&gt;
    &lt;/gfe:cache-listener&gt;
    &lt;gfe:entry-ttl timeout=&quot;600&quot; action=&quot;DESTROY&quot;/&gt;
    &lt;gfe:entry-tti timeout=&quot;300 action=&quot;INVLIDATE&quot;/&gt;
  &lt;/gfe:region-template&gt;

For - Caffiene or Internal Implementation of Guava

LoadingCache&lt;Key, Graph&gt; graphs = Caffeine.newBuilder()
    .maximumSize(10_000)
    .expireAfterWrite(5, TimeUnit.MINUTES)
    .refreshAfterWrite(1, TimeUnit.MINUTES)
    .build(key -&gt; createExpensiveGraph(key));

答案3

得分: 0

你正在使用哪个缓存服务?

如果你使用的是 Redis 缓存,它们提供了许多相关命令:

EXPIRE:为键设置超时时间。
EXPIREAT:与上述命令类似,但接受绝对的 Unix 时间戳(自1970年1月1日以来的秒数)。
TTL:返回具有超时的键的剩余存活时间。
关于 Redis 中过期的一个重要事项是:只有在使用 SET 或 GETSET 进行键的移除或覆盖时,超时值才会被清除。所有其他命令(如 INCR、LPUSH、HMSET 等)都不会改变初始超时时间。

Redis 的绝对过期是一种原生功能,使用 EXPIRE 实现滑动过期只需在每个命令后重置超时值。

一个基本的做法是:

MULTI
GET MYKEY
EXPIRE MYKEY 60
EXEC
英文:

which caching service are you using?

if u using Redis cache, they have got many commands for this :

EXPIRE : Set a timeout on key.
EXPIREAT : Same as previous but takes an absolute Unix timestamp (seconds since January 1, 1970).
TTL : Returns the remaining time to live of a key that has a timeout
One important thing you have to know about Expiration on Redis : the timeout value is cleared only when the key is removed or overwritten using SET or GETSET. All others commands (INCR, LPUSH, HMSET, ...) will never change the initial timeout.

Absolute expiration is a native feature of Redis using EXPIRE. To implement a sliding expiration you simply need to reset to timeout value after each command.

A basic way to do this could be

MULTI
GET MYKEY
EXPIRE MYKEY 60
EXEC

huangapple
  • 本文由 发表于 2020年9月25日 15:24:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/64059670.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定