在 Rails 中处理数百个并发请求

发布于 2024-11-18 02:52:49 字数 320 浏览 3 评论 0原文

我正在编写一个 Ruby on Rails 应用程序,该网站最重要的功能之一是实时投票。我们完全期望在短短 1 分钟内收到 10,000 个投票请求。加上其他请求,这意味着我们可能会收到大量请求。

我最初的想法是设置服务器使用 apache + phusion,但是,对于投票,我正在考虑编写一个 php 脚本并在 memcached 中写入/读取信息。数据只需要保留大约15分钟,因此1分钟内向数据库写入10,000次似乎毫无意义。我们还需要标记用户的 ip,这样他们就不会投票两次,从而在 memcached 中变得额外复杂。

如果有人有任何建议或想法可以使这项工作尽可能最好,请提供帮助。

I am writing a ruby on rails application and one of the most important featuers of the website is live voting. We fully expect that we will get 10k voting requests in as little as 1 minutes. Along with other requests that means we could be getting a ton of requests.

My initial idea is to set up the server to use apache + phusion, however, for the voting specifically I'm thinking about writing a php script on side and to write/read the information in memcached. The data only needs to persist for about 15 minutes, so writing to the database 10,000 times in 1 minute seems pointless. We also need to mark the ip of the user so they don't vote twice thus being extra complicated in memcached.

If anyone has any suggestions or ideas to make this work as best as possible, please help.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

↘紸啶 2024-11-25 02:52:49

如果您正在为这种大规模涌入构建应用程序,那么您将需要将其基本组件精简到绝对最低限度。

使用完整的 Rails 堆栈来实现这种强度并不实际,也没有必要。最好构建一个非常薄的 Rack 层,通过直接 DB 调用来处理投票,甚至跳过 ORM,基本上是 INSERT 语句的包装器。 Sinatra 和 Sequel(作为高效的查询生成器)可能会对此有所帮助。

您还应该确保正确调整数据库,并对数据库运行许多负载测试,以确保其按预期执行,并为更高的负载留出足够的空间。

一分钟内进行 10,000 个数据库调用并不是什么大问题,在正确调整的堆栈上,每个调用只需要几分之一毫秒。 Memcached 可以提供更高的性能,特别是如果结果不是永久性的。 Memcached 有一个原子增量运算符,这正是您在简单地统计投票时所需要的。 Redis也是一个非常强大的临时存储。

另一个想法是完全废弃数据库并编写一个持久服务器进程,该进程使用基于 JSON 的简单协议。如果您致力于使用 Ruby,Eventmachine 非常适合将这些东西组合在一起;如果您愿意用 JavaScript 构建专门的计数服务器,则 NodeJS 也是如此。

即使在使用专用服务器进程的普通硬件上,也可以轻松实现一分钟 10,000 次操作,而无需完整数据库堆栈的开销。

您只需确保您的范围得到很好的定义,以便您可以在部署之前测试并严重滥用您的实现。

由于您所描述的本质上相当于哈希查找,因此基本代码很简单:

contest = @contest[contest_id]

unless (contest[:voted][ip])
  contest[:voted][ip] = true
  contest[:votes][entry_id] += 1
end

每秒运行数十万次是完全实用的,因此唯一的开销是包装 JSON 层它。

If you're architecting an app for this kind of massive influx, you're going to need to strip down the essential components of it to the absolute minimum.

Using a full Rails stack for that kind of intensity isn't really practical, nor necessary. It would be much better to build a very thin Rack layer that handles the voting by making direct DB calls, skipping even an ORM, basically being a wrapper around an INSERT statement. This is something Sinatra and Sequel, which serves as an efficient query generator, might help with.

You should also be sure to tune your database properly, plus run many load tests against it to be sure it performs as expected, with a healthy margin for higher loading.

Making 10,000 DB calls in a minute isn't a big deal, each call will take only a fraction of a millisecond on a properly tuned stack. Memcached could offer higher performance especially if the results are not intended to be permanent. Memcached has an atomic increment operator which is exactly what you're looking for when simply tabulating votes. Redis is also a very capable temporary store.

Another idea is to scrap the DB altogether and write a persistent server process that speaks a simple JSON-based protocol. Eventmachine is great for throwing these things together if you're committed to Ruby, as is NodeJS if you're willing to build out a specialized tally server in JavaScript.

10,000 operations in a minute is easily achievable even on modest hardware using a specialized server processes without the overhead of a full DB stack.

You will just have to be sure that your scope is very well defined so you can test and heavily abuse your implementation prior to deploying it.

Since what you're describing is, at the very core, something equivalent to a hash lookup, the essential code is simply:

contest = @contest[contest_id]

unless (contest[:voted][ip])
  contest[:voted][ip] = true
  contest[:votes][entry_id] += 1
end

Running this several hundred thousand times in a second is entirely practical, so the only overhead would be wrapping a JSON layer around it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文