我整理了一个简单的示例,尝试使用一个基本示例来证明Rails中的并发请求。请注意,我正在使用MRI Ruby2和Rails 4.2。
def api_call
sleep(10)
render :json => "done"
end
然后,我在Mac(I7/4核)的Chrome上转到4个不同的选项卡,查看它们是串行还是并行运行(真正的并发运行是很接近的,但不是同一件事)。即http://localhost:3000/api_call
我无法使用Puma,Thin或Unicorn来工作。每个请求都是按顺序提出的。 10秒后显示第一个选项卡,20秒后显示第二个选项卡(因为它必须等待第一个选项卡完成),此后再显示第三个选项卡...。
根据我的阅读,我相信以下内容是正确的(请纠正我),并且是我的结果:
-unicorn.rb
worker_processes 4
preload_app true
timeout 30
listen 3000
after_fork do |server, worker|
ActiveRecord::Base.establish_connection
end
所以,
有一个非常大的similar question to mine,但是我无法让它按已回答的方式工作,它无法回答我有关使用MRI Ruby的并发请求的所有问题。
Github项目:https://github.com/afrankel/limitedBandwidth(注意:该项目所关注的不仅仅是服务器上的多进程/线程问题)
最佳答案
我邀请您阅读Jesse Storimer的Nobody understands the GIL系列
它可以帮助您更好地了解MRI的内部构造。
我还发现了Pragmatic Concurrency with Ruby,它看起来很有趣。它具有并发测试的一些示例。
编辑:
另外我可以推荐文章Removing config.threadsafe!
可能与Rails 4不相关,但是它解释了配置选项,您可以使用其中之一来允许并发。
让我们讨论您问题的答案。
您甚至可以使用Puma拥有多个线程(使用MRI)。 GIL确保一次仅一个线程处于 Activity 状态,这就是开发人员将其称为限制性的约束(因为没有真正的并行执行)。请记住,GIL不保证线程安全。
这并不意味着其他线程没有在运行,而是在等待轮到他们。它们可以交错(文章可以帮助更好地理解)。
让我澄清一些术语:工作进程,线程。
进程在单独的内存空间中运行,可以服务多个线程。
同一进程的线程在共享内存空间中运行,这就是它们的进程。对于线程,在本文中我们指的是Ruby线程,而不是CPU线程。
关于您问题的配置和您共享的GitHub存储库,我认为一个适当的配置(我使用Puma)是设置4个工作程序和1至40个线程。这个想法是由一个 worker 提供一个选项卡。每个标签最多可发送10个请求。
因此,让我们开始吧:
我在虚拟机上使用Ubuntu。因此,首先,我在虚拟机的设置中启用了4个核心(以及我认为可能会有帮助的其他一些设置)。
我可以在我的机器上验证这一点。所以我同意了。
Linux command --> lscpu
Architecture: x86_64
CPU op-mode(s): 32-bit, 64-bit
Byte Order: Little Endian
CPU(s): 4
On-line CPU(s) list: 0-3
Thread(s) per core: 1
Core(s) per socket: 4
Socket(s): 1
NUMA node(s): 1
Vendor ID: GenuineIntel
CPU family: 6
Model: 69
Stepping: 1
CPU MHz: 2306.141
BogoMIPS: 4612.28
L1d cache: 32K
L1d cache: 32K
L2d cache: 6144K
NUMA node0 CPU(s): 0-3
我使用了您共享的GitHub项目,并对其进行了一些修改。我创建了一个名为
puma.rb
的Puma配置文件(将其放在config
目录中),其内容如下:workers Integer(ENV['WEB_CONCURRENCY'] || 1)
threads_count = Integer(ENV['MAX_THREADS'] || 1)
threads 1, threads_count
preload_app!
rackup DefaultRackup
port ENV['PORT'] || 3000
environment ENV['RACK_ENV'] || 'development'
on_worker_boot do
# Worker specific setup for Rails 4.1+
# See: https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server#on-worker-boot
#ActiveRecord::Base.establish_connection
end
默认情况下,Puma以1个工作人员和1个线程启动。您可以使用环境变量来修改那些参数。我是这样做的:
export MAX_THREADS=40
export WEB_CONCURRENCY=4
要使用此配置启动Puma,请输入
bundle exec puma -C config/puma.rb
在Rails应用目录中。
我用四个选项卡打开了浏览器,以调用应用程序的URL。
第一个请求在15:45:05左右开始,最后一个请求在15h49:44左右开始。那是经过4分39秒的时间。
您还可以在日志文件中按未排序的顺序查看请求的ID。 (见下文)
GitHub项目中的每个API调用都会休眠15秒。我们有四个4个标签,每个标签有10个API调用。最长经过时间为600秒,即10分钟(在严格的串行模式下)。
从理论上讲,理想的结果应该是并行的,并且耗时不远于15秒,但是我完全没有想到。
我不确定确切的结果是什么,但是我仍然感到非常惊讶(考虑到我在虚拟机上运行并且MRI受GIL和其他一些因素的限制)。该测试的经过时间少于最大经过时间的一半(在严格的串行模式下),我们将结果切成少于一半。
随时检查一下,并让我知道您的读者可能有任何反馈。
我的机器上仍然有克隆。如果您有兴趣,请告诉我。
要按顺序回答您的问题:
答案是:MRI是多线程的,但受GIL限制为一个 Activity
一次线程。这就提出了一个问题:核磁共振成像不是更好吗
用更多的进程和更少的线程进行测试?我真的不知道
最初的猜测不会有很大的不同,也不会有很大的不同。也许有人可以阐明这一点。
修改。
附录
日志文件Rails应用程序:
**config.allow_concurrency = false (by default)**
-> Ideally 1 worker per core, each worker servers up to 10 threads.
[3045] Puma starting in cluster mode...
[3045] * Version 2.11.2 (ruby 2.1.5-p273), codename: Intrepid Squirrel
[3045] * Min threads: 1, max threads: 40
[3045] * Environment: development
[3045] * Process workers: 4
[3045] * Preloading application
[3045] * Listening on tcp://0.0.0.0:3000
[3045] Use Ctrl-C to stop
[3045] - Worker 0 (pid: 3075) booted, phase: 0
[3045] - Worker 1 (pid: 3080) booted, phase: 0
[3045] - Worker 2 (pid: 3087) booted, phase: 0
[3045] - Worker 3 (pid: 3098) booted, phase: 0
Started GET "/assets/angular-ui-router/release/angular-ui-router.js?body=1" for 127.0.0.1 at 2015-05-11 15:45:05 +0800
...
...
...
Processing by ApplicationController#api_call as JSON
Parameters: {"t"=>"15?id=9"}
Completed 200 OK in 15002ms (Views: 0.2ms | ActiveRecord: 0.0ms)
[3075] 127.0.0.1 - - [11/May/2015:15:49:44 +0800] "GET /api_call.json?t=15?id=9 HTTP/1.1" 304 - 60.0230
**config.allow_concurrency = true**
-> Ideally 1 worker per core, each worker servers up to 10 threads.
[22802] Puma starting in cluster mode...
[22802] * Version 2.11.2 (ruby 2.2.0-p0), codename: Intrepid Squirrel
[22802] * Min threads: 1, max threads: 40
[22802] * Environment: development
[22802] * Process workers: 4
[22802] * Preloading application
[22802] * Listening on tcp://0.0.0.0:3000
[22802] Use Ctrl-C to stop
[22802] - Worker 0 (pid: 22832) booted, phase: 0
[22802] - Worker 1 (pid: 22835) booted, phase: 0
[22802] - Worker 3 (pid: 22852) booted, phase: 0
[22802] - Worker 2 (pid: 22843) booted, phase: 0
Started GET "/" for 127.0.0.1 at 2015-05-13 17:58:20 +0800
Processing by ApplicationController#index as HTML
Rendered application/index.html.erb within layouts/application (3.6ms)
Completed 200 OK in 216ms (Views: 200.0ms | ActiveRecord: 0.0ms)
[22832] 127.0.0.1 - - [13/May/2015:17:58:20 +0800] "GET / HTTP/1.1" 200 - 0.8190
...
...
...
Completed 200 OK in 15003ms (Views: 0.1ms | ActiveRecord: 0.0ms)
[22852] 127.0.0.1 - - [13/May/2015:18:00:07 +0800] "GET /api_call.json?t=15?id=10 HTTP/1.1" 304 - 15.0103
**config.allow_concurrency = true (by default)**
-> Ideally each thread serves a request.
Puma starting in single mode...
* Version 2.11.2 (jruby 2.2.2), codename: Intrepid Squirrel
* Min threads: 1, max threads: 40
* Environment: development
NOTE: ActiveRecord 4.2 is not (yet) fully supported by AR-JDBC, please help us finish 4.2 support - check http://bit.ly/jruby-42 for starters
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop
Started GET "/" for 127.0.0.1 at 2015-05-13 18:23:04 +0800
Processing by ApplicationController#index as HTML
Rendered application/index.html.erb within layouts/application (35.0ms)
...
...
...
Completed 200 OK in 15020ms (Views: 0.7ms | ActiveRecord: 0.0ms)
127.0.0.1 - - [13/May/2015:18:25:19 +0800] "GET /api_call.json?t=15?id=9 HTTP/1.1" 304 - 15.0640
关于ruby-on-rails - MRI Ruby的并发请求,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/29955290/