Использование памяти единорога заполняет почти все ОЗУ
![New Relic Process snapshot]()
Здесь есть по существу 3 проблемы:
1) Единорог, похоже, неуклонно заполняет всю ОЗУ, заставляя меня вручную удалять работников.
2) Единорог почему-то порождает дополнительных работников, хотя я указал определенное количество рабочих (7 из них). Это отчасти вызывает наращивание памяти, что также заставляет меня вручную удалять работников.
3). В моем случае ненадежное развертывание бездействия является ненадежным. Иногда он подбирает изменения, иногда я получаю таймауты шлюза. Каждое развертывание становится очень напряженной ситуацией.
Мне не очень нравится использовать Монита, потому что он убивает рабочих, не дожидаясь, пока рабочие перестанут обслуживать свои запросы.
Итак, это нормально? У других людей, которые используют Unicorn, есть такая же проблема, когда ОЗУ просто растет неуправляемо?
А также где рабочие числа порожденных рабочих не совпадают с числом определенных работников?
Другой альтернативой является убийца-убийца единорога, который я бы попробовал после чтения Unicorn Eating Memory.
Маленькое обновление:
![enter image description here]()
Итак, дошло до того, что New Relic рассказывала мне, что память была почти на 95%. Поэтому мне пришлось убить рабочего. Интересно, что убийство этого работника привело к значительному снижению памяти, как видно из графика ниже.
Что с этим?
Для справки, здесь мои unicorn.rb
и unicorn_init.sh
. Хотелось бы, чтобы кто-то сказал мне, что там где-то есть ошибка.
unicorn.rb
root = "/home/deployer/apps/myapp/current"
working_directory root
pid "#{root}/tmp/pids/unicorn.pid"
stderr_path "#{root}/log/unicorn.stderr.log"
stdout_path "#{root}/log/unicorn.log"
listen "/tmp/unicorn.myapp.sock"
worker_processes 7
timeout 30
preload_app true
before_exec do |_|
ENV["BUNDLE_GEMFILE"] = '/home/deployer/apps/myapp/current/Gemfile'
end
before_fork do |server, worker|
# Disconnect since the database connection will not carry over
if defined? ActiveRecord::Base
ActiveRecord::Base.connection.disconnect!
end
old_pid = "#{root}/tmp/pids/unicorn.pid.oldbin`"
if old_pid != server.pid
begin
sig = (worker.nr + 1) >= server.worker_processes ? :QUIT : :TTOU
Process.kill(sig, File.read(old_pid).to_i)
rescue Errno::ENOENT, Errno::ESRCH
end
end
sleep 1
end
after_fork do |server, worker|
# Start up the database connection again in the worker
if defined?(ActiveRecord::Base)
ActiveRecord::Base.establish_connection
end
Redis.current.quit
Rails.cache.reconnect
end
unicorn_init.sh
#!/bin/sh
set -e
# Feel free to change any of the following variables for your app:
TIMEOUT=${TIMEOUT-60}
APP_ROOT=/home/deployer/apps/myapp/current
PID=$APP_ROOT/tmp/pids/unicorn.pid
CMD="cd $APP_ROOT; BUNDLE_GEMFILE=/home/deployer/apps/myapp/current/Gemfile bundle exec unicorn -D -c $APP_ROOT/config/unicorn.rb -E production"
AS_USER=deployer
set -u
OLD_PIN="$PID.oldbin"
sig () {
test -s "$PID" && kill -$1 `cat $PID`
}
oldsig () {
test -s $OLD_PIN && kill -$1 `cat $OLD_PIN`
}
run () {
if [ "$(id -un)" = "$AS_USER" ]; then
eval $1
else
su -c "$1" - $AS_USER
fi
}
case "$1" in
start)
sig 0 && echo >&2 "Already running" && exit 0
run "$CMD"
;;
stop)
sig QUIT && exit 0
echo >&2 "Not running"
;;
force-stop)
sig TERM && exit 0
echo >&2 "Not running"
;;
restart|reload)
sig USR2 && echo reloaded OK && exit 0
echo >&2 "Couldn't reload, starting '$CMD' instead"
run "$CMD"
;;
upgrade)
if sig USR2 && sleep 2 && sig 0 && oldsig QUIT
then
n=$TIMEOUT
while test -s $OLD_PIN && test $n -ge 0
do
printf '.' && sleep 1 && n=$(( $n - 1 ))
done
echo
if test $n -lt 0 && test -s $OLD_PIN
then
echo >&2 "$OLD_PIN still exists after $TIMEOUT seconds"
exit 1
fi
exit 0
fi
echo >&2 "Couldn't upgrade, starting '$CMD' instead"
run "$CMD"
;;
reopen-logs)
sig USR1
;;
*)
echo >&2 "Usage: $0 <start|stop|restart|upgrade|force-stop|reopen-logs>"
exit 1
;;
esac
Ответы
Ответ 1
Кажется, у вас две проблемы: 1) У вас есть ошибки в координации грациозного перезапуска, в результате чего старые работники единорога и старый мастер встанут; 2) Ваше приложение (не единорог) - это утечка памяти.
Для первого, глядя на ваш код before_fork
, кажется, вы используете подход с ограничением памяти из пример конфигурации Однако, у вас есть опечатка в имени файла .oldbin
(посторонний back-tick в конце), что означает, что вы никогда не сигнализируете старый процесс, потому что вы не можете прочитать pid из несуществующего файла.
Позже вам придется исследовать и тренировать. Посмотрите в своем приложении для кеширования семантики, которая накапливает данные с течением времени; внимательно изучите все использование глобалов, классов-варов и классов-экземпляров-варов, которые могут сохранять ссылки на данные из запроса для запроса. Запустите некоторые профили памяти, чтобы охарактеризовать использование вашей памяти. Вы можете уменьшить утечку памяти, убив работников, когда они вырастут больше, чем какой-либо верхний предел; unicorn-worker-killer упрощает процесс.
Ответ 2
Используйте unicorn-worker-killer, это позволяет убивать работников, которые потребляют много ОЗУ:)