gpt4 book ai didi

java - 线程安全的全局内存计数器,每 x 递增一次刷新到 mysql

转载 作者:塔克拉玛干 更新时间:2023-11-01 22:15:23 25 4
gpt4 key购买 nike

是否可以创建一个所有 servlet 都将使用的内存计数器?

此全局计数器将跟踪 Web 应用程序的页面浏览量,并且该计数器将特定于当前登录的用户。即该集合将为每个用户提供一个 key 。

globalCounterMap[userId].incrementCounter += 1;

在某个时间间隔或浏览量计数,我想将当前计数保存到 mysql(插入新行)例如:

table_pageviews [id, userId, pageview_count, date]

所以这个计数器会在刷新后重置为 0。

因此,如果我有一个所有 servlet 都将从其继承的 BaseServlet,我将如何定义该字段? (最终的,静态的?)

ConcurrentHashMap 合适吗?也许我可以为每个条目的值存储一个 AtomicLong。

在刷新期间,我可以通过设置为 0 来使用原子 long 的 getAndSet,并保存我“获得”的值:http://docs.oracle.com/javase/1.5.0/docs/api/java/util/concurrent/atomic/AtomicLong.html

在刷新到 mysql 的过程中我必须同步吗? (假设我每 1000 次网页浏览就这样做)

更新

因此,即使我有 10 台服务器,每台服务器都有自己的内存计数器,事情仍然有效,因为它们最终都会将它们的计数刷新到数据库,然后我将简单地聚合行以获得最终计数。

最佳答案

正如 Konstantin 所说,redis 之类的东西可能是更好的解决方案。 Cassandra 计数器也是执行此类操作的一种非常好的方法。

如果您想使用 java 执行此操作,这里有一些代码可以安全且无阻塞地增加计数,

class Counter {

private final ConcurrentHashMap<String, AtomicInteger> counts = new ConcurrentHashMap<String, AtomicInteger>();

//increment the count for the user
public void increment(String user) {
while(true) {
AtomicInteger current = counts.get(user);
if(current == null) {
//new user, initialize the count
counts.putIfAbsent(user, new AtomicInteger());
continue;
}

int value = current.incrementAndGet();
if(value > 0) {
//we have incremented the counter
break;
} else {
//someone is flushing this key, remove it
//so we can increment on our next iteration
counts.replace(user, current, new AtomicInteger());
}

}
}

//call this periodically to flush keys to the database
//this will empty the counts map so that users who
//are not active do not take up space
public void flush() {
Map<String, Integer> toFlush = new HashMap<String, Integer>();

for(Map.Entry<String, AtomicInteger> entry : counts.entrySet()) {
String user = entry.getKey();
AtomicInteger currentCount = entry.getValue();
//stop incrementing this count
counts.remove(user, currentCount);
//if someone is trying to increment this AtomicInteger after
//we remove it, they will see a -ve value from incrementAndGet, and
//will know their increment did not succeed
Integer count = currentCount.getAndSet(Integer.MIN_VALUE);
toFlush.put(user, count);
}

for(Map.Entry<String, Integer> clearedEntry : toFlush.entrySet()) {
writeToDb(clearedEntry.getKey(), clearedEntry.getValue());
}

}

public void writeToDb(String user, int count) {
//do something with the count here
}


}

代码相当复杂,正如 Peter Lawrey 所说,使用 synchronized 关键字保护的简单映射可能会表现得足够好并且更容易维护。

关于java - 线程安全的全局内存计数器,每 x 递增一次刷新到 mysql,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/8477352/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com