cache2k:Guava Cache及Caffeine之外的新选择

这篇具有很好参考价值的文章主要介绍了cache2k:Guava Cache及Caffeine之外的新选择。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

本文主要研究一下cache2k这款新型缓存

示例

Cache<String,String> cache = new Cache2kBuilder<String, String>() {}
                .eternal(true)
                .expireAfterWrite(5, TimeUnit.MINUTES)    // expire/refresh after 5 minutes
                .setupWith(UniversalResiliencePolicy::enable, b -> b // enable resilience policy
                                .resilienceDuration(30, TimeUnit.SECONDS)          // cope with at most 30 seconds
                        // outage before propagating
                        // exceptions
                )
                .refreshAhead(true)                       // keep fresh when expiring
                .loader(k -> expensiveOperation(k))         // auto populating function
                .build();

常见问题的解决方案

空值问题

JCache规范不支持null,所以cache2k默认也不支持,不过可以通过permitNullValues(true)来开启,这样子缓存就可以存储null值

cache stampede问题

又称作cache miss storm,指的是高并发场景缓存同时失效导致大面积回源,cache2k采用的是block的请求方式,避免对同一个key并发回源

org/cache2k/core/HeapCache.java

protected Entry<K, V> getEntryInternal(K key, int hc, int val) {
    if (loader == null) {
      return peekEntryInternal(key, hc, val);
    }
    Entry<K, V> e;
    for (;;) {
      e = lookupOrNewEntry(key, hc, val);
      if (e.hasFreshData(clock)) {
        return e;
      }
      synchronized (e) {
        e.waitForProcessing();
        if (e.hasFreshData(clock)) {
          return e;
        }
        if (e.isGone()) {
          metrics.heapHitButNoRead();
          metrics.goneSpin();
          continue;
        }
        e.startProcessing(Entry.ProcessingState.LOAD, null);
        break;
      }
    }
    boolean finished = false;
    try {
      load(e);
      finished = true;
    } finally {
      e.ensureAbort(finished);
    }
    if (e.getValueOrException() == null && isRejectNullValues()) {
      return null;
    }
    return e;
  }

同步回源造成的接口稳定性问题

cache2k提供了refreshAhead参数,在新数据没有拉取成功之前,过期数据仍然可以访问,避免请求到来时发现数据过期触发同步回源造成接口延时增大问题。不过具体底层还依赖prefetchExecutor,如果refresh的时候没有足够的线程可以使用则会立马过期,等待下次get出发同步回源

org/cache2k/core/HeapCache.java

public void timerEventRefresh(Entry<K, V> e, Object task) {
    metrics.timerEvent();
    synchronized (e) {
      if (e.getTask() != task) { return; }
      try {
        refreshExecutor.execute(createFireAndForgetAction(e, Operations.SINGLETON.refresh));
      } catch (RejectedExecutionException ex) {
        metrics.refreshRejected();
        expireOrScheduleFinalExpireEvent(e);
      }
    }
  }

默认的executor如下,采用的是SynchronousQueue队列,可以通过builder自己去设置refreshExecutor

  Executor provideDefaultLoaderExecutor(int threadCount) {
    int corePoolThreadSize = 0;
    return new ThreadPoolExecutor(corePoolThreadSize, threadCount,
      21, TimeUnit.SECONDS,
      new SynchronousQueue<>(),
      threadFactoryProvider.newThreadFactory(getThreadNamePrefix()),
      new ThreadPoolExecutor.AbortPolicy());
  }

回源故障问题

针对回源的下游出现故障的问题,cache2k提供了ResiliencePolicy策略,其实现类为UniversalResiliencePolicy
当load方法抛出异常且cache里头还有数据的时候,异常不会抛给client,用当前的数据返回,这里有个resilienceDuration时间,如果超过这个时间load方法还继续抛出异常则异常会抛给client。如果没有单独设置resilienceDuration,则默认取的是expiryAfterWrite时间

org/cache2k/core/HeapCache.java

