Discourse 会追踪用户在屏幕上看到的每篇帖子的阅读时长。该系统多年来不断演进,我常发现需要回顾代码来弄清楚其工作原理及存在原因。
本文介绍了当前实现中一些令人头疼的技术细节。
Discourse 客户端如何追踪时间
帖子时间追踪功能实现在 screen-track.js.es6 中。该模块负责追踪帖子在屏幕上停留的时间以及主题在屏幕上停留的时间。
当主题页面“滚动”时,它会通知屏幕追踪器当前视图中有哪些帖子,以及其中哪些帖子已被阅读。我们将部分可见的帖子视为“在视图中”。
随后,屏幕追踪器每秒触发一次“滴答”(tick),以决定需要向服务器发送哪些数据。
屏幕追踪器将维护多个列表:
a. 尚未发送到服务器的(帖子/阅读该帖子所花时间)列表
b. 已知已被阅读的帖子列表
c. 已知当前在屏幕上的帖子列表
在每个“滴答”开始时(每秒一次),如果列表 (a) 中有帖子,我们将考虑将其发送到服务器:
-
如果自上次向服务器发送数据以来,已过去
flush_timing_secs设置值(默认为 60 秒)的时间。 -
如果用户尚未阅读列表中的任何帖子,我们将立即发送整个列表。
在每个“滴答”结束时,如果 Discourse 处于焦点状态:
如果存在任何“在屏幕上的帖子”,我们将为每个帖子记录“1 个滴答”的时间。
如果我们在任何时刻离开该主题(导航到 Discourse 中的其他位置)
我们将立即把列表 (a) 中所有“在传输中”的数据发送到服务器。
限制
-
每次查看主题时,每篇帖子最多记录 6 分钟 的阅读时间(如果您导航离开后再返回该帖子,此计时将重置)。
-
如果 3 分钟 内您完全没有滚动,我们将禁用此子系统,直到再次发生滚动。
-
对于匿名用户,我们将最多记录 5 个主题 的时间数据(当用户注册后,这些数据会转换为
posts_timing表中的数据)。
关键观察
-
尽管
post_timings表可以精确到毫秒,但由于“滴答”触发的时机不同,每篇帖子实际上存在 0-1000 毫秒 的“未记录”时间。 -
每次“会话”查看主题时,每篇帖子最多可记录 6 分钟阅读时间。每篇帖子的阅读时间没有上限;如果用户多次返回主题,一篇帖子可以被阅读数天。
我们如何使用这些数据?
我们使用的最关键信息是“用户 X 是否阅读了帖子 Y",这决定了主题中的未读数量以及大量其他关键数据。
除了这种二元用途外,我们还使用 post_timings 中记录的时间来计算帖子的 avg_time。
帖子的平均时间计算方式为:对阅读时间的自然对数取平均,再求指数(即几何平均数)。
例如:
帖子 1:sam,10 秒
帖子 2:jane,1 小时
avg_time = exp((log(3600000) + log(10000)) / 2)
=~ exp((15.09 + 9.2) / 2)
=~ 189094
=~ 189 秒
该 avg_time 随后被用作“帖子得分”计算器的一个组成部分 a component。
得分 = 5 × 回复数 + 15 × 点赞得分 + 5 × 入站链接数 + 2 × 书签数 + 0.05 × avg_time + 0.2 × 帖子阅读数。
因此,在上述例子中,平均阅读 189 秒相当于 37 分。这大约相当于 2 个多一点点赞,或 72 次阅读。
“帖子得分”随后用于“最佳帖子”功能,以确定主题中哪些帖子表现最佳。


