2012-03-30 12 views
22

Abbiamo appena migrato da passeggero a unicorno per ospitare alcune app di rotaie. Tutto funziona alla grande ma notiamo tramite New Relic che le richieste sono in coda tra 100 e 300ms.unicorno richiesta di accodamento

Ecco il grafico:

enter image description here

Non ho idea di dove questo viene da ecco la nostra conf unicorno:

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 

nostro 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; 
} 

e la nostra app specifica 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; 
} 

Non siamo sotto carico pesante, quindi non capisco perché le richieste siano bloccate in coda. Come specificato nel conf unicorno, abbiamo 6 lavoratori unicorno.

Qualche idea da dove potrebbe provenire?

Acclamazioni

EDIT:

richieste medio al minuto: circa il 15 per la maggior parte del tempo, più di 300 in fa capolino, ma non abbiamo vissuto uno dalla migrazione.
CPU Media carico: 0,2-0,3

Ho provato con 8 lavoratori, non ha cambiato nulla.

Ho anche usato raindrops per vedere cosa stavano facendo gli unicorni.

Ecco lo script 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) 

Come ho lanciato:

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

Così è fondamentalmente controlla ogni 1/10s se ci sono richieste nella coda e se ci sono si uscite:

la presa | numero di richieste in elaborazione | il numero di richieste nella coda

Ecco un succo del risultato:

https://gist.github.com/f9c9e5209fbbfc611cb1

EDIT2:

ho cercato di ridurre il numero di lavoratori nginx per una notte scorsa ma non ha cambiato nulla.

Per informazioni, siamo ospitati su Engine Yard e dispongono di un'istanza media con CPU alta 1,7 GB di memoria, 5 unità di calcolo EC2 (2 core virtuali con 2.5 unità di calcolo EC2 ciascuna)

Ospitiamo 4 applicazioni di rotaie, questa ha 6 lavoratori, ne abbiamo una con 4, una con 2 e un'altra con uno. Stanno tutti sperimentando l'accodamento delle richieste da quando siamo migrati verso l'unicorno. Non so se il Passeggero stava barando, ma New Relic non ha registrato alcuna richiesta di accodamento quando lo stavamo usando. Abbiamo anche un'app node.js che gestisce i caricamenti di file, un database mysql e 2 redis.

EDIT 3:

Stiamo usando rubino 1.9.2p290, nginx 1.0.10, 4.2.1 unicorno e newrelic_rpm 3.3.3. Domani proverò senza novità e ti farò sapere i risultati qui, ma per le informazioni che stavamo usando passeggero con una nuova reliquia, la stessa versione di rubino e nginx e non ha avuto alcun problema.

EDIT 4:

ho cercato di aumentare la client_body_buffer_size e proxy_buffers con

client_body_buffer_size 256k;
proxy_buffers 8 256k;

Ma non ha fatto il trucco.

EDIT 5:

Abbiamo finalmente capito tutto ... rullo di tamburi ... Il vincitore è stata la nostra cifra SSL. Quando lo abbiamo cambiato in RC4 abbiamo visto la richiesta di accodamento in coda da 100-300 ms a 30-100 ms.

+1

è il tuo tempo di risposta effettivo aumentato nel corso quando si sta utilizzando per passeggeri, o è solo la ripartizione di New Relic di ciò che è responsabile per i tempi di risposta che è allarmante? –

+0

Quando stavamo usando il passeggero, stavamo servendo le richieste in circa 120 ms, quindi sì, è il tempo medio di richiesta allarmante. La mia domanda principale è: perché le richieste sono bloccate in coda quando abbiamo poche richieste al minuto con 6 lavoratori unicorni? – Mike

+0

Qual è il carico? Quanti RPM? Hai fatto i conti (i tuoi 6 lavoratori possono tenere il passo con il carico)? Hai provato a generare altri lavoratori unicorni? –

risposta

12

Ho appena diagnosticato un grafico di nuova reliquia simile a quello che è interamente colpa di SSL. Prova a spegnerlo. Stiamo vedendo 400ms richiedere il tempo di accodamento, che scende a 20ms senza SSL.

Alcuni punti interessanti sul perché alcuni fornitori di SSL potrebbe essere lenta: http://blog.cloudflare.com/how-cloudflare-is-making-ssl-fast

+0

Ho appena aggiunto una modifica, l'abbiamo capito alcuni mesi fa. Non l'abbiamo spento, ma cambiare la cifra ha fatto il trucco. Accetto comunque la tua risposta. – Mike

+0

Terminate SSL/TLS prima che raggiunga l'upstream? Seconda domanda: hai provato a mettere in cache la connessione SSL/TLS sul lato reverse-proxy? – Anatoly

+0

@mikhailov Neanche io, penso. Questa era un'app Rails ospitata su Engineyard. –

1

Quale versione di rubino, unicorno, nginx (non dovrebbe importare molto ma vale la pena menzionarla) e newrelic_rpm stai usando?

Inoltre, proverei a eseguire un perfection test di base senza newrelic. NewRelic analizza la risposta e ci sono casi in cui questo può essere lento a causa del problema con 'rindex' in ruby ​​pre-1.9.3. Questo è solitamente visibile solo quando la risposta è molto ampia e non contiene tag "body" (ad esempio AJAX, JSON, ecc.). Ho visto un esempio di ciò in cui una risposta AJAX da 1MB impiegava 30 secondi per l'analisi di NewRelic.

+0

Grazie per la risposta, ho risposto in EDIT3 – Mike

+0

Dato che non si vedeva il problema con Passenger, è improbabile che il problema NewRelic a cui mi riferivo. – jmervine

+0

Sto analizzando un problema simile e ho il sospetto che la patch NewRelic per nginx includa il tempo di download della richiesta dal client, che potrebbe spiegare tempi di "coda" elevati. Attualmente non sono stato in grado di provarlo, anche se ... – valo

0

Sei sicuro di voler eseguire il buffering delle richieste dai client in nginx e quindi di eseguire il buffering delle risposte dagli unicorni prima di inviarli ai client. Dal tuo setup sembra che tu faccia (perché questo è di default), ma ti suggerirò di ricontrollarlo.

La configurazione da guardare è:

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

Questo è per il buffer della risposta da parte dei unicorni. Ne hai sicuramente bisogno perché non vuoi tenere gli unicorni impegnati nell'invio di dati a un client lento.

Per il buffer della richiesta da parte del cliente Penso che avete bisogno di guardare:

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

Penso che tutto questo non può spiegare un ritardo di 100 ms, ma non sono a conoscenza tutte le impostazioni del sistema, quindi vale la pena dare un'occhiata a questa direzione. Sembra che la tua coda non sia causata da una contesa della CPU, ma da qualche tipo di blocco dell'IO.

+0

Ho provato con 'client_body_buffer_size 256k;' e 'proxy_buffers 8 256k;' ma non è migliorato. – Mike