2012-03-30 190 views
22

我們剛剛從乘客轉移到獨角獸主持幾個rails應用程序。 一切都很好,但我們通過New Relic注意到請求在100到300毫秒之間排隊。獨角獸請求排隊

以下是圖表:

enter image description here

我不知道在哪裏,這是從這裏來的是我們獨​​角獸的conf:

current_path = '/data/actor/current' 
shared_path = '/data/actor/shared' 
shared_bundler_gems_path = "/data/actor/shared/bundled_gems" 
working_directory '/data/actor/current/' 

worker_processes 6 

listen '/var/run/engineyard/unicorn_actor.sock', :backlog => 1024 

timeout 60 

pid "/var/run/engineyard/unicorn_actor.pid" 

logger Logger.new("log/unicorn.log") 

stderr_path "log/unicorn.stderr.log" 
stdout_path "log/unicorn.stdout.log" 

preload_app true 

if GC.respond_to?(:copy_on_write_friendly=) 
    GC.copy_on_write_friendly = true 
end 

before_fork do |server, worker| 
    if defined?(ActiveRecord::Base) 
    ActiveRecord::Base.connection.disconnect! 
    end 

    old_pid = "#{server.config[:pid]}.oldbin" 

    if File.exists?(old_pid) && server.pid != old_pid 
    begin 
     sig = (worker.nr + 1) >= server.worker_processes ? :TERM : :TTOU 
     Process.kill(sig, File.read(old_pid).to_i) 
    rescue Errno::ENOENT, Errno::ESRCH 
     # someone else did our job for us 
    end 
    end 
    sleep 1 
end 

if defined?(Bundler.settings) 
    before_exec do |server| 
    paths = (ENV["PATH"] || "").split(File::PATH_SEPARATOR) 
    paths.unshift "#{shared_bundler_gems_path}/bin" 
    ENV["PATH"] = paths.uniq.join(File::PATH_SEPARATOR) 

    ENV['GEM_HOME'] = ENV['GEM_PATH'] = shared_bundler_gems_path 
    ENV['BUNDLE_GEMFILE'] = "#{current_path}/Gemfile" 
    end 
end 

