mirror of
https://github.com/whyour/qinglong.git
synced 2026-09-19 14:59:43 +08:00
perf: reduce resident dependencies and duplicate WebSocket authentication (#3071)
* perf: load HTTP dependencies only in the HTTP worker * docs: record 30-minute idle resource comparison * perf: isolate database initialization from the cluster primary * docs: record isolated database startup resource comparison * perf: avoid an extra shell child in Docker health probes * perf: load notification dependencies only when needed * perf: retain queue cleanup after notification loading comparison * fix: stop page polling after unmount and avoid duplicate fetches * perf: share WebSocket session polling across connections * docs: measure real WebSocket container CPU and memory * perf: reuse JWT validation within each WebSocket session check * perf: bound login IP lookup allocations with an address cache * refactor: limit resource optimization PR to three core changes * docs: benchmark the three core optimizations against develop * chore: remove documentation and benchmark artifacts from PR
This commit is contained in:
+60
-20
@@ -1,4 +1,4 @@
|
||||
import sockJs from 'sockjs';
|
||||
import sockJs, { Connection } from 'sockjs';
|
||||
import { Server } from 'http';
|
||||
import { Container } from 'typedi';
|
||||
import SockService from '../services/sock';
|
||||
@@ -13,6 +13,41 @@ export default async ({ server }: { server: Server }) => {
|
||||
log: () => {},
|
||||
});
|
||||
const sockService = Container.get(SockService);
|
||||
const sessions = new Map<Connection, { token: string; platform: string }>();
|
||||
let sessionTimer: ReturnType<typeof setInterval> | undefined;
|
||||
let checking = false;
|
||||
|
||||
const checkSessions = async () => {
|
||||
if (checking || sessions.size === 0) return;
|
||||
checking = true;
|
||||
// Connections accepted during this read must not use an older snapshot.
|
||||
const batch = [...sessions];
|
||||
try {
|
||||
const current = await shareStore.getAuthInfo();
|
||||
// Reuse validation only within this synchronous check of one auth snapshot.
|
||||
const validated = new Map<string, Map<string, boolean>>();
|
||||
for (const [conn, { token, platform }] of batch) {
|
||||
if (!sessions.has(conn)) continue;
|
||||
let platforms = validated.get(token);
|
||||
if (!platforms) {
|
||||
platforms = new Map();
|
||||
validated.set(token, platforms);
|
||||
}
|
||||
let valid = platforms.get(platform);
|
||||
if (valid === undefined) {
|
||||
valid = isValidToken(current, token, platform, config.jwt.secret);
|
||||
platforms.set(platform, valid);
|
||||
}
|
||||
if (!valid) conn.close('401');
|
||||
}
|
||||
} catch {
|
||||
for (const [conn] of batch) {
|
||||
if (sessions.has(conn)) conn.close('401');
|
||||
}
|
||||
} finally {
|
||||
checking = false;
|
||||
}
|
||||
};
|
||||
|
||||
echo.on('connection', async (conn) => {
|
||||
if (!conn.headers || !conn.url || !conn.pathname) {
|
||||
@@ -20,35 +55,40 @@ export default async ({ server }: { server: Server }) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const authInfo = await shareStore.getAuthInfo();
|
||||
let closed = false;
|
||||
conn.on('close', () => {
|
||||
closed = true;
|
||||
sessions.delete(conn);
|
||||
sockService.removeClient(conn);
|
||||
if (sessions.size === 0 && sessionTimer) {
|
||||
clearInterval(sessionTimer);
|
||||
sessionTimer = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
let authInfo;
|
||||
try {
|
||||
authInfo = await shareStore.getAuthInfo();
|
||||
} catch {
|
||||
if (!closed) conn.close('401');
|
||||
return;
|
||||
}
|
||||
if (closed) return;
|
||||
const platform = getPlatform(conn.headers['user-agent'] || '') || 'desktop';
|
||||
const headerToken = conn.url.replace(`${conn.pathname}?token=`, '');
|
||||
|
||||
if (isValidToken(authInfo, headerToken, platform, config.jwt.secret)) {
|
||||
sockService.addClient(conn);
|
||||
const checkSession = setInterval(async () => {
|
||||
try {
|
||||
const current = await shareStore.getAuthInfo();
|
||||
if (
|
||||
!isValidToken(current, headerToken, platform, config.jwt.secret)
|
||||
) {
|
||||
conn.close('401');
|
||||
}
|
||||
} catch {
|
||||
conn.close('401');
|
||||
}
|
||||
}, 1000);
|
||||
checkSession.unref();
|
||||
sessions.set(conn, { token: headerToken, platform });
|
||||
if (!sessionTimer) {
|
||||
sessionTimer = setInterval(checkSessions, 1000);
|
||||
sessionTimer.unref();
|
||||
}
|
||||
|
||||
conn.on('data', (message) => {
|
||||
conn.write(message);
|
||||
});
|
||||
|
||||
conn.on('close', function () {
|
||||
clearInterval(checkSession);
|
||||
sockService.removeClient(conn);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user