private Object loadGotException(Entry<K, V> e, long t0, long t, Throwable wrappedException) {
    ExceptionWrapper<K, V> exceptionWrapper =
      new ExceptionWrapper(keyObjFromEntry(e), wrappedException, t0, e, exceptionPropagator);
    long expiry = 0;
    long refreshTime = 0;
    boolean suppressException = false;
    RefreshAheadPolicy.Context<Object> refreshCtx;
    try {
      if (e.isValidOrExpiredAndNoException()) {
        expiry = timing.suppressExceptionUntil(e, exceptionWrapper);
      }
      if (expiry > t0) {
        suppressException = true;
      } else {
        expiry = timing.cacheExceptionUntil(e, exceptionWrapper);
      }
      refreshCtx = getContext(e, t0, t, true, true, false, expiry);
      refreshTime = timing.calculateRefreshTime(refreshCtx);
    } catch (Exception ex) {
      return resiliencePolicyException(e, t0, t, new ResiliencePolicyException(ex), null);
    }
    exceptionWrapper = new ExceptionWrapper<>(exceptionWrapper, Math.abs(expiry));
    Object wrappedValue = exceptionWrapper;
    if (expiry != 0) {
      wrappedValue = timing.wrapLoadValueForRefresh(refreshCtx, e, exceptionWrapper);
    }
    Object loadResult;
    synchronized (e) {
      insertUpdateStats(e, (V) wrappedValue, t0, t, true, expiry, suppressException);
      if (suppressException) {
        e.setSuppressedLoadExceptionInformation(exceptionWrapper);
        loadResult = e.getValueOrException();
      } else {
        if (isRecordModificationTime()) {
          e.setModificationTime(t0);
        }
        e.setValueOrWrapper(exceptionWrapper);
        loadResult = exceptionWrapper;
      }
      finishLoadOrEviction(e, expiry, refreshTime);
    }
    return loadResult;
  }

这里timing.suppressExceptionUntil是委托给了ResiliencePolicy#suppressExceptionUntil

cache2k-addon/src/main/java/org/cache2k/addon/UniversalResiliencePolicy.java

public long suppressExceptionUntil(K key,
                                     LoadExceptionInfo<K, V> loadExceptionInfo,
                                     CacheEntry<K, V> cachedEntry) {
    if (resilienceDuration == 0 || resilienceDuration == Long.MAX_VALUE) {
      return resilienceDuration;
    }
    long maxSuppressUntil = loadExceptionInfo.getSinceTime() + resilienceDuration;
    long deltaMs = calculateRetryDelta(loadExceptionInfo);
    return Math.min(loadExceptionInfo.getLoadTime() + deltaMs, maxSuppressUntil);
  }

UniversalResiliencePolicy还提供了异常重试的功能,重试间隔为retryInterval,如果没有配置则为resilienceDuration的5%,采取的是指数退避的模式,factor为1.5

小结

cache2k提供了Guava Cache及Caffeine没有的ResiliencePolicy,针对C端高并发场景提供了容错的功能,值得借鉴一下。

个人公众号「码匠的流水账」(geek_luandun),欢迎关注文章来源地址https://www.toymoban.com/news/detail-437499.html

doc

  • cache2k
  • cache2k User Guide
  • Introduction to cache2k
  • caffeine
  • guava cache

