我举了一个简单的例子,试图用一个基本的例子来证明Rails中的并发请求。注意,我使用的是MRI、Ruby2和Rails 4.2。
def api_call
sleep(10)
render :json => "done"
end然后,我在我的mac (I7 /4 Core)上查看Chrome中的4个不同的选项卡,看看它们是串联运行还是并行运行(确实是并行的,这是非常接近的,但不是一回事)。即,打电话
我不能用美洲豹,瘦或独角兽来完成这个任务。每一个请求都是依次提出的。第一个标签在10秒后,第二个选项卡在20秒后(因为它必须等待第一个选项卡完成),第三个选项卡在那之后.
根据我所读到的,我相信以下是正确的(请纠正我),并且是我的结果:
-- unicorn.rb
worker_processes 4
preload_app true
timeout 30
listen 3000
after_fork do |server, worker|
ActiveRecord::Base.establish_connection
end所以,
有一个非常类似于我的问题,但我无法使它的工作,因为我的回答,它没有回答我所有的问题,并发请求使用MRI。
Github项目:https://github.com/afrankel/limitedBandwidth (注意:项目正在研究的不仅仅是服务器上的多进程/线程问题)
发布于 2015-05-07 09:56:19
我邀请你阅读杰西斯托默尔的没有人理解吉尔系列,它可能会帮助你更好地理解一些核磁共振内部。
我还发现了与Ruby的语用并发,它读起来很有趣。它有一些并发测试的例子。
编辑:另外,我可以推荐文章移除config.threadsafe!可能与Rails 4无关,但它解释了配置选项,您可以使用其中一个选项来允许并发。
让我们讨论一下你问题的答案。
您可以有几个线程(使用MRI),即使是美洲豹。GIL确保一次只有一个线程是活动的,这是开发人员称之为限制的约束(因为没有真正的并行执行)。请记住,GIL并不保证线程安全。这并不意味着其他线程没有运行,而是在等待轮到它们。它们可以相互交织(文章有助于更好地理解)。
让我澄清一些术语:工作进程,线程。一个进程在一个单独的内存空间中运行,可以为多个线程服务。同一进程的线程在共享内存空间中运行,这是它们的进程的线程。对于线程,我们指的是这个上下文中的Ruby线程,而不是CPU线程。
关于您的问题的配置和您共享的GitHub回购,我认为一个合适的配置(我使用了Puma)是设置4个工作人员和1到40个线程。这个想法是,一个工人提供一个标签。每个选项卡最多发送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的彪马配置文件(放在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个worker和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秒,但我一点也没想到。我不知道结果究竟是什么,但我仍然非常惊讶(考虑到我在虚拟机上运行,核磁共振受GIL和其他一些因素的限制)。这个测试的运行时间少于最大运行时间的一半(在严格的串行模式下),我们将结果减少到不足一半。
编辑--我进一步阅读了关于Rack::Lock的文章,它在每个请求周围包装了一个互斥体(上面的第三篇文章)。我发现
config.allow_concurrency = true选项可以节省时间。一个小小的警告是增加连接池(尽管请求没有查询,数据库必须相应地设置);最大线程数是一个很好的缺省值。40在这个案子里。 我用jRuby测试了这个应用程序,实际使用allow_concurrency=true的时间是2分钟。 我用核磁共振对应用程序进行了测试,用allow_concurrency=true测试了实际运行时间为1分钟47秒。这对我来说是个大惊喜。这真的让我感到惊讶,因为我预计核磁共振要比JRuby慢。事实并非如此。这让我质疑关于磁共振成像和JRuby速度差异的广泛讨论。 现在看不同标签上的回复是“更随意的”了。发生在选项卡1之前完成选项卡3或4,这是我首先请求的。 我想因为你没有比赛条件,所以考试似乎没问题。但是,如果在实际应用程序中设置config.allow_concurrency=true,我不确定应用程序范围的后果。
请随时查看,并让我知道您的读者可能有任何反馈。我的机器上还有克隆人。如果你有兴趣,请告诉我。
按顺序回答你的问题:
附录
日志文件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发布于 2015-12-26 10:45:24
对于“Elyasin”和“Arthur Frankel”,我创建了这个存储库,用于测试在MRI和JRuby中运行的美洲豹。在这个小项目中,我没有做sleep来模拟一个长时间运行的请求。正如我在MRI中发现的那样,GIL似乎与常规处理不同,更类似于外部I/O请求。
我把斐波纳契序列计算放在控制器里。在我的机器上,fib(39)在JRuby中花费了6.x秒,在MRI上花费了11秒,这足以显示出两者之间的差异。
我打开了两个浏览器窗口。我这样做不是在同一个浏览器中打开选项卡,而是为了防止浏览器发送到同一个域的并发请求受到某些限制。我现在确定了细节,但是两种不同的浏览器足以防止这种情况发生。
我测试了薄层+核磁共振,然后是美洲狮+核磁共振,然后是美洲狮+ JRuby。研究结果如下:
关于我的设置:
config.cache_classes被设置为true,这意味着config.allow_concurrency = truehttps://stackoverflow.com/questions/29955290
复制相似问题