Spara serverns processid i en fil
När man startar en server så kan det ibland vara behändigt att ha koll på vilket process id som servern får. Låt oss se hur vi kan ta reda på serverns process id i en Node.js server och spara undan detta id i en fil.
Låt oss se hur vi kan skicka signaler till servern för att stänga ned den och låt oss se hur vi kan hitta servrar som vi “tappat bort” i bakgrunden.
#Förutsättning
Följande artikel förutsätter att du kör på en Unix-maskin, Linux eller Mac OS. Du kan köra vissa delar av artikeln via Cygwin på en Windows-maskin, det finns noterat när du behöver göra annorlunda.
I slutet av artikeln visas en sammanfattning i hur du gör motsvarande ps och kill på Cygwin och Windows.
Artikeln bygger delvis på ett exempel som du återfinner i ditt kursrepo (linux, dbjs) under example/nodejs/saveProcessId
.
#Processens id
När man startar igång en process (på Unix/Linux/Mac OS) så får den ett process id som man kan använda för att senare kommunicera med processen. Detta är speciellt bra när man skickar processerna till bakgrunden med &
-tecknet.
Se när jag lägger en process i bakgrunden från terminalen.
$ sleep 5 && ls & [1] 30408
Kommandosekvensen sover i fem sekunder och gör sedan en ls
på katalogen man står i. Det är alltså hela kommandot sleep 5 && ls
som körs i bakgrunden. Den siffran som skrivs ut är processens id.
Låt oss göra samma test men vi låter den sova lite längre så vi kan se den i processtabellen.
$ sleep 50 && ls & [1] 30547 $ ps PID TTY TIME CMD 22338 pts/25 00:00:00 bash 30546 pts/25 00:00:00 bash 30547 pts/25 00:00:00 sleep 30548 pts/25 00:00:00 ps $ ps aux | grep 30547 mos 30547 0.0 0.0 7488 1404 pts/25 S 16:40 0:00 sleep 50 mos 30564 0.0 0.0 14384 1704 pts/25 S+ 16:41 0:00 grep --color=auto 30547
Med kommandot ps
kunde jag nu se processen i processtabellen. Du kan använda olika switchar till kommandot ps. Till exempel ps -H
för att visa en trädstruktur över familjeförhållanden mellan processer. Men kommandot tar olika switchar beroende på vilket system du är, så kolla man-sidan för att se vad just ditt system, stödjer.
#Skicka signaler till en process
När en process körs i bakgrunden så behövs ett sätt att kommunicera med den, och stänga ned den. Ett sådant sätt är kommandot kill
som kan skicka en signal till en process via processens id.
Det finns många olika signaler och vi skall inte gå in på dem här. Men låt oss ändå kika på en översikt.
$ kill -l
1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP
6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1
11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM
16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP
21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ
26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR
31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3
38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8
43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7
58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2
63) SIGRTMAX-1 64) SIGRTMAX
Ett vanligt sätt för en server att hantera en signal kan vara att läsa om sin konfigurationsfil eller att stänga ned sig själv. En process kan implementera olika hanterare när den mottar en signal.
Men, vi skall hålla det enkelt. Allt vi vill göra är att ha en möjlighet att döda servern. Så här.
$ sleep 60 &
[1] 31060
$ kill 31060
[1]+ Terminated sleep 60
Ibland kan processen vara lite svårare att stänga ned, då kan man skicka signalen SIGKILL, nummer 9, för att vara övertydlig att man verkligen vill stänga ned processen oavsett vad.
$ kill -9 31060
[1]+ Killed sleep 60
Att göra kill -9 PID
är det mest brutala sättet att stänga ned en process på. Vill du vara snällare så börjar du med kill PID
där PID är processens id.
#Processid för en Node.js server
Jag tar och gör ett litet exempelprogram saveProcessId
i Node.js som tar reda på det process id som programmet får. Värdet på processens id finns i variabeln process.pid
.
$ node index.js Simple server listen on port 1337 with process id 31410
Låt säga att jag nu vill skicka processen till bakgrunden istället. Så här.
$ node index.js &
[1] 28975
Simple server listen on port 1337 with process id 28981
Wrote process id to file 'pid'
Var uppmärksam på att det är två olika processid som visas, det är det som skrivs till filen pid
som du vill använda, i detta fallet 28981.
Nu kan jag skicka en signal till processen via kill
(Cygwin: /bin/kill -f
).
$ kill 28981
Caught SIGTERM. Removing pid-file and will then exit.
Bra, nu kan jag kommunicera med processen, även om jag lägger den i bakgrunden.
Ibland får man inte någon utskrift om att processen har avbrutits. Är man osäker så skickar man signalen en gång till. Om id:et inte finns så får man ett felmeddelande.
$ kill 31576
-bash: kill: (31576) - No such process
Då vet man att processen är borta. Men vad händer om jag startar upp servern igen?
#Min port är upptagen?
Jag startar servern igen (trots att jag redan har en server startad som jag “glömt bort”).
$ node index.js
Simple server listen on port 1337 with process id 32569
events.js:141
throw er; // Unhandled 'error' event
^
Error: listen EADDRINUSE :::1337
Det gick ju inte. Krash. Någon lyssnar på porten? Hmm.
Låt mig visa hur det fungerar.
Vi startar från början och låtsas att inget har hänt, porten är ledig.
$ node index.js &
[1] 29367
Simple server listen on port 1337 with process id 29373
Wrote process id to file 'pid'
Låt nu se vilken process som blockar porten 1337. Vi använder kommandot netstat
(Cygwin: netstat -a -o | grep 1337
).
$ netstat -an --tcp --program | grep 1337 tcp6 0 0 :::1337 :::* LISTEN 29373/nodejs
I slutet av raden ser vi namnet på processen och vilket PID den har, 29373 i detta fallet.
Det är samma PID som ligger i filen pid
och med det kan vi döda processen.
$ cat saveProcessId/pid
29373
$ kill 29373
Caught SIGTERM. Removing pid-file and will then exit.
[1]+ Done node saveProcessId/index.js
Bra, då vet vi att vi kan använda netstat
för att se vilken process som körs på en viss port.
#Funkar det på Cygwin?
Jajamensan, det fungerar på Cygwin om du använder de varianter jag visat. Men låt oss ta en snabb repetition för hur det fungerar på Cygwin som är delvis Unix och delvis Windows.
$ node index.js &
[1] 3348
Simple server listen on port 1337 with process id 6392
Wrote process id to file 'pid'
Notera att det är två processid som vanligt. Det som är lite udda här är att det första är från Cygwin och det andra är en process i Windows.
Den första processen hittar du som vanligt med ps
.
$ ps | grep 3348 3348 4924 3348 5544 cons0 197609 17:34:35 /usr/bin/sh 5760 3348 3348 6460 cons0 197609 17:34:35 /cygdrive/c/Program Files/nodejs/node
Den andra processen hittar du som en process i Windows via ps -W
.
$ ps -W | grep 6392 6392 0 0 6392 ? 0 17:34:36 C:\Program Files\nodejs\node.exe
Den första processen kan du döda med kill
som vanligt eftersom den är en process i Cygwin.
Men, den andra behöver du troligen döda med /bin/kill -f
. Den varianten av kill kommer att försöka döda processen som en windows process.
$ /bin/kill --help -f, --force force, using win32 interface if necessary
För att kontrollera vilken process som kör på en viss port så använder vi netstat
.
$ netstat -a -o | grep 1337 TCP 0.0.0.0:1337 DESKTOP-C3SVUSV:0 LISTENING 6392 TCP [::]:1337 DESKTOP-C3SVUSV:0 LISTENING 6392
Eftersom Node.js är (troligen) installerat via Windows och inte Cygwin, så är det en process i Windows som vi behöver döda, alltså använder vi /bin/kill -f
.
#Spara processid i en fil
Låt oss prata lite om exempelprogrammet saveProcessId
som vi använt för våra tester.
Jag har utökat det med att skriva processens id till en fil. Det är bra om man har ett externt skript eller liknande som skall skicka signaler till servern.
För att ta ett jämförande exempel så sparar webbservern Apache sitt pid i en fil i /run/apache2/apache2.pid
(på Debian, det skiljer mellan system) för att externa tjänster skall kunna kommunicera med processen via dess pid.
$ cat /run/apache2/apache2.pid 1442
Jag tar mitt exempelprogram som nu ser ut så här. Att skriva till fil görs med modulen fs.
import server from "./server.js";
const path = require('path');
const fs = require("fs");
// Start the server to listen on a port
server.listen(1337);
// Write pid to file
var pidFile = path.join(__dirname, "pid");
fs.writeFile(pidFile, process.pid, function(err) {
if (err) {
return console.log(err);
}
console.log("Wrote process id to file 'pid'");
});
console.log("Simple server listen on port 1337 with process id " + process.pid);
Perfekt. Nu sparar vi alltså processens pid till en fil och det gör det enkelt att komma åt den om vi vill skicka en signal till den.
#Fånga en signal och radera pid-filen
Nu när jag skapar en pid-fil så vore det ju bra om jag städade bort den när processen stängs ned. Ett sätt att göra det är att hantera signalen som skickas till node-processen.
Med node är det enkelt att fånga en signal. Här ser du hur man lägger till en callback som anropas när en viss signal tas emot av processen.
/**
* Listen on SIGINT, SIGTERM
*/
function controlledShutdown(signal) {
console.warn(`Caught ${signal}. Removing pid-file and will then exit.`);
fs.unlinkSync(pidFile);
process.exit();
}
// Add event handlers for signals
process.on("SIGTERM", () => { controlledShutdown("SIGTERM"); });
process.on("SIGINT", () => { controlledShutdown("SIGINT"); });
Detta fungerar som tänkt på en Unix-plattform. Man kan både avbryta processen med CTRL-C
och genom att skicka en signal till processen med kill
. Resultatet blir att processen stängs ned och filen plockas bort.
Defaultsignalen som skickas med kill är SIGTERM
.
Men på Windows finns ett annat sätt att hantera vissa signaler så här kan det behövas lite mer handpåläggning. Följande löser så att signalen för CTRL_C
fungerar.
// Handle WIN32 signals in a specific mode
if (process.platform === "win32") {
var rl = require("readline").createInterface({
input: process.stdin,
output: process.stdout
});
rl.on("SIGINT", function () {
process.emit("SIGINT");
});
}
Som synes så blir det lite handpåläggning för att få signalhanteringen att fungera med Windows. Men det är ju i grund och botten två helt skilda operativsystem, Windows och Unix, så det är ju inte så konstigt.
Se det som ett exempel på hur man kan skriva plattformsspecifik kod i Node.
#Avslutningsvis
Nu har du lärt dig hur du hittar och kan använda dig av processens pid. Det är bra att ha allmän koll på detta när man jobbar med servrar.
Artikeln har en egen tråd i forumet där du kan fråga eller ge tips, relaterat till denna artikel.
#Revision history
- 2017-02-14: (C, mos) Bort med babel.
- 2017-02-06: (B, mos) Uppdaterat exempelprogram och används även i dbjs-kursen, inklusive fånga signaler och Cygwin specifikt.
- 2015-10-16: (A, mos) Första utgåvan i kursen linux.