独角兽请求排队
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_size
和proxy_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;
,但没有改善。以上是关于独角兽请求排队的主要内容,如果未能解决你的问题,请参考以下文章