1

我正在尝试使用缓存来维护基于请求类型的可路由服务器列表。

LoadingCache<Request, ActorRef> serversByRequestType = Caffeine.newBuilder()
  .writer(new CacheWriter<RequestType, ActorRef>() {

    @Override public void write(RequestType req, ActorRef server) {
      // We need to handle this type of request now.
      //
      server.tell(StartUp(req))
    }

    @Override public void delete(RequestType req, ActorRef server, RemovalCause cause) {
      // This req type can no longer be handled, so remove from
      // routable servers.
      //
      server.tell(ShutDown(req))
    }

  })
  .build();

在我只能n启动服务器的情况下,重要的是同步从缓存中删除(并因此关闭)服务器,然后在缓存中添加(并因此启动)同一服务器(即,改变服务器可以处理的流量)。

在上面的代码中,没有阻塞就没有办法做到这一点。

在一个理想的世界中,删除必须在添加到缓存之前发生,所以我可以异步关闭服务器并等待一个ShutdownServer事件......但是没有办法从方法中接收这个信号write,这必然需要知道什么时候开始。换句话说,我想发送一个SwitchServerTraffic(from: RequestType, to: RequestType)server,其中from将是被驱逐的密钥,to将是添加的密钥。

如果我可以访问候选驱逐集:当请求进来时,如果它的类型不在缓存中并且缓存已满,我可以从驱逐集中选择一个元素并关闭其服务器,然后添加请求像 Akka 一样同步输入缓存。

有没有办法访问 Caffeine 中的候选驱逐集?如果没有,是否有另一种方法来解决这个问题?

4

1 回答 1

2

您可以按驱逐顺序对项目进行猜测,但这不能保证。该策略是非确定性的,概率性有助于防止 HashDoS 攻击。通常,驱逐策略的低级细节不会暴露以允许算法改进。

Map<K, V> coldest = cache.policy().eviction().get().coldest(count);

如果在驱逐之前发生删除,则要么要求所有写入都由排他锁保护,要么可以同时评估策略。前者会成为瓶颈,而后者会导致命中率显着降低(例如,通过使用随机抽样)。缓存改为使用带有中间缓冲区的记录和重放策略,它吸收了并发的大部分损失。

默认情况下,驱逐与面向用户的调用异步执行,因此大多数调用CacheWriter#delete将被隐藏。但是,如果该调用很昂贵,那么它会延迟下一个项目的驱逐。

不幸的是,如果不站在您的立场上,很难提供建议,因为您的场景对您的需求有独特的限制。Cache.asMap()您可能会发现无论解决方案最终看起来如何,计算方法都非常有用。

于 2018-06-08T16:19:43.370 回答