after_fork do |server, worker| 
    worker_pid = File.join(File.dirname(server.config[:pid]), "unicorn_worker_actor_#{worker.nr$ 
    File.open(worker_pid, "w") { |f| f.puts Process.pid } 
    if defined?(ActiveRecord::Base) 
    ActiveRecord::Base.establish_connection 
    end 

end 

我們的nginx.conf:

user deploy deploy; 
worker_processes 6; 

worker_rlimit_nofile 10240; 
pid /var/run/nginx.pid; 

events { 
    worker_connections 8192; 
    use epoll; 
} 

http { 

    include /etc/nginx/mime.types; 

    default_type application/octet-stream; 

    log_format main '$remote_addr - $remote_user [$time_local] ' 
        '"$request" $status $body_bytes_sent "$http_referer" ' 
        '"$http_user_agent" "$http_x_forwarded_for"'; 

    sendfile on; 

    tcp_nopush  on; 

    server_names_hash_bucket_size 128; 

    if_modified_since before; 
    gzip    on; 
    gzip_http_version 1.0; 
    gzip_comp_level 2; 
    gzip_proxied  any; 
    gzip_buffers  16 8k; 
    gzip_types  application/json text/plain text/html text/css application/x-javascript t$ 
    # gzip_disable  "MSIE [1-6]\.(?!.*SV1)"; 

    # Allow custom settings to be added to the http block 
    include /etc/nginx/http-custom.conf; 
    include /etc/nginx/stack.conf; 
    include /etc/nginx/servers/*.conf; 
} 

和我們的應用特定的nginx conf:

upstream upstream_actor_ssl { 
    server unix:/var/run/engineyard/unicorn_actor.sock fail_timeout=0; 
} 

server { 
    listen 443; 

    server_name letitcast.com; 

    ssl on; 
    ssl_certificate /etc/nginx/ssl/letitcast.crt; 
    ssl_certificate_key /etc/nginx/ssl/letitcast.key; 
    ssl_session_cache shared:SSL:10m; 

    client_max_body_size 100M; 

    root /data/actor/current/public; 

    access_log /var/log/engineyard/nginx/actor.access.log main; 
    error_log /var/log/engineyard/nginx/actor.error.log notice; 

    location @app_actor { 
    include /etc/nginx/common/proxy.conf; 
    proxy_pass http://upstream_actor_ssl; 
    } 

    include /etc/nginx/servers/actor/custom.conf; 
    include /etc/nginx/servers/actor/custom.ssl.conf; 

    if ($request_filename ~* \.(css|jpg|gif|png)$) { 
    break; 
    } 

    location ~ ^/(images|javascripts|stylesheets)/ { 
    expires 10y; 
    } 

    error_page 404 /404.html; 
    error_page 500 502 504 /500.html; 
    error_page 503 /system/maintenance.html; 

    location = /system/maintenance.html { } 

    location/{ 
    if (-f $document_root/system/maintenance.html) { return 503; } 

    try_files $uri $uri/index.html $uri.html @app_actor; 
    } 

    include /etc/nginx/servers/actor/custom.locations.conf; 
} 

我們不是在重負載下,所以我不明白爲什麼請求被阻塞在隊列中。 按照獨角獸的規定,我們有6名獨角獸工人。

任何想法可能來自哪裏?

乾杯

編輯:每分鐘

平均請求:約15大部分的時間,300多名在偷看,但我們沒有經歷過一次,因爲遷移。
CPU負載平均值:0.2-0.3

我試着8名工人,它沒有改變任何東西。

我也使用raindrops來看看獨角獸工作者所做的。

這裏的Ruby腳本:

#!/usr/bin/ruby 

# this is used to show or watch the number of active and queued 
# connections on any listener socket from the command line 

require 'raindrops' 
require 'optparse' 
require 'ipaddr' 
usage = "Usage: #$0 [-d delay] ADDR..." 
ARGV.size > 0 or abort usage 
delay = false 

# "normal" exits when driven on the command-line 
trap(:INT) { exit 130 } 
trap(:PIPE) { exit 0 } 

opts = OptionParser.new('', 24, ' ') do |opts| 
    opts.banner = usage 
    opts.on('-d', '--delay=delay') { |nr| delay = nr.to_i } 
    opts.parse! ARGV 
end 

socks = [] 
ARGV.each do |f| 
    if !File.exists?(f) 
    puts "#{f} not found" 
    next 
    end 

    if !File.socket?(f) 
    puts "#{f} ain't a socket" 
    next 
    end 

    socks << f 
end 

fmt = "% -50s % 10u % 10u\n" 
printf fmt.tr('u','s'), *%w(address active queued) 

begin 
    stats = Raindrops::Linux.unix_listener_stats(socks) 
    stats.each do |addr,stats| 
    if stats.queued.to_i > 0 
     printf fmt, addr, stats.active, stats.queued 
    end 
    end 
end while delay && sleep(delay) 

我是如何推出它:

./linux-tcp-listener-stats.rb -d 0.1 /var/run/engineyard/unicorn_actor.sock 

所以它基本上是檢查每1/10秒,如果有在隊列中的請求,如果有它輸出:

插座 | 正在處理的請求數 | 隊列

這裏的請求數量是結果的要點:

https://gist.github.com/f9c9e5209fbbfc611cb1

EDIT2:

我試圖nginx的工人數量減少到最後一晚但它沒有改變任何東西。

有關我們託管在Engine Yard上的信息,並且具有高CPU中等實例1.7 GB的內存,5個EC2計算單元(2個虛擬核心,5個EC2計算單元)

我們承載4個rails應用程序,這個應用程序有6個工作人員,我們有一個4個,一個2個,另一個1個。自從我們遷移到獨角獸以來,他們都遇到了請求排隊。 我不知道乘客是否在作弊,但New Relic在我們使用它時沒有記錄任何請求排隊。我們還有一個node.js應用程序處理文件上傳,一個mysql數據庫和2個redis。

編輯3:

我們使用紅寶石1.9.2p290,nginx的1.0.10,麒麟4.2.1和3.3.3 newrelic_rpm。 我會嘗試沒有newrelic明天,並會讓你知道這裏的結果,但對於我們使用乘客與新文物,相同版本的紅寶石和nginx的信息,並沒有任何問題。

編輯4:

我試圖增加client_body_buffer_sizeproxy_buffers

client_body_buffer_size 256k;
proxy_buffers 8 256k;

但它並沒有這樣的伎倆。

編輯5:

我們終於找到它了... ...擊鼓聲的 冠軍是我們的SSL暗號。當我們將其更改爲RC4時,我們看到請求排隊從100-300ms降至30-100ms。

+1

是您的實際響應時間增加了,當你使用汽車,或者是它只是什麼負責響應時間New Relic的擊穿該令人震驚? – 2012-03-31 17:27:58

+0

當我們使用乘客時,我們在大約120ms內提供了請求,所以是平均請求時間令人震驚。我的主要問題是,爲什麼當我們每分鐘沒有請求6名獨角獸工作人員的請求時,這些請求陷入隊列中? – Mike 2012-03-31 23:27:16

+0

負載是多少?多少RPM?你有沒有做數學(你的6名工人能否跟上負荷)?你有沒有嘗試產卵更多的獨角獸工作者? – 2012-04-01 22:27:18

回答

12

我剛剛診斷出一個類似的外觀新的遺蹟圖,完全是SSL的錯誤。嘗試關閉它。我們看到400毫秒的請求排隊時間,在沒有SSL的情況下會降至20毫秒。

爲什麼一些SSL提供商可能會慢一些有趣的觀點:http://blog.cloudflare.com/how-cloudflare-is-making-ssl-fast

+0

我剛剛添加了一個編輯,我們在幾個月前就知道了。 我們沒有把它關掉,但改變密碼就是了。 無論如何,我接受你的答案。 – Mike 2013-03-21 17:59:36

+0

您在終止SSL/TLS之前是否終止了上游?第二個問題:您是否試圖在反向代理端緩存SSL/TLS連接? – Anatoly 2014-05-23 14:44:32

+0

@mikhailov我想也沒有。這是一個在Engineyard上託管的Rails應用程序。 – 2014-05-23 16:11:11

1

你使用的是什麼版本的ruby,獨角獸,nginx(應該不重要但值得一提)和newrelic_rpm?

此外,我會嘗試運行無新增功能的基線性能測試。 NewRelic解析響應,並且有些情況下,由於ruby 1.9.3之前的'rindex'問題,這可能會很慢。這通常只在您的回覆非常大且不包含「body」標籤(例如AJAX,JSON等)時纔會顯着。我看到一個例子,其中1MB AJAX響應需要30秒才能解析NewRelic。

+0

感謝您的回覆,我在EDIT3 – Mike 2012-04-07 12:28:24

+0

中回覆鑑於您沒有看到Passenger的問題,因此我所指的NewRelic問題不太可能發生。 – jmervine 2012-04-07 16:41:38

+0

我正在調查一個類似的問題,我懷疑NewRelic補丁爲nginx提供了來自客戶端的請求的下載時間,這可以解釋高「隊列」時間。目前我還沒有能夠證明這一點,儘管... – valo 2012-11-15 19:20:35

0

您確定您正在緩衝來自nginx中客戶端的請求,然後在將它們發送回客戶端之前緩衝來自獨角獸的響應。從你的設置看,你似乎(因爲這是默認情況下),但我會建議你仔細檢查。

在配置看爲:

http://wiki.nginx.org/HttpProxyModule#proxy_buffering

這是用於從獨角獸響應的緩衝。您絕對需要它,因爲您不想讓獨角獸忙於將數據發送到慢速客戶端。

對於來自客戶端的請求的緩衝,我認爲你需要看看:

http://wiki.nginx.org/HttpCoreModule#client_body_buffer_size

我覺得這一切都無法解釋的100毫秒的延遲,但我不熟悉所有的系統設置,所以值得看看這個方向。看起來你的隊列不是由CPU爭用引起的,而是由某種IO阻塞造成的。

+0

我嘗試了'client_body_buffer_size 256k;'和'proxy_buffers 8 256k;'但沒有改進。 – Mike 2012-12-17 12:44:28