如何部署线程安全的异步Rails应用程序? [英] How to deploy a threadsafe asynchronous Rails app?

查看:85
本文介绍了如何部署线程安全的异步Rails应用程序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我已经在网络上阅读了大量有关不同版本的红宝石和滑轨的线程安全性和性能的资料,并且我认为我现在已经很了解这些内容了.

I've read tons of material around the web about thread safety and performance in different versions of ruby and rails and I think I understand those things quite well at this point.

讨论中似乎遗漏的是如何实际部署异步Rails应用程序.在谈论应用程序中的线程和同步性时,人们需要优化两件事:

What seems to be oddly missing from the discussions is how to actually deploy an asynchronous Rails app. When talking about threads and synchronicity in an app, there are two things people want to optimize:

  1. 以最少的RAM使用率利用所有CPU内核
  2. 在先前的请求正在等待IO时能够处理新请求

第1点是人们(正确地)对JRuby感到兴奋的地方.对于这个问题,我只想优化点2.

Point 1 is where people get (rightly) excited about JRuby. For this question I am only trying to optimize point 2.

说这是我的应用程序中唯一的控制器:

Say this is the only controller in my app:

TheController < ActionController::Base
  def fast
    render :text => "hello"
  end

  def slow
    render :text => User.count.to_s
  end
end

fast没有IO,并且每秒可以处理数百或数千个请求,并且slow必须通过网络发送请求,等待工作完成,然后通过网络接收答案,并且因此比fast慢得多.

fast has no IO and can serve hundreds or thousands of requests per second, and slow has to send a request over the network, wait for work to be done, then receive the answer over the network, and is therefore much slower than fast.

因此,理想的部署将允许对slow的请求在IO上等待时满足数百对fast的请求.

So an ideal deployment would allow hundreds of requests to fast to be fulfilled while a request to slow is waiting on IO.

在网络上的讨论中似乎缺少的是堆栈的哪一层负责启用此并发. Thin具有一个--threaded标志,该标志将在线程中[在实验中调用Rack应用程序]" -是否为每个传入请求启动一个新线程?在持久化线程中缓冲机架应用程序实例,并等待传入​​请求?

What seems to be missing from the discussions around the web is which layer of the stack is responsible for enabling this concurrency. thin has a --threaded flag, which will "Call the Rack application in threads [experimental]" -- does that start a new thread for each incoming request? Spool up rack app instances in threads that persist and wait for incoming requests?

是唯一的方法吗?还是有其他方法吗? ruby运行时对优化点2有影响吗?

Is thin the only way or are there others? Does the ruby runtime matter for optimizing point 2?

推荐答案

适合您的方法很大程度上取决于您的slow方法的工作方式.

The right approach for you depends heavily on what your slow method is doing.

在理想世界中,您可以使用 sinatra-synchrony 宝石之类的东西来处理光纤中的每个请求.您只会受到最大光纤数量的限制.不幸的是,光纤上的堆栈大小是硬编码的,并且很容易在Rails应用程序中被超载.此外,由于异步IO启动后会自动产生数据,因此我阅读了一些有关调试光纤的困难的恐怖故事.当使用纤维时,竞赛条件仍然是可能的.目前,至少在Web应用程序的前端,光纤Ruby还是一个贫民窟.

In a perfect world, you could use use something like the sinatra-synchrony gem to handle each request in a fiber. You'd only be limited by the maximum number of fibers. Unfortunately, the stack size on fibers is hardcoded, and it is easy to overrun in a Rails app. Additionally, I've read a few horror stories of the difficulties of debugging fibers, due to the automatic yielding after async IO has been initiated. Race conditions are still possible when using fibers, as well. Currently, fibered Ruby is a bit of a ghetto, at least on the front-end of a web app.

一种更实用的解决方案,不需要更改代码,那就是使用带有工作线程池(例如Rainbows)的Rack服务器!或彪马.我相信Thin的--threaded标志可以在一个新线程中处理每个请求,但是拆分本机OS线程并不便宜.最好在线程池设置足够大的情况下使用线程池.在Rails中,不要忘记在生产环境中设置config.threadsafe!.

A more pragmatic solution that doesn't require code changes is to use a Rack server that has pool of worker threads such as Rainbows! or Puma. I believe Thin's --threaded flag handles each request in a new thread, but spinning up a native OS thread is not cheap. Better to use a thread pool with the pool size set sufficiently high. In Rails, don't forget to set config.threadsafe! in production.

如果您同意更改代码,可以查看Konstantin Haase出色的实时对话机架.他讨论了如何使用EventMachine::Deferrable类在基于Rack的传统请求/响应周期之外生成响应.这看起来确实很整洁,但是您必须以异步样式重写代码.

If you're OK with changing code, you can check out Konstantin Haase's excellent talk on real-time Rack. He discusses using the EventMachine::Deferrable class to produce a response outside of the traditional request/response cycle that Rack is built on. This seems really neat, but you have to rewrite the code in an async style.

还可以查看抽筋巨人.这些使您可以在与Rails应用程序一起托管的单独的Rack应用程序中实现slow方法,但是您可能还必须重写代码才能在Cramp/Goliath处理程序中工作.

Also take a look at Cramp and Goliath. These let you implement your slow method in a separate Rack app that is hosted alongside your Rails app, but you will probably have to rewrite your code to work in the Cramp/Goliath handlers as well.

关于您的Ruby运行时问题,还取决于slow所做的工作.如果您要进行大量的CPU计算,则可能会遇到GIL带来问题的风险.如果您正在执行IO,则GIL 会妨碍您. (我说不应该,因为我相信我已经阅读了有关较旧的mysql gem阻止GIL的问题.)

As for your question about the Ruby runtime, it also depends on the work that slow is doing. If you're doing CPU-heavy computation, then you run the risk of the GIL giving you issues. If you're doing IO, then the GIL shouldn't get in your way. (I say shouldn't because I believe I've read about issues with the older mysql gem blocking the GIL.)

就个人而言,我已经成功地将sinatra-synchrony用于后端mashup Web服务.我可以并行向外部Web服务发出多个请求,然后等待所有请求返回.同时,前端Rails服务器使用线程池,并直接向后端发出请求.尚不完美,但目前效果很好.

Personally, I've had success using sinatra-synchrony for a backend, mashup web service. I can issue several requests to external web services in parallel, and wait for all of them to return. Meanwhile, the frontend Rails server uses a thread pool, and makes requests directly to the backend. Not perfect, but it works well enough right now.

这篇关于如何部署线程安全的异步Rails应用程序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