多核心下 nginx - node.js 的組合

目前我們網站都是寫在 node.js 上,然後用 nginx 當 reverse proxy 來接。

以四核然後有 VT 為例,nproc 看到的會是 8,此時一些相對應的 nginx.conf 設定:

1
2
worker_processes 8;
worker_cpu_affinity 00010001 00010001 00100010 00100010 01000100 01000100 10001000 10001000;

worker_cpu_affinity 的值是按照 /proc/cpuinfo 內容來決定的,用來做範例的機器排列是 processor 0~3 對應到 core 0~3,然後 processor 4~7 又對應到 core 0~3,所以 processor [0, 4], [1, 5], [2, 6], [3, 7] 各自屬於同一個 core。同一個 core 可以允許 processes 互換,所以上面 worker_cpu_affinity 的意思是指定 worker 0 可以跑在 0, 4 兩個 CPU 上(第 0 跟第 4 bit 為 1),然後 worker 1 也是跑在 0, 4 上面。依此類推。

node.js 部份並沒有用 forever 來跑,而是用 upstart 來看住。相關的一些設定長這樣:

1
2
3
4
5
6
7
8
9
10
11
12
# portal.conf

start on runlevel [2345]

task

script
NPROC=$(nproc)
for N in $(seq $NPROC); do
start portal-worker N=$N
done
end script

用 portal 去跑好幾個 portal-worker 起來。而 portal-worker 則是會看執行時給的 N 值來決定怎麼跑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# portal worker

manual

respawn

instance $N

env NODE_ENV=production
setuid www-data
setgid www-data
chdir /home/johndoe/portal
script
test $N = 1 && exec taskset 0x11 node server.js -c config-1
test $N = 2 && exec taskset 0x11 node server.js -c config-2
test $N = 3 && exec taskset 0x22 node server.js -c config-3
test $N = 4 && exec taskset 0x22 node server.js -c config-4
test $N = 5 && exec taskset 0x44 node server.js -c config-5
test $N = 6 && exec taskset 0x44 node server.js -c config-6
test $N = 7 && exec taskset 0x88 node server.js -c config-7
test $N = 8 && exec taskset 0x88 node server.js -c config-8
end script

portal-worker 的 log 都會在 /var/log/upstart 底下,例如 N=1 時,就會是 /var/log/upstart/portal-worker-1.log。另外可以看到 taskset 的邏輯基本上跟 worker_cpu_affinity 是一樣的。後面 -c 是自訂參數,用來指定不同設定檔,例如規定 node process 聽在不同的 port 上。這樣搭配 nginx 裡頭 upstream 功能就可以平均分配:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
upstream portal {
server 127.0.0.1:5001;
server 127.0.0.1:5002;
server 127.0.0.1:5003;
server 127.0.0.1:5004;
server 127.0.0.1:5005;
server 127.0.0.1:5006;
server 127.0.0.1:5007;
server 127.0.0.1:5008;
}

server {
# ...snipped...
location / {
proxy_pass http://portal;
proxy_redirect default;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $hostname;
proxy_set_header X-Forwarded-Proto $scheme;
}
}

值得注意的是,upstart 在 12.04 有個 bug,Supplementary groups not set for user jobs,這會導致上面的 setuid www-data 不如預期,例如 user www-data 的 supplementary group 有一個 groupabc,然後某檔案設定為 groupabc 可以寫,結果 upstart 跑起來以後是寫不進去的。 解法之一是改用 sudo -u www-data -E -- 來跑。14.04 就沒這問題了。