我在一台拥有2个CPU、2GB内存和Ubuntu 20.04操作系统的GCP计算引擎实例上运行了3个Node.js应用,并使用Nginx反向代理。其中之一是一个Socket.IO聊天服务器。Socket.IO应用程序使用@socket.io/cluster-adapter
来利用所有可用的CPU核心。
我按照此教程更新了Linux设置以获得最大连接数。这是ulimit
命令的输出:
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 7856
max locked memory (kbytes, -l) 65536
max memory size (kbytes, -m) unlimited
open files (-n) 500000
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 7856
virtual memory (kbytes, -v) unlimited
cat /proc/sys/fs/file-max
2097152
/etc/nginx/nginx.conf
user www-data;
worker_processes auto;
worker_rlimit_nofile 65535;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 30000;
# multi_accept on;
}
...
/etc/nginx/sites-available/default
...
//socket.io part
location /socket.io/ {
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header X-NginX-Proxy false;
proxy_pass http://localhost:3001/socket.io/;
proxy_redirect off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
...
我的聊天服务器代码,
const os = require("os");
const cluster = require("cluster");
const http = require("http");
const { Server } = require("socket.io");
const { setupMaster, setupWorker } = require("@socket.io/sticky");
const { createAdapter, setupPrimary } = require("@socket.io/cluster-adapter");
const { response } = require("express");
const PORT = process.env.PORT || 3001;
const numberOfCPUs = os.cpus().length || 2;
if (cluster.isPrimary) {
const httpServer = http.createServer();
// setup sticky sessions
setupMaster(httpServer, {
loadBalancingMethod: "least-connection", // either "random", "round-robin" or "least-connection"
});
// setup connections between the workers
setupPrimary();
cluster.setupPrimary({
serialization: "advanced",
});
httpServer.listen(PORT);
for (let i = 0; i < numberOfCPUs; i++) {
cluster.fork();
}
cluster.on("exit", (worker) => {
console.log(`Worker ${worker.process.pid} died`);
cluster.fork();
});
}
//worker process
else {
const express = require("express");
const app = express();
const Chat = require("./models/chat");
const mongoose = require("mongoose");
const request = require("request"); //todo remove
var admin = require("firebase-admin");
var serviceAccount = require("./serviceAccountKey.json");
const httpServer = http.createServer(app);
const io = require("socket.io")(httpServer, {
cors: {
origin: "*",
methods: ["GET", "POST"],
},
transports: "websocket",
});
mongoose.connect(process.env.DB_URL, {
authSource: "admin",
user: process.env.DB_USERNAME,
pass: process.env.DB_PASSWORD,
});
app.use(express.json());
app.get("/", (req, res) => {
res
.status(200)
.json({ status: "success", message: "Hello, I'm your chat server.." });
});
// use the cluster adapter
io.adapter(createAdapter());
// setup connection with the primary process
setupWorker(io);
io.on("connection", (socket) => {
activityLog(
"Num of connected users: " + io.engine.clientsCount + " (per CPU)"
);
...
//chat implementations
});
}
加载测试客户端代码,
const { io } = require("socket.io-client");
const URL = //"https://myserver.com/";
const MAX_CLIENTS = 6000;
const CLIENT_CREATION_INTERVAL_IN_MS = 100;
const EMIT_INTERVAL_IN_MS = 300; //1000;
let clientCount = 0;
let lastReport = new Date().getTime();
let packetsSinceLastReport = 0;
const createClient = () => {
const transports = ["websocket"];
const socket = io(URL, {
transports,
});
setInterval(() => {
socket.emit("chat_event", {});
}, EMIT_INTERVAL_IN_MS);
socket.on("chat_event", (e) => {
packetsSinceLastReport++;
});
socket.on("disconnect", (reason) => {
console.log(`disconnect due to ${reason}`);
});
if (++clientCount < MAX_CLIENTS) {
setTimeout(createClient, CLIENT_CREATION_INTERVAL_IN_MS);
}
};
createClient();
const printReport = () => {
const now = new Date().getTime();
const durationSinceLastReport = (now - lastReport) / 1000;
const packetsPerSeconds = (
packetsSinceLastReport / durationSinceLastReport
).toFixed(2);
console.log(
`client count: ${clientCount} ; average packets received per second: ${packetsPerSeconds}`
);
packetsSinceLastReport = 0;
lastReport = now;
};
setInterval(printReport, 5000);
如您所见,从此代码中可以看出,我只使用
websocket
来传输。因此,按照这个StackOverflow答案的说法,应该能够服务最多8000个连接。但是当我运行负载测试时,服务器在1600个连接后变得不稳定。CPU使用率升高到90%,内存使用率升高到70%。在Nginx错误日志中找不到任何问题。我该如何将连接数增加至至少8000个?我应该升级实例还是更改任何Linux设置?任何帮助都将不胜感激。更新: 我删除了与群集相关的所有内容,并将其重新作为普通的单线程nodejs应用程序运行。这次,结果稍微好一些,稳定连接数量为2800个(CPU使用率40%,内存使用率50%)。请注意,在测试期间,我没有执行任何磁盘I/O操作。