問題: 在本地啓動egg.js,使用egg-socket.io,能正常連接到websocket,而放到docker中運行,則無法連接得上,提示Session ID unknown,http報400錯誤。
原因: 之所以在本地能成功連接,是因爲本地啓動方式爲egg-bin dev
,會針對本地開發做很多處理,而在docker中啓動腳本是egg-scripts start
,如果沒有在sticky模式下運行,則會出現無法正常連接,同時報錯。
值得注意點是Egg框架是以 Cluster 方式啓動的,而 socket.io 協議實現需要 sticky 特性支持,否則在多進程模式下無法正常工作。
由於 socket.io 的設計,在多進程中服務器必須在 sticky
模式下工作,故需要給 startCluster 傳遞 sticky 參數。
添加之後再運行就連接正常了。
修改 package.json
中 npm scripts
腳本:
{
"scripts": {
"dev": "egg-bin dev --sticky",
"start": "egg-scripts start --sticky"
}
}
Nginx 配置
location / {
# 這兩行需要開啓
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:7001;
}
Cluster 是什麼呢?
簡單的說,
- 在服務器上同時啓動多個進程。
- 每個進程裏都跑的是同一份源代碼(好比把以前一個進程的工作分給多個進程去做)。
- 更神奇的是,這些進程可以同時監聽一個端口(具體原理推薦閱讀 @DavidCai1993 這篇 Cluster 實現原理)。
其中:
- 負責啓動其他進程的叫做 Master 進程,他好比是個『包工頭』,不做具體的工作,只負責啓動其他進程。
- 其他被啓動的叫 Worker 進程,顧名思義就是幹活的『工人』。它們接收請求,對外提供服務。
- Worker 進程的數量一般根據服務器的 CPU 核數來定,這樣就可以完美利用多核資源。
const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
if (cluster.isMaster) {
// Fork workers.
for (let i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', function(worker, code, signal) {
console.log('worker ' + worker.process.pid + ' died');
});
} else {
// Workers can share any TCP connection
// In this case it is an HTTP server
http.createServer(function(req, res) {
res.writeHead(200);
res.end("hello world\n");
}).listen(8000);
}