【问题标题】:Conditional memoization in GuavaGuava 中的条件记忆
【发布时间】:2020-02-04 18:04:55
【问题描述】:

我知道如何memoize a single object。但是,我只想在满足某些条件时才记住。我正在调用一项服务,该服务有时会返回不成功的响应。如果服务响应成功,我只想记住它。

MyResponse myResponse = myService.call()
boolean success = myResponse.isSuccessful();

我的缓存是这样创建的:

private Supplier<MyResponse> cache;

private void createCache() {
    this.cache = Suppliers
        .memoizeWithExpiration(myService::call, timeout,
            TimeUnit.MINUTES);
}

问题: 只有在使用传递给memoizeWithExpiration方法的供应商成功的情况下,是否可以以某种方式缓存响应?


我发现这样做的唯一解决方法是,在检索值时,首先调用cache.get(),检查存储在缓存中的对象是否成功,如果不是,再次调用createCache()清除它然后再次获得价值。这样,如果后续的服务调用返回一个有效的对象,它就会被存储,如果没有,每个后续的调用都会清除缓存并再次调用该服务。

 public MyResponse getResponse() {
    MyResponse myResponse = cache.get();
    if (myResponse.isSuccess()) {
      return myResponse;
    } else {
      createCache();
      return cache.get();
    }
  }

但是,在这个解决方案中,如果缓存是空的并且服务返回不成功的响应,它会立即被再次调用。

【问题讨论】:

  • 您在看错地方 IMO。您需要将getResponse() 中的逻辑更改为在所有情况下都不使用cache.get(),因为如果您成功不有条件地缓存它,这将无法返回结果。 IE。在您的else 分支中,您调用myService::call,然后有条件地创建缓存。没有缓存? -> 调用服务,成功了吗? -> 缓存,否则返回错误,不缓存。
  • 你是对的,但我不知道是否可以调用服务,然后选择缓存响应而不再次调用服务——这就是我在这里要问的。 memoizeWithExpiration 方法的全部意义在于服务调用隐藏在缓存的get 方法下。因此,我可以将值放入缓存的唯一方法是调用get 方法,该方法调用下面的服务。如果您知道如何使用 Guava 实现这一点,请写一个答案,我很乐意接受。
  • 此时您偏离了 Guava 的假设。 memoization 代码很简单,因此您可以复制它并使用您的其他用例进行扩展。

标签: java caching guava memoization


【解决方案1】:

您可以在 Service 类或任何其他合适的地方创建一个方法 callUntilSuccess(这里我假设它在您的 Service 中)。您还可以在此方法中定义最大尝试次数,之后它将返回 null,因此您可以避免无限期地调用您的服务(此建议未在下面提供的代码中实现,但这样做很容易)。由于 Guava 方法需要一个供应商,您甚至可以使用此逻辑创建一个 lambda,并将其直接传递给 memoizeWithExpiration 方法。

public MyResponse callUntilSuccess() {

    MyResponse response = myService.call();
    while (!response.isSuccessful()) {
        response = myService.call();
    }
    return response;
}   

然后这样做memoization:

private void createCache() {
    this.cache = Suppliers
         .memoizeWithExpiration(myService::callUntilSuccess, timeout,
                TimeUnit.MINUTES);
}   

【讨论】:

    【解决方案2】:

    这就是你要找的吗?

    
        private void createCache() {
            this.cache = Suppliers.memoizeWithExpiration(
               Suppliers.compose(
                   response -> (response.isSuccess() ? response : null),
                   myService::call
               ),
               timeout,
               TimeUnit.MINUTES
            );
        }
    

    在这里,它会缓存响应,或者为空,这取决于它是否成功。

    更多关于compose的信息在这里https://github.com/google/guava/blob/master/guava/src/com/google/common/base/Suppliers.java#L45

    编辑: 如果你需要在成功时缓存该值,而在失败时将缓存留空,同时返回失败的请求,那么你自己就差不多了,只需在getResponse 中更改返回逻辑,如下所示:

    
        public MyResponse getResponse() {
            final MyResponse myResponse = cache.get();
            if (!myResponse.isSuccess()) {
                this.createCache(); // clear cache
            }
            return myResponse; // don't call .get() again!
        }
    
    

    【讨论】:

    • 是的,就是这样,确实非常简单!另一个人首先回应,所以我接受他的回答,但我也喜欢compose 的想法。谢谢。
    猜你喜欢
    • 2013-11-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多