2013-08-10 24 views
14

New Relic Process snapshotUnicorn sử dụng bộ nhớ lấp đầy gần như tất cả các RAM

Có cơ bản 3 vấn đề ở đây:

1) Unicorn dường như đều đặn lấp đầy tất cả các RAM, gây cho tôi để loại bỏ nhân bằng tay.

2) Unicorn dường như sinh ra thêm công nhân vì lý do nào đó, mặc dù tôi đã chỉ định số lượng công nhân cố định (7 người trong số họ). Điều này một phần gây ra sự tích tụ RAM, điều này cũng khiến tôi phải loại bỏ công nhân một cách thủ công.

3) Triển khai thời gian ngừng hoạt động của Zero không đáng tin cậy trong trường hợp của tôi. Đôi khi nó chọn lên những thay đổi, đôi khi tôi nhận được thời gian chờ gateway. Mỗi triển khai trở thành một tình huống rất căng thẳng.

Tôi không thực sự thích sử dụng Monit, bởi vì nó giết chết công nhân mà không cần chờ công nhân hoàn thành việc phục vụ yêu cầu của họ.

Vì vậy, điều này có bình thường không? Những người khác triển khai bằng cách sử dụng Unicorn có cùng một vấn đề trong đó RAM chỉ phát triển không kiểm soát được không?

Và cũng ở nơi công nhân, số lượng công nhân sinh ra không khớp với số lượng công nhân được xác định?

Cách thay thế khác là kẻ giết người công nhân kỳ lân, mà tôi sẽ thử sau khi đọc Unicorn Eating Memory.

Tiny Cập nhật:

enter image description here

Vì vậy, nó đến một điểm mà New Relic đã nói cho tôi những ký ức đã gần 95%. Vì vậy, tôi đã phải giết một công nhân. Điều thú vị là, giết chết nhân viên đó đã mang bộ nhớ xuống rất nhiều, như được thấy từ biểu đồ bên dưới.

Có chuyện gì với điều đó?

Để tham khảo, đây là unicorn.rbunicorn_init.sh của tôi. Có tình yêu dành cho ai đó nói với tôi rằng có một sai lầm trong đó đâu đó.

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 
+0

bạn tạo biểu đồ này như thế nào? – ant

Trả lời

9

Dường như bạn có hai vấn đề: 1) Bạn có lỗi trong việc phối hợp khởi động lại duyên dáng khiến công nhân lân cũ và chủ cũ dính quanh; 2) Ứng dụng của bạn (không phải kỳ lân) đang rò rỉ bộ nhớ.

Đối với trước đây, nhìn vào mã before_fork của bạn, nó xuất hiện bạn đang sử dụng phương pháp bộ nhớ hạn chế từ the example config Tuy nhiên, bạn có một lỗi đánh máy trong tên tập tin .oldbin (một back-ve không liên quan ở cuối) có nghĩa bạn không bao giờ báo hiệu quá trình cũ vì bạn không thể đọc pid từ một tệp không tồn tại.

Để sau này, bạn sẽ phải điều tra và khoan.Tìm trong ứng dụng của bạn để lưu vào bộ nhớ đệm ngữ nghĩa tích luỹ dữ liệu theo thời gian; kiểm tra cẩn thận tất cả việc sử dụng globals, class-vars và class-instance-vars có thể giữ lại các tham chiếu dữ liệu từ yêu cầu đến yêu cầu. Chạy một số cấu hình bộ nhớ để mô tả việc sử dụng bộ nhớ của bạn. Bạn có thể giảm thiểu rò rỉ bộ nhớ bằng cách giết người lao động khi chúng lớn hơn một số giới hạn trên; unicorn-worker-killer làm cho việc này trở nên dễ dàng.

+0

Cảm ơn! Tôi sẽ sửa chữa nó ngay lập tức và báo cáo lại. Đánh giá cao thời gian. –

+0

Nhân viên giết người công nhân kỳ lân này có được sử dụng thương mại không? – tulio84z

+0

@ dbenhur u có thể giải thích thêm một chút về "khởi động lại duyên dáng" và cách tiếp cận hạn chế bộ nhớ này không? – tulio84z

3

Sử dụng unicorn-worker-killer, điều này giúp dễ dàng hơn khi tiêu diệt nhiều người lao động tiêu thụ nhiều RAM)

Các vấn đề liên quan