简体   繁体   English

如何实现一个以点击次数为值的番石榴缓存?

[英]How to implement a Guava Cache with the value as number of hits?

I'm trying to implement a cache which will count the number of login attempts in the last 5 minutes, in my code i want to check if the user has attempted more than MAX_ATTEMPTS.我正在尝试实现一个缓存,它将计算过去 5 分钟内的登录尝试次数,在我的代码中,我想检查用户是否尝试了超过 MAX_ATTEMPTS。

In all the code examples i found online for "Guava Cache" use the load method to fetch the value from some other source or calculate it using some method, how can i increment it every time there is a cache hit ?在我在网上找到的“番石榴缓存”的所有代码示例中,使用加载方法从其他来源获取值或使用某种方法计算它,我如何在每次缓存命中时增加它?

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

later at runtime i would like to check:稍后在运行时我想检查:

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

and also increment it if:并在以下情况下增加它:

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

@Oren Your solution isn't thread-safe, since you're operating on a value outside Cache. @Oren 您的解决方案不是线程安全的,因为您正在对 Cache 之外的值进行操作。 You'd better use Cache#asMap() view and change value inside ConcurrentMap#compute(K, BiFunction<K, V, V>) method:您最好使用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;
  }
});

You can try and adapt the following snippet:您可以尝试修改以下代码段:

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.");
    }
}

SOLVED by using only the Cache functionality of Guava ( without using the loader )仅使用 Guava 的缓存功能解决(不使用加载器)

The cache looks like this:缓存看起来像这样:

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;
    }

}

and the use of it:以及它的使用:

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));
}

Thanks spi for this solution.感谢 spi 提供此解决方案。 (now i'm looking for a way to test it without having a test that runs 24 hours.) (现在我正在寻找一种方法来测试它,而无需运行 24 小时的测试。)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM