очередь запросов единорога

Мы только что перешли с пассажира на единорога, чтобы разместить несколько приложений для рельсов. Все работает отлично, но через New Relic мы заметили, что запросы стоят в очереди от 100 до 300 мс.

Вот график:

введите здесь описание изображения

Я понятия не имею, откуда это взялось, вот наша конфа единорога:

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 при просмотре, но после миграции мы не получили ни одного запроса.
Средняя загрузка ЦП: 0,2–0,3

Пробовал с 8 рабочими, ничего не изменилось.

Я также использовал капли дождя, чтобы посмотреть, чем заняты рабочие-единороги.

Вот рубиновый скрипт:

#!/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 и имеем экземпляр Medium с высокой производительностью ЦП, 1,7 ГБ памяти, 5 вычислительных блоков EC2 (2 виртуальных ядра с 2,5 вычислительными блоками EC2 в каждом)

Мы размещаем 4 приложения rails, у этого есть 6 рабочих, у нас есть один с 4, один с 2 и еще один с одним. Все они столкнулись с очередями запросов с тех пор, как мы перешли на unicorn. Я не знаю, обманывал ли Passenger, но New Relic не регистрировал очереди запросов, когда мы его использовали. У нас также есть приложение node.js, обрабатывающее загрузку файлов, база данных mysql и 2 redis.

ИЗМЕНИТЬ 3:

Мы используем ruby ​​1.9.2p290, nginx 1.0.10, unicorn 4.2.1 и newrelic_rpm 3.3.3. Завтра я попробую без newrelic и сообщу вам о результатах здесь, но для информации мы использовали пассажира с новой реликвией, ту же версию ruby ​​и nginx, и у нас не было никаких проблем.

ИЗМЕНИТЬ 4:

Я попытался увеличить client_body_buffer_size и proxy_buffers с помощью

client_body_buffer_size 256k;
proxy_buffers 8 256k;

Но это не помогло.

ИЗМЕНИТЬ 5:

Наконец-то мы разобрались… барабанная дробь… Победителем стал наш SSL-шифр. Когда мы изменили его на RC4, мы увидели, что очередь запросов сократилась со 100-300 мс до 30-100 мс.


person Mike    schedule 30.03.2012    source источник
comment
Увеличилось ли ваше фактическое время отклика по сравнению с тем, когда вы использовали Passenger, или вас беспокоит только разбивка New Relic того, что отвечает за время отклика?   -  person Andrew Gorcester    schedule 31.03.2012
comment
Когда мы использовали пассажира, мы обслуживали запросы примерно за 120 мс, так что да, настораживает среднее время запроса. Мой главный вопрос: почему запросы застревают в очереди, когда у нас мало запросов в минуту с 6 работниками-единорогами?   -  person Mike    schedule 01.04.2012
comment
Какая нагрузка? Сколько об/мин? Вы сделали математику (могут ли ваши 6 рабочих справиться с нагрузкой)? Вы пытались создать больше рабочих-единорогов?   -  person Sergio Tulentsev    schedule 02.04.2012
comment
Мне любопытно, является ли это задержкой, присущей Unicorn; сколько времени потребуется, чтобы написать простейшее приложение Hello World, которое почти ничего не делает и может быть использовано для измерения задержки Unicorn в самом простом виде?   -  person sarnold    schedule 02.04.2012
comment
@SergioTulentsev Я обновил свой вопрос   -  person Mike    schedule 02.04.2012
comment
@sarnold Я постараюсь завтра найти время, чтобы попробовать это   -  person Mike    schedule 02.04.2012
comment
@Mike: Кстати, вам не нужен рабочий nginx для каждого рабочего единорога. IIRC, рекомендуется иметь по одному рабочему nginx на ядро ​​(и только если вы находитесь под нагрузкой). В вашей настройке одного работника nginx более чем достаточно. Интересно, могло ли это как-то повлиять на единорогов?   -  person Sergio Tulentsev    schedule 02.04.2012
comment
@sarnold: Единорог определенно не медленный. В моем приложении 15 рабочих обрабатывают 10-30 тыс. об/мин с временем отклика менее миллисекунды.   -  person Sergio Tulentsev    schedule 02.04.2012
comment
@Серхио: это обнадеживает. Если у Майка значительно медленнее, может быть проще понять, почему в меньшем приложении. Это размещается в виртуализированной среде? Есть ли на машине другие задания, работающие с более высоким приоритетом или приоритетом в реальном времени?   -  person sarnold    schedule 02.04.2012
comment
@sarnold: я полагаю, его приложение работает на виртуальных машинах EngineYard. Мой - на выделенном сервере (не слишком мощном).   -  person Sergio Tulentsev    schedule 02.04.2012
comment
Только что отредактировал вопрос, ребята, добавив дополнительную информацию о спецификациях виртуальной машины и о том, что на ней размещено.   -  person Mike    schedule 02.04.2012
comment
@Mike Обратите внимание на предостережения здесь и здесь о медленных клиентах: If your application responses are larger than the socket buffer or if you’re handling large requests (uploads), worker processes will also be bottlenecked by the speed of the client connection. You should not allow unicorn to serve clients outside of your local network. Насколько я понимаю, это значительно повлияет на ваши тесты производительности. Если это полезная информация, я перепишу это как ответ.   -  person MrGomez    schedule 03.04.2012
comment
@MrGomez, вот почему мы используем nginx, мы не обрабатываем большие загрузки через unicorn (мы используем узел).   -  person Mike    schedule 03.04.2012
comment
@Майк, ты когда-нибудь понимал, что происходит? Я вижу похожие времена в очереди запросов с использованием единорога...   -  person Aaron Gibralter    schedule 05.11.2012
comment
@mike любопытно - теперь, когда RC4 устаревает в пользу AES, что вы планируете делать? Кроме того, на приведенном выше графике наш экземпляр newrelic не показывает очередь — это монитор сервера или вам нужно было сделать что-то конкретное в мониторе приложения, чтобы получить данные очереди?   -  person MBHNYC    schedule 20.01.2016


