独角兽请求排队

Posted

技术标签:

【中文标题】独角兽请求排队【英文标题】:unicorn request queuing 【发布时间】:2012-04-14 14:53:11 【问题描述】:

我们刚刚从乘客迁移到独角兽,以托管几个 Rails 应用程序。 一切正常,但我们通过 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;

我们的负载并不重,所以我不明白为什么请求会卡在队列中。 按照 unicorn conf 中的规定,我们有 6 个 unicorn 工人。

知道这可能来自哪里吗?

干杯

编辑:

每分钟的平均请求数:大部分时间约为 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 个虚拟内核,每个内核有 2.5 个 EC2 计算单元)

我们托管 4 个 rails 应用程序,这个有 6 个 worker,一个有 4 个,一个有 2 个,另一个有一个。自从我们迁移到独角兽后,他们都在经历请求排队。 我不知道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,并会在这里告诉您结果,但对于我们使用带有 new relic 的乘客的信息,相同版本的 ruby​​ 和 nginx 并且没有任何问题。

编辑 4:

我试图增加client_body_buffer_sizeproxy_buffers

client_body_buffer_size 256k; proxy_buffers 8 256k; 但它没有起到作用。

编辑 5:

我们终于想通了...鼓声... 获胜者是我们的 SSL 密码。当我们将其更改为 RC4 时,我们看到请求队列从 100-300 毫秒下降到 30-100 毫秒。

【问题讨论】:

您的实际响应时间是否比您使用Passenger 时增加了,还是仅仅是New Relic 对响应时间的原因进行了细分? @sarnold:独角兽肯定不慢。在我的应用程序中,15 名工作人员以亚毫秒级的响应时间处理 10-30k rpm。 @Mike 注意here 和here 关于慢速客户端的警告: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. 据我了解,这将显着影响您的性能测试。如果这是有用的信息,我会重写它作为答案。 @MrGomez 这就是我们使用 nginx 的原因,我们不通过 unicorn 处理大型上传(我们使用的是节点)。 @Mike 你知道发生了什么吗?我在使用独角兽的请求队列中看到了类似的时间...... 【参考方案1】:

我刚刚诊断出一个外观相似的 New relic 图完全是 SSL 的错。尝试将其关闭。我们看到 400 毫秒的请求排队时间,在没有 SSL 的情况下下降到 20 毫秒。

关于为什么某些 SSL 提供程序可能运行缓慢的一些有趣点:http://blog.cloudflare.com/how-cloudflare-is-making-ssl-fast

【讨论】:

我刚刚添加了一个编辑,几个月前我们就知道了。我们没有将其关闭,但更改密码成功了。无论如何我都会接受你的回答。 您是否在 SSL/TLS 到达上游之前终止它?第二个问题:您是否尝试在反向代理端缓存 SSL/TLS 连接? @mikhailov 两者都不是,我想。这是一个托管在 Engineyard 上的 Rails 应用程序。 @MattGibson 你没有这个盒子的根访问权限吗? @mikhailov 是的,我做到了,但该项目已被封存,不再运行。【参考方案2】:

你使用的是什么版本的 ruby​​、unicorn、nginx(应该没多大关系但值得一提)和 newrelic_rpm?

另外,我会尝试在没有 newrelic 的情况下运行基线性能测试。 NewRelic 解析响应,在某些情况下,由于 ruby​​ 1.9.3 之前的 'rindex' 问题,这可能会很慢。这通常只有在您的响应非常大并且不包含“body”标签(例如 AJAX、JSON 等)时才会注意到。我看到了一个例子,其中一个 1MB AJAX 响应需要 30 秒才能让 NewRelic 解析。

【讨论】:

感谢您的回复,我在EDIT3中回复了 鉴于您没有看到Passenger的问题,这不太可能是我所指的NewRelic问题。 我正在调查一个类似的问题,我怀疑 nginx 的 NewRelic 补丁包含来自客户端的请求的下载时间,这可以解释高“排队”时间。不过,目前我还无法证明这一点……【参考方案3】:

您确定在 nginx 中缓冲来自客户端的请求,然后缓冲来自独角兽的响应,然后再将它们发送回客户端。从您的设置看来,您确实这样做了(因为这是默认设置),但我建议您仔细检查一下。

要查看的配置是:

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

这是为了缓冲来自独角兽的响应。您肯定需要它,因为您不想让独角兽忙于向慢速客户端发送数据。

对于来自客户端的请求的缓冲我认为你需要看看:

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

我认为这一切都无法解释 100 毫秒的延迟,但我并不熟悉您的所有系统设置,因此值得看看这个方向。看来您的排队不是由 CPU 争用引起的,而是由某种 IO 阻塞引起的。

【讨论】:

我尝试使用client_body_buffer_size 256k;proxy_buffers 8 256k;,但没有改善。

以上是关于独角兽请求排队的主要内容,如果未能解决你的问题,请参考以下文章

2021全球独角兽榜发布:中国301家独角兽企业全名单来了!

全球估值最高AI芯片独角兽诞生!仍是中企

中国独角兽企业估值榜 TOP300

石墨文档入选「北京市朝阳区未来独角兽企业榜单」

独角兽超时处理

2019年中国独角兽企业