【问题标题】:How to track a page view using google app engine?如何使用谷歌应用引擎跟踪页面视图?
【发布时间】:2018-01-11 16:20:12
【问题描述】:
我有一个网络应用程序可以让用户提交博客文章。我想跟踪每个博客文章页面的页面浏览量。所以当一些访问时:
/post/123
用户的访问者应该会看到访问过此页面的人数。
我想到的一个看似不可扩展的解决方案是向 Blog 类型添加 page_views 属性:
class Blog(ndb.Model):
title = ndb.StringProperty()
page_views = ndb.IntegerProperty()
然后,每当访问该页面时,只需执行 blog.page_views 加 1。然后 blog.put()。但是,这种尝试意味着我们将过于频繁地写入数据库。
有没有更好的办法?
【问题讨论】:
标签:
python
google-app-engine
analytics
app-engine-ndb
【解决方案1】:
由于 ndb 中的一致性问题,写入数据存储区中的计数器可能非常不准确,尤其是在您的应用程序获得大量流量的情况下。您的一个实例可能会读取 1234 page_views 的当前计数,然后尝试将 1235 写入数据存储。但是,与此同时,可能有其他访问者来过,他们都会看到相同的page_views 值。另外,由于一致性,您获得的读数可能会过时。所以,你的 1235 实际上可能是 1278,甚至更大。
为避免如此多的写入,请考虑在内存缓存中创建计数器,然后增加那里的计数。 Memcache 跨实例持续存在,并且值几乎是瞬时变化的。然后,定期将 Memcache 计数转储到数据存储区,在那里递增,然后将其删除。
例如,每当访问者查看帖子时,都会增加内存缓存计数,并将延迟任务设置为 5 分钟后以将计数持久保存到数据存储中。这样,您就可以在一次写入操作中收集 5 分钟的视图。
Memcache 容易出错,因此您的计数永远不会 100% 准确。但是,每 5 分钟左右转储一次可以减少错误。
【解决方案2】:
如果您的意图是获得非常准确的页面查看次数,是的,您必须将其保存在数据存储中,并且您必须解决超过最大实体组写入速率约 1/秒的风险。这种情况下的典型做法是Sharding counters。
但是,如果您可以不时地丢失一些视图(恕我直言,这完全可以接受),您可以使用不同的策略,使用 memcache 存储计数器和时间戳,您可以对其进行调整在数据存储操作方面要轻松得多。在每个页面视图中,您都会调用一个事务函数(以防止破坏数据存储计数器值),该函数将:
- 增加内存缓存计数器值(如果缺失或无效,则将其设置为 1)
- 检查 memcache 时间戳值,如果有效且“足够新”(可调整)则返回,否则继续
- 用当前时间更新 memcache 时间戳(以防止与下一步一起为另一个并发请求调用相同函数的竞争条件)
- 将内存缓存计数器值添加到数据存储计数器值并保存数据存储计数器;如果此事务失败,则意味着其他并发请求已经在执行此操作,无需执行任何操作(该功能将在此时结束)
- 将内存缓存计数器重置为零
- 可选,如果您不希望有足够长的不活动期,在此期间您可能会丢失内存计数器中累积的大量视图,这些视图可能随时消失 - 将延迟任务(或延迟任务,如果您更喜欢使用延迟库),它也将调用相同的事务函数,但在该运行中除外:
- 它不会增加内存缓存计数器值
- 它不会将另一个延迟任务排入队列
我会选择等于“最近”的任务延迟值。
通过调整“足够近”的值,您可以控制更新数据存储计数器值的频率。
当您想要显示视图数量时,您只需读取数据存储区值和内存缓存值(按此顺序,以防止可能出现的竞争条件,您将两次计算内存缓存计数器值)并添加它们获取访问次数。