如何实现以命中数作为值的Guava缓存?

时间:2018-10-08 13:50:53

标签: java guava

我正在尝试实现一个缓存,该缓存将计算最近5分钟内登录尝试的次数,在我的代码中,我想检查用户是否尝试了超过MAX_ATTEMPTS次。

我在网上找到的所有针对“ Guava Cache”的代码示例都使用load方法从其他来源获取值或使用某些方法对其进行计算,那么每次遇到缓存命中时如何增加它的值?

static LoadingCache<String, Integer> cache = CacheBuilder.newBuilder()
    .maximumSize(100000)
    .expireAfterAccess(5, TimeUnit.MINUTES)
    .build(
            new CacheLoader<String, Integer>() {
                public Integerload(String user) {
                       return ????;
                }
            }
    );

稍后在运行时我要检查:

if(cache.getIfPresent(user) != null && cache.get(user) > MAX_ATTEMPTS)

,并在以下情况下增加它:

if(cache.getIfPresent(user) != null && cache.get(user) <= MAX_ATTEMPTS)

3 个答案:

答案 0 :(得分:1)

您可以尝试修改以下代码段:

public class Demo {
    public static void main(String[] x) {
        CacheLoader<String, AtomicInteger> initialValueLoader = new CacheLoader<String, AtomicInteger>() {
            @Override
            public AtomicInteger load(String key) {
                // do not care of the key. everybody starts with 0 login attempts.
                return new AtomicInteger(0);
            }
        };

        LoadingCache<String, AtomicInteger> c = CacheBuilder
            .newBuilder()
            .maximumSize(100000)
            .expireAfterAccess(2, TimeUnit.SECONDS)
            .build(initialValueLoader);

        String user = "bob";

        try {
            // when the user tries to login, increment the attemps by one
            Verify.verify(c.get(user).incrementAndGet() == 1);

            // next call will get one
            Verify.verify(c.get(user).get() == 1);
        } catch (ExecutionException e) {
            throw new RuntimeException("oups: " + e, e);
        }

        // simulate time
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
        }

        // after some time, the cache is cleared
        try {
            Verify.verify(c.get(user).get() == 0);
        } catch (ExecutionException e) {
            throw new RuntimeException("oups: " + e, e);
        }

        System.out.println("done.");
    }
}

答案 1 :(得分:1)

@Oren您的解决方案不是线程安全的,因为您使用的是Cache外部的值。您最好使用Cache#asMap()视图并在ConcurrentMap#compute(K, BiFunction<K, V, V>)方法内更改值:

forgetPasswordCache.asMap().compute(email, (cachedEmail, currentCount) -> {
  if (currentCount != null && currentCount >= RESET_PASSWORD_MAX_ATTEMPTS) {
    logger.error("User with id: " + user.getId() + " and email: " + email +
         " has reached the maximum number of reset password attempts, the mail will not be sent");
    return null;
  }

  if (currentCount == null) {
    return 1;
  } else {
    return currentCount + 1;
  }
});

答案 2 :(得分:-1)

已解决,仅使用Guava的缓存功能(不使用加载程序)

缓存看起来像这样:

public class ForgetPasswordAttemptsCache {

    private static final Cache<String, Integer> forgetPasswordCache = CacheBuilder.newBuilder()
            .expireAfterAccess(24, TimeUnit.HOURS)
            .build();

    private ForgetPasswordAttemptsCache(){
    }

    public static Cache<String, Integer> getInstance() {
        return forgetPasswordCache;
    }

}

及其用途:

final String email = user.getEmail();
Integer currentCount = ForgetPasswordAttemptsCache.getInstance().getIfPresent(email);

if (currentCount != null && currentCount >= RESET_PASSWORD_MAX_ATTEMPTS) {
    logger.error("User with id: " + user.getId() + " and email: " + email +
            " has reached the maximum number of reset password attempts, the mail will not be sent");
    return;
}

if (currentCount == null) {
    ForgetPasswordAttemptsCache.getInstance().put(email, new Integer(1));
} else {
    ForgetPasswordAttemptsCache.getInstance().put(email, new Integer(currentCount + 1));
}

感谢spi提供此解决方案。 (现在,我正在寻找一种无需运行24小时即可进行测试的方法。)