到了这里,关于cache2k:Guava Cache及Caffeine之外的新选择的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Guava:Cache强大的本地缓存框架

    Guava Cache是一款非常优秀的本地缓存框架。 Guava Cache 的数据结构跟 JDK1.7 的 ConcurrentHashMap 类似,提供了基于时间、容量、引用三种回收策略,以及自动加载、访问统计等功能。 基本的配置 例子中,缓存最大容量设置为 100 ( 基于容量进行回收 ),配置了 失效策略 和 刷新策

    2024年02月02日
    浏览(31)
  • flink1.15 维表join guava cache和mysql方面优化

    优化前  mysql响应慢,导致算子中数据输出追不上输入,导致显示cpu busy:100% 优化后效果两个图对应两个时刻: - - 图中guava cache命中率是通过guava自带统计,打印出来的. 1 guava缓存数据量上限 = 类中配置的guava缓存数据上线 * task个数(即flink并行度) 缓存越久 命中率越高 数据越陈旧

    2024年01月17日
    浏览(31)
  • ARM基础(4):L1 Cache之I-Cache和D-cache详解

    在上一篇文章ARM基础(3):MPU内存保护单元详解及例子中,我介绍了MPU,我们知道MPU允许按区域修改一级Cache的属性,这个Cache一般为L1 Cache,它位于CPU的内部,用来加快指令和数据的访问速度。同时,CPU在处理共享数据时需要确保CPU和主存之间的数据一致性。这篇文章就来详细

    2024年02月09日
    浏览(69)
  • Service Worker Cache 和 HTTP Cache 的对比

    在 Web 应用开发中,缓存机制对于提升用户体验和减少网络请求具有重要的作用,其中包括传统的 HTTP 缓存和 Service Worker 中的 Cache API。这两种缓存机制各有优势,但是对于 Web 应用,Service Worker 的 Cache API 有着更为显著的优势。 Service Worker 的 Cache API 提供了比 HTTP 缓存更加丰

    2024年01月17日
    浏览(33)
  • 【ARM Cache 系列文章 2 -- Cache Coherence及内存顺序模学习】

    请阅读 【ARM Cache 系列文章专栏导读】

    2024年02月16日
    浏览(42)
  • 几个影响 cpu cache 性能因素及 cache 测试工具介绍

    1 cache 性能及影响因素 1.1 内存访问和性能比较 有如下测试代码: 该测试代码预先分配大量内存,并与一个数组指针管理,使其可以通过数组索引访问该片申请内存,接着对该内存域进行预读写,使其在测试中不受 pagefault 影响测试结果。 该测试主要的测试点: 第一个循环按

    2024年02月14日
    浏览(36)
  • 如何使用Python内置缓存装饰器: @lru_cache,@cache 与 @cached_property

    使用缓存是优化Python程序速度的重要方法之一 。如果使用得当,可以大幅减少计算资源的负载,有效加快代码运行速度 Python 的内置库 functools 模块附带了 @lru_cache,@cache, @cached_property 装饰器,使用非常简便,不需要安装第3方库,不需要 redis 等数据库保存对象等,通常只需要

    2024年02月11日
    浏览(34)
  • python装饰器原理 | 常用装饰器使用(@cache, @lru_cache)

    🚀 关于python的装饰器原理介绍可看这里,讲的挺简洁易懂:python装饰器原理 ⭐ 弄懂装饰器原理后,来学学常用装饰器。 也就是一种装饰在被执行的函数上,将其执行的结果缓存起来,当下次请求的时候,如果请求该函数的传参未变则直接返回缓存起来的结果而不再执行函

    2023年04月25日
    浏览(26)
  • 146. LRU Cache最近最少使用 (LRU) 缓存 Least Recently Used (LRU) cache.

    Design a data structure that follows the constraints of a Least Recently Used (LRU) cache. Implement the LRUCache class: LRUCache(int capacity) Initialize the LRU cache with positive size capacity. int get(int key) Return the value of the key if the key exists, otherwise return -1. void put(int key, int value) Update the value of the key if the key exists. O

    2024年02月10日
    浏览(41)
  • 【计算机组成原理】高速缓冲存储器 Cache 的三种映射方式(Cache Mapping)

    缓存是计算机系统中常见的一种高速存储器,用于临时存储常用数据,以便快速访问。在缓存中,有三种常见的映射方式,分别是直接映射、全相联映射和组相联映射。 在直接映射中,每个主存块只能映射到缓存中的一个特定位置。该位置是通过对主存块的某个地址的一部分

    2024年01月19日
    浏览(39)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包