Как настроить работу Websockets, с использованием Socket.IO, на облачной платформе DigitalOcean App Platform?
Надеюсь, у вас отличный день.
Вступление
Вопрос касается deploy на DigitalOcean с использованием их App Platfrom, а также общего понимания devops.
Использую Node v18.12.1, Socket.IO v4.1.3 на сервере, Socket.IO v4.6.1 на клиенте.
Я уже некоторое время пытаюсь установить соединение через Socket.IO с серверным компонентом моей платформы приложений (App Platform), но пока безуспешно. К моему удивлению, на моей локальной машине все работает нормально. Помимо всего прочего обычные HTTP запросы к серверу на DigitalOcean также отрабатывают совершенно нормально на порт 3060. Т.е. проблема исключительно в Websockets на DigitalOcean (и, вероятно, моей криворукости).
Описание неисправности
После отправки события Socket.IO от клиента я получаю только следующее в Devtools -> Network -> Preview:
You need to enable JavaScript to run this app.
Читал также, что проблема может скрываться в HTTPS протоколе, который на DigitalOcean включен по-умолчанию. Однако в таком случае не совсем понятно почему отрабатывают остальные HTTP запросы к серверу.
Подробности
Я использую node:cluster для своего сервера. Этот код был взят из официальной документации Socket.IO для node:cluster.
...
// До этого момента идут разные require, подключения middleware...
if (cluster.isPrimary) {
console.log("Primary cluster is running...");
const httpServer = http.createServer(app);
// setup sticky sessions
setupMaster(httpServer, {
loadBalancingMethod: "least-connection",
});
// setup connections between the workers
setupPrimary();
const primaryClusterPort = 3265;
httpServer.listen(primaryClusterPort, () => {
// Listen on the HTTP server in the worker process
console.log(`Primary cluster running on port ${primaryClusterPort}...`);
});
const workerCount = 2;
for (let i = 0; i < workerCount; i++) {
cluster.fork();
}
cluster.on("online", function (worker) {
console.log("Worker " + worker.process.pid + " is online");
});
cluster.on("exit", function (worker, code, signal) {
console.log(
"Worker " +
worker.process.pid +
" died with code: " +
code +
", and signal: " +
signal
);
console.log("Starting a new worker");
cluster.fork();
});
} else {
const httpServer = http.createServer(app); // Create the HTTP server in the worker process
const io = new Server(httpServer, {
cors: {
origin: "*", // Allow all origins
methods: ["GET", "POST"], // Allow GET and POST methods
credentials: true, // Allow credentials
},
});
io.adapter(createAdapter());
// setup connection with the primary process
setupWorker(io);
io.on("connection", (socket) => {
socket.on("exportExcel", (data) => {
console.log("Socket on `exportExcel`: ", data);
// for test
socket.emit("exportExcel", { path: "export905.xlsx" });
});
});
httpServer.listen(PORT, () => {
// Listen on the HTTP server in the worker process
console.log(`App listening on port ${PORT}...`);
});
}
Я надеюсь, что кто-нибудь сможет мне помочь, и я готов предоставить любую дополнительную информацию, если это необходимо. Заранее спасибо.
Ответы (1 шт):
Ради проверки переписал свой код (с использованием API от Socket.IO) на API npm-пакета ws.
TL;DR
Проблемой для DigitalOcean App Platform является само наличие Socket.IO. Решение: Использовать другую библиотеку для сокетов (я выбрал ws).
Client (функция на button onClick):
const protocol = window.location.protocol.includes(
"https"
)
? "wss"
: "ws";
const ws = new WebSocket(`${protocol}://${SOCKET_URL}`);
ws.onopen = () => {
let filtPost = parseFilterToQuery({
filters,
typeAnd,
});
const message = JSON.stringify({
nameColl,
filtrs: filtPost,
cms,
});
ws.send(message);
};
ws.onmessage = function (data) {
const aLink = document.createElement("a");
aLink.href = SERVER_URL + data.data;
aLink.target = "_blank";
aLink.download = "export.xlsx";
aLink.click();
};
Обратите внимание на самую первую строку:
const protocol...
Это обязательное условие для того, чтобы на DigitalOcean мы подключались соответсвенно к wss (зашифрованному) сокет-соединению.
Server:
...
} else {
const httpServer = http.createServer(app); // Create the HTTP server in the worker process
const wss = new WebSocket.Server({ server: httpServer });
wss.on("connection", function (ws) {
ws.on("message", async function (message) {
const parsedMessage = JSON.parse(message);
exportExcel({
...parsedMessage,
ws,
userID: "test",
cmsSlug: parsedMessage.cms,
callback: (data) => {
ws.send(data);
},
});
console.log("received: %s", message);
});
});
httpServer.listen(PORT, () => {
// Listen on the HTTP server in the worker process
console.log(`App listening on port ${PORT}...`);
});
}
