Ruby并发:非阻塞I/O vs 线程

13

我正在尝试在 Ruby(1.9.3-p0)中使用并发技术,并创建了一个非常简单的、I/O密集型的代理任务。首先,我尝试了非阻塞的方法:

require 'rack'
require 'rack/fiber_pool'
require 'em-http'
require 'em-synchrony'
require 'em-synchrony/em-http'

proxy = lambda {|*|
  result = EM::Synchrony.sync EventMachine::HttpRequest.new('http://google.com').get
  [200, {}, [result.response]]
}

use Rack::FiberPool, :size => 1000
run proxy

=begin
$ thin -p 3000 -e production -R rack-synchrony.ru start
>> Thin web server (v1.3.1 codename Triple Espresso)

$ ab -c100 -n100 http://localhost:3000/
Concurrency Level:      100
Time taken for tests:   5.602 seconds
HTML transferred:       21900 bytes
Requests per second:    17.85 [#/sec] (mean)
Time per request:       5602.174 [ms] (mean)
=end

哦,我以为一定是我的操作有问题。在这个任务中,我们大部分时间都在等待I/O,平均请求时间竟然是5.6秒?我尝试了另一个:

require 'sinatra'
require 'sinatra/synchrony'
require 'em-synchrony/em-http'

get '/' do
  EM::HttpRequest.new("http://google.com").get.response
end

=begin
$ ruby sinatra-synchrony.rb -p 3000 -e production
== Sinatra/1.3.1 has taken the stage on 3000 for production with backup from Thin
>> Thin web server (v1.3.1 codename Triple Espresso)

$ ab -c100 -n100 http://localhost:3000/
Concurrency Level:      100
Time taken for tests:   5.476 seconds
HTML transferred:       21900 bytes
Requests per second:    18.26 [#/sec] (mean)
Time per request:       5475.756 [ms] (mean)
=end

嗯,稍微好一点了,但并不是我所称之为成功的。最后,我尝试了一个线程实现:

require 'rack'
require 'excon'

proxy = lambda {|*|
  result = Excon.get('http://google.com')
  [200, {}, [result.body]]
}    
run proxy

=begin
$ thin -p 3000 -e production -R rack-threaded.ru --threaded --no-epoll start
>> Thin web server (v1.3.1 codename Triple Espresso)

$ ab -c100 -n100 http://localhost:3000/
Concurrency Level:      100
Time taken for tests:   2.014 seconds
HTML transferred:       21900 bytes
Requests per second:    49.65 [#/sec] (mean)
Time per request:       2014.005 [ms] (mean)
=end

这真的非常令人惊讶。难道我漏掉了什么吗?为何EM在这里表现如此糟糕?是否需要进行一些调整?我尝试了各种组合(Unicorn、几个Rainbows配置等等),但它们中没有一个能够接近简单、古老的I/O阻塞线程。

欢迎提出想法、评论和显然更好的实现建议。


1
你不应该使用远程服务器进行测试,延迟可能会有所不同。你应该尝试使用更少的纤维再次进行异步测试,使用20个纤维时,每个请求需要300毫秒,而使用1000个纤维时,每个请求需要1秒,这是使用你的确切ab命令行得出的结果。你的线程服务器正在使用默认的eventmachine线程池,该线程池默认为20个线程。 - Schmurfy
不确定,将纤程池大小设置为20实际上会降低我的计算机性能。 - BSM
也许不是20,但1000确实很高。我使用本地服务器进行了测试,因此响应时间非常短。 - Schmurfy
1个回答

3
看到你的“每个请求的时间”恰好等于“测试所用时间”的总和了吗?这是一种报告算法产生的现象,因为你的请求数(-n)等于你的并发级别(-c)。平均时间是总时间*并发数/请求数。所以当-n == -c时报告的平均时间将是最长请求的时间。你应该使用-n > -c进行多次ab运行,以获得合理的度量结果。
你似乎正在使用旧版本的ab,因为相对较新的版本默认情况下会报告更详细的结果。直接针对谷歌运行时,我发现当-n == -c时,总时间与平均时间相似,并且当-n > -c时,得到的数字更加合理。你真正想要查看每秒请求数,所有并发请求的平均值以及最终服务级别的分解,以获得更好的理解。
$ ab -c50 -n50 http://google.com/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking google.com (be patient).....done


Server Software:        gws
Server Hostname:        google.com
Server Port:            80

Document Path:          /
Document Length:        219 bytes

Concurrency Level:      50
Time taken for tests:   0.023 seconds           <<== note same as below
Complete requests:      50
Failed requests:        0
Write errors:           0
Non-2xx responses:      50
Total transferred:      27000 bytes
HTML transferred:       10950 bytes
Requests per second:    2220.05 [#/sec] (mean)
Time per request:       22.522 [ms] (mean)      <<== note same as above
Time per request:       0.450 [ms] (mean, across all concurrent requests)
Transfer rate:          1170.73 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        1    2   0.6      3       3
Processing:     8    9   2.1      9      19
Waiting:        8    9   2.1      9      19
Total:         11   12   2.1     11      22
WARNING: The median and mean for the initial connection time are not within a normal deviation
        These results are probably not that reliable.

Percentage of the requests served within a certain time (ms)
  50%     11
  66%     12
  75%     12
  80%     12
  90%     12
  95%     12
  98%     22
  99%     22
 100%     22 (longest request)        <<== note same as total and mean above


$ ab -c50 -n500 http://google.com/
This is ApacheBench, Version 2.3 <$Revision: 655654 $>
Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
Licensed to The Apache Software Foundation, http://www.apache.org/

Benchmarking google.com (be patient)
Completed 100 requests
Completed 200 requests
Completed 300 requests
Completed 400 requests
Completed 500 requests
Finished 500 requests


Server Software:        gws
Server Hostname:        google.com
Server Port:            80

Document Path:          /
Document Length:        219 bytes

Concurrency Level:      50
Time taken for tests:   0.110 seconds
Complete requests:      500
Failed requests:        0
Write errors:           0
Non-2xx responses:      500
Total transferred:      270000 bytes
HTML transferred:       109500 bytes
Requests per second:    4554.31 [#/sec] (mean)
Time per request:       10.979 [ms] (mean)
Time per request:       0.220 [ms] (mean, across all concurrent requests)
Transfer rate:          2401.69 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        1    1   0.7      1       3
Processing:     8    9   0.7      9      13
Waiting:        8    9   0.7      9      13
Total:          9   10   1.3     10      16

Percentage of the requests served within a certain time (ms)
  50%     10
  66%     11
  75%     11
  80%     12
  90%     12
  95%     13
  98%     14
  99%     15
 100%     16 (longest request)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接