Ответы (3)


Я только что диагностировал похожий граф New Relic, полностью виноватый в SSL. Попробуйте отключить его. Мы видим время ожидания запросов в очереди 400 мс, которое падает до 20 мс без SSL.

Несколько интересных моментов о том, почему некоторые провайдеры SSL могут работать медленно: http://blog.cloudflare.com/how-cloudflare-is-making-ssl-fast

person Matt Gibson    schedule 21.03.2013
comment
Я только что добавил правку, мы поняли это несколько месяцев назад. Мы не отключили его, но смена шифра помогла. Я принимаю ваш ответ в любом случае. - person Mike; 21.03.2013
comment
Вы завершаете SSL/TLS до того, как он попадет в восходящий поток? Второй вопрос: пытались ли вы кэшировать соединение SSL/TLS на стороне обратного прокси? - person Anatoly; 23.05.2014
comment
@mikhailov Думаю, тоже. Это было приложение Rails, размещенное на Engineyard. - person Matt Gibson; 23.05.2014
comment
@MattGibson, разве у тебя нет root-доступа к коробке? - person Anatoly; 23.05.2014
comment
@mikhailov Да, видел, но проект был законсервирован и больше не работает. - person Matt Gibson; 24.05.2014

Какую версию ruby, unicorn, nginx (не имеет большого значения, но стоит упомянуть) и newrelic_rpm вы используете?

Кроме того, я бы попробовал запустить базовый тест производительности без newrelic. NewRelic анализирует ответ, и в некоторых случаях это может быть медленным из-за проблемы с 'rindex' в ruby ​​до версии 1.9.3. Обычно это заметно только тогда, когда ваш ответ очень большой и не содержит тегов «тело» (например, AJAX, JSON и т. д.). Я видел пример этого, когда ответ AJAX размером 1 МБ требовалось 30 секунд для анализа NewRelic.

person jmervine    schedule 05.04.2012
comment
Спасибо за ваш ответ, я ответил в EDIT3 - person Mike; 07.04.2012
comment
Учитывая, что вы не видели проблемы с Passenger, маловероятно, что проблема NewRelic, о которой я говорил. - person jmervine; 07.04.2012
comment
Я исследую аналогичную проблему, и у меня есть подозрение, что патч NewRelic для nginx включает время загрузки запроса от клиента, что может объяснить большое время ожидания. Однако пока я не могу этого доказать... - person valo; 15.11.2012

Вы уверены, что буферизуете запросы от клиентов в nginx, а затем буферизуете ответы от единорогов, прежде чем отправлять их обратно клиентам. Судя по вашей настройке, вы это делаете (потому что это по умолчанию), но я предлагаю вам дважды проверить это.

Конфиг для просмотра:

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

Это для буферизации ответа от единорогов. Вам это определенно нужно, потому что вы не хотите, чтобы единороги были заняты отправкой данных медленному клиенту.

Для буферизации запроса от клиента я думаю, что вам нужно посмотреть:

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

Я думаю, что все это не может объяснить задержку в 100 мс, но я не знаком со всеми настройками вашей системы, поэтому стоит взглянуть в этом направлении. Кажется, что ваша очередь вызвана не конфликтом ЦП, а какой-то блокировкой ввода-вывода.

person valo    schedule 14.11.2012
comment
Я пробовал с client_body_buffer_size 256k; и proxy_buffers 8 256k;, но это не улучшилось. - person Mike; 17.12.2012