9

如何实现类似于 quora 的动态更新投票计数:- 每当用户对答案进行投票时,它会自动反映给正在查看该页面的每个人。

我正在寻找以下地址的答案:

  • 我们是否必须继续轮询每个答案的投票计数,如果是,那么如何管理由于如此多的用户
    轮询投票而产生的服务器负载。
  • 或者使用 websockits/推送通知,这些的可扩展性如何?
  • 如何将赞成/反对票数存储在数据库/内存中以支持这一点。他们如何控制读/写次数。我的后端数据库是 mysql

我正在寻找的答案可能并不完全是 quora 是如何做到的,而可能是如何使用可用的开源技术来做到这一点。

4

3 回答 3

7

您需要担心的不是后端系统细节,而是前端。在任何实际规模下,始终打开连接都是不切实际的。相反,您希望相反 - 能够尽可能快地从后端服务和关闭连接。

Websockets 是一项性感的技术,但同样,在现实世界中,代理存在问题,如果您正在开发应该在各种屏幕(台式机、平板电脑、移动设备)上工作的东西,它可能会成为您的担忧。即使是古老的长民意调查也可能无法通过防火墙和代理。

这是一个好消息:我认为

“继续投票以获取每个答案的赞成票数”

在这种情况下是一个完全好的解决方案。考虑以下:

  • 您的用例不需要任何实时更新。稍后看到计数器更新几乎没有什么坏处
  • 对于非常受欢迎的主题,无论如何您都想将多个赞成/反对票压缩为一个
  • 大多数主题在几天/几周内根本看不到赞成/反对投票的流量,因此保持连接打开,等待永远不会发生的事件是一种浪费
  • 大多数用户永远不会对刚来阅读某个主题的人投赞成票/反对票,因此您对主题统计数据的读/写比率将大大偏向于阅读
  • 客户端之间的网络延迟差异很大,您会看到 100B http 响应的可怕传输速率,而这个缓慢的客户端正在逐字节获取他的响应,您的宝贵服务器连接更重要的是 - 后端服务器上的线程正忙

这是我要开始的:

  • 在主页加载后,让浏览器定期轮询新的主题统计信息
  • 保留您的 MySQL,保留计数器。每次有赞成/反对票时更新数据库
  • 将 Memcached 作为直写缓存放在 DB 前面,即每次有向上/向下投票更新缓存时,然后更新 DB。将计数器的显式过期时间设置为 10-15 分钟。每次更新计数器时,过期时间都会自动延长。
  • 将这些轮询 http 调用设计为可被 http 代理缓存,将 expire 和 ttl http 标头设置为 60 秒
  • 在前端服务器前面放置一个反向代理(Varnishnginx ),让这个代理缓存上述轮询调用。这些负责二级缓存并帮助更快地释放后端服务器线程,请参阅上面的网络延迟问题
  • 设置您的反向代理组件以直接与 memcached 服务器对话,而无需调用后端服务器,是的,如果您可以同时使用 Varnish 和 nginx。
  • 存储此类数据没有花哨的模式,它是inc()/dec()memcached 中的一个简单操作,请注意,从竞争条件的角度来看它是安全的。在 MySQL 中也是一个安全的原子操作UPDATE table SET field = field + 1 WHERE [...]

积极的多级缓存涵盖了您的read路径:在 Memcached 和所有 http 缓存中,请注意这些 http 轮询请求也将被缓存在边缘

为了照顾不受欢迎的话题的长尾 - 使此类响应的 http ttl 与受欢迎程度成反比。

当 http 缓存过期并且 memcached 也没有它时,读取请求只会很少到达前端服务器。如果这仍然是一个问题,请添加 memecached 服务器并全面增加 memcached 中的过期时间。

完成后,您就完成了所有reads工作。根据规模的不同,您可能仍然存在的唯一问题是高writes/低投票率高。这是您的单个 MySQL 实例可能开始显示一些滞后的地方。不要害怕 - 沿着老路继续分片您的实例,或仅为计数器添加 NoSQL 存储。

除非绝对必要或者你想找个借口玩它,否则不要使用任何消息传递系统。

于 2014-02-15T07:45:16.597 回答
5

Websockets、服务器发送事件(我认为这就是你所说的“推送通知”)和 AJAX 长轮询具有相同的缺点——它们使底层 TCP 连接长时间保持打开状态。
所以问题是服务器可以处理多少个打开的 TCP 连接。基本上,它取决于它的操作系统、文件描述符的数量(一个配置参数)和可用内存(每个打开的连接都保留一个读/写缓冲区)。 这里有更多内容。

我们曾经测试过在单个服务器上保持 100 万个 websocket 连接打开的可能性(Windows 7 x64 具有 16Gb 的 RAM,JVM 1.7 具有 8Gb 的堆,使用Undertow 测试版来服务 Web 请求)。令人惊讶的是,最难的部分是在服务器上产生负载)
它设法保持了 1M。但同样,服务器没有做任何有用的事情,只是收到请求,进行协议升级并保持这些连接打开。无论出于何种原因,也有一些丢失的连接。我们没有调查。但在生产中,您还必须 ping 服务器并处理重新连接。

除此之外,Websockets 在这里看起来有点矫枉过正,SSE 仍然没有被广泛采用。 所以我会选择好的旧 AJAX 轮询,但尽可能优化它。
适用于任何地方,易于实现和调整,不依赖外部系统(我有几次糟糕的经验),优化的可能性。例如,您可以在单个浏览器中对所有打开的文章进行分组更新,或者根据文章的受欢迎程度调整更新间隔。
毕竟,这里似乎不需要实时通知。

于 2014-02-14T14:58:43.360 回答
2

听起来你可能可以使用像 Kafka、RabbitMQ 或 ActiveMQ 这样的消息传递系统。您的前端会将投票发送到消息通道并通过侦听器接收它们,并且您可以让服务器端部分将投票定期保存到数据库。

您还可以通过轮询数据库来完成任务,并通过存储过程增加/减少与帖子相关的数字......这里有很多选项,这取决于您可能面临的并发量。

于 2014-02-09T14:56:54.953 回答