2017-06-27 12 views
8

Trong ứng dụng Nút của tôi, tôi đang tìm kiếm tín hiệu SIGINT để dừng một cách duyên dáng (sử dụng pm2, nhưng điều này không liên quan ở đây).Quy trình con của nút: cách đánh chặn các tín hiệu như SIGINT

Ứng dụng của tôi cũng thực thi/sinh ra một vài quy trình con.

Tôi có thể móc trên SIGINT để chặn và thực hiện dừng duyên dáng, tuy nhiên quy trình con của tôi được truyền qua cùng một tín hiệu và do đó, ngay lập tức bị tiêu diệt.

Tôi làm cách nào để chặn tín hiệu SIGINT trên các quy trình con của mình?

Một ví dụ về những gì tôi đang làm:

const child = child_process.spawn('sleep', ['10000000']); 
console.log(`Child pid: ${child.pid}`); 

child.on('exit', (code, signal) => { console.log('Exit', code, signal); }); 

process.on('SIGINT',() => { 
    console.log("Intercepting SIGINT"); 
}); 

Trả lời

4

Theo mặc định, các quy trình con được tạo bởi child_process.spawn() có cùng số process group làm cha mẹ, trừ khi chúng được gọi với số {detached:true}option.

Kết quả là kịch bản này sẽ hành xử khác nhau trong các môi trường khác nhau:

// spawn-test.js 
const { spawn } = require('child_process'); 
const one = spawn('sleep', ['101']); 
const two = spawn('sleep', ['102'], {detached: true}); 
two.unref(); 
process.on('SIGINT', function() { 
    console.log('just ignore SIGINT'); 
}); 

Mở vỏ tương tác, một SIGINT từ CTL-C sẽ được gửi đến toàn bộ nhóm theo mặc định, vì vậy con không tách ra sẽ lấy SIGINT và thoát:

[email protected] $ node spawn-test.js 
^Cjust ignore SIGINT 
[email protected] [another-terminal-window] $ ps aux | grep sleep 
... sleep 102 
# note that sleep 101 is not running anymore 
# because it recieved the SIGINT from the Ctl-C 

... nhưng các cuộc gọi đến kill(2) chỉ có thể báo hiệu quá trình cha mẹ của bạn, vì vậy trẻ em sống sót:

[email protected] $ node spawn-test.js & echo $? 
[2] 1234 
[email protected] [another-terminal-window] $ kill -SIGINT 1234 
[email protected] [another-terminal-window] $ ps aux | grep sleep 
... sleep 101 
... sleep 102 
# both are still running 

Tuy nhiên, pm2 là một con thú khác. Thậm chí nếu bạn thử các kỹ thuật nói trên, nó giết chết cây toàn bộ quá trình, bao gồm quá trình tách của bạn, ngay cả với một dài --kill-timeout:

# Test pm2 stop 
[email protected] $ pm2 start spawn-test.js --kill-timeout 3600 
[email protected] $ pm2 stop spawn-test 
[email protected] $ ps aux | grep sleep 
# both are dead 

# Test pm3 reload 
[email protected] $ pm2 start spawn-test.js --kill-timeout 3600 
[email protected] $ pm2 reload spawn-test 
[email protected] $ ps aux | grep sleep 
# both have different PIDs and were therefore killed and restarted 

Điều này có vẻ giống như một lỗi trong PM2.

Tôi đã gặp các sự cố tương tự bằng cách sử dụng hệ thống init (systemd trong trường hợp của tôi) thay vì pm2, vì điều này cho phép kiểm soát tín hiệu tốt hơn.Trên hệ thống, các tín hiệu được gửi đến toàn bộ nhóm theo mặc định, nhưng bạn có thể sử dụng KillMode=mixed để chỉ gửi tín hiệu đến tiến trình cha, nhưng vẫn SIGKILL con xử lý nếu chúng vượt quá thời gian chờ.

file đơn vị systemd của tôi trông như thế này:

[Unit] 
Description=node server with long-running children example 

[Service] 
Type=simple 
Restart=always 
RestartSec=30 
TimeoutStopSec=3600 
KillMode=mixed 
ExecStart=/usr/local/bin/node /path/to/your/server.js 

[Install] 
WantedBy=multi-user.target 
+0

Cảm ơn cho câu trả lời tuyệt vời của bạn! –

3

Thông thường trong C, bạn muốn giải quyết việc này bằng cách bỏ qua các tín hiệu ở trẻ em (hoặc bằng cách đẻ trứng nó trong một nhóm quy trình mới để các thiết bị đầu cuối tín hiệu được tạo ra cho nhóm tiến trình nền trước không đạt được nó).

Từ nhìn vào https://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options, có vẻ như NodeJ không hiển thị API cho điều này, tuy nhiên, nó có tùy chọn để sinh ra quá trình con thông qua trình bao, vì vậy những gì bạn có thể làm là bật và bỏ qua tín hiệu trong trình bao, điều này sẽ khiến trạng thái bị bỏ qua của nó được kế thừa cho con của trình bao.

const child_process = require('child_process') 
//const child = child_process.spawn('sleep', ['10000000']); 
const child = child_process.spawn("trap '' INT; sleep 10000000", [], {shell: true }); 
console.log(`Child pid: ${child.pid}`); 

child.on('exit', (code, signal) => { console.log('Exit', code, signal); }); 

process.on('SIGINT',() => { 
    console.log("Intercepting SIGINT"); 
}); 


//emulate cat to keep the process alive 
process.stdin.pipe(process.stdout); 

Bây giờ, khi bạn nhấn Ctrl-C, quá trình nút xử lý nó và quá trình ngủ sẽ tiếp tục. (Trong trường hợp bạn không quen thuộc với các tín hiệu được tạo ra từ thiết bị đầu cuối khác, bạn có thể dễ dàng tiêu diệt nhóm này bằng cách nhấn Ctrl- \ (gửi SIGQUIT đến nhóm) nếu bạn không nhớ đến coredump).

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