簡體   English   中英

如何實現一個以點擊次數為值的番石榴緩存?

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

我正在嘗試實現一個緩存,它將計算過去 5 分鍾內的登錄嘗試次數,在我的代碼中,我想檢查用戶是否嘗試了超過 MAX_ATTEMPTS。

在我在網上找到的“番石榴緩存”的所有代碼示例中,使用加載方法從其他來源獲取值或使用某種方法計算它,我如何在每次緩存命中時增加它?

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)

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

您可以嘗試修改以下代碼段:

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

僅使用 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 小時的測試。)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM