使用并发,线程,静态变量实现GAE / Java计数器

时间:2012-03-02 15:46:39

标签: java multithreading google-app-engine concurrency thread-safety

我已经阅读了很多关于计数器的GAE文章以及GAE中令人讨厌的写入限制。我已经看到了带有分片,cron任务,内存缓存等的解决方案。然后我学到了足够多的java线程能够提出这个问题:

  

问:我们可以使用并发/在servlet线程中实现一个计数器   servlet static varibales?

这将带来额外的好处,即减少对数据存储区和内存缓存的写入(花费相同的$),并删除cron要求,因为一系列快速计数器命中中的最后一个servlet将更新数据存储区。

我不太了解并发编程以提出解决方案,但我想象一下静态servlet变量,可能是“atomic-integers”和last-update标志,以便检查这个servlet是否是最后一个servlet更新静态var,在最后200ms内,从而触发保存到数据存储区。

// Servlet gets a hit
// static var counter++
// Checks last datastore save time of the static var, if longer than 200ms
// save to datastore & save "last update time"
// If shorter than 200 ms ago, let the next servlet call update the datastore

可以这样做吗?有什么建议? Mucho赞赏你的想法。

3 个答案:

答案 0 :(得分:5)

只是详细说明我的评论:

您可能已经注意到,App Engine会在达到某些流量阈值时启动应用的新实例。鉴于新的实例可以在完全不同的服务器上运行,您的静态计数器将变为无效。

现在只是澄清一下,在App Engine中实现计数器的正确方式是使用Sharding并将其拆分为多个entites。

现在假设您希望最小化数据存储区调用,您可以使用memcache来存储计数器数据,并以指定的频率将其写入数据存储区(例如通过cron)。您的memcache数据将在应用的所有实例中保持一致。

当memcache发生故障时,你当然会冒着丢失你的memcache计数器的风险,但这就是把它写到数据存储区的工作。同样,这不是一个100%万无一失的解决方案,分片是;但这是最小化数据存储区调用的一种方法。

答案 1 :(得分:0)

Guido van Rossum在他的博客上article谈到这一点。据我了解,你处于竞争状态问题。我对Java一无所知。但是guido链接到一个用于Java的Memcache API Memcache服务。也许这可以帮助您找到解决方案。

答案 2 :(得分:0)

如果您正在计算“关键任务”的内容,请不要假设同一个实例将处理下一个请求或任何后续请求。 App Engine能够快速扩展以处理需求高峰的另一面是,当尖峰通过时,实例会消失。实例也可能因其他原因而消失。当计数到右边时,分片计数器是可行的方法。

将内存缓存作为缓存之外的任何其他内容通常都是错误的,可以任意缩短生命周期。

如果大致计数可以,那么你有几个选择。你可以做一些事情,比如在memcache中累积计数,每隔N次点击将数量计算到数据存储区,或者你可以使用受锁保护的全局数据来执行相同操作。