perf: 优化缓存下载

This commit is contained in:
xion 2024-10-11 22:10:30 +08:00
parent 79287d7de9
commit 973b76a5f0
6 changed files with 75 additions and 47 deletions

View File

@ -2,7 +2,7 @@
port: 3005, port: 3005,
api: { api: {
host: 'localhost:4000', // 后台代理 host: 'localhost:4000', // 后台代理
testHost: 'localhost:4000', path: '/api/router',
}, },
allowedOrigins: ['localhost', 'xiongxiao.me', 'zxj.im'], allowedOrigins: ['localhost', 'xiongxiao.me', 'zxj.im'],
domain: 'kevisual.xiongxiao.me', domain: 'kevisual.xiongxiao.me',

View File

@ -19,13 +19,13 @@
"@rollup/plugin-json": "^6.1.0", "@rollup/plugin-json": "^6.1.0",
"@rollup/plugin-node-resolve": "^15.3.0", "@rollup/plugin-node-resolve": "^15.3.0",
"@rollup/plugin-typescript": "^12.1.0", "@rollup/plugin-typescript": "^12.1.0",
"@types/node": "^22.7.4", "@types/node": "^22.7.5",
"cross-env": "^7.0.3", "cross-env": "^7.0.3",
"nodemon": "^3.1.7", "nodemon": "^3.1.7",
"rollup": "^4.24.0", "rollup": "^4.24.0",
"ts-lib": "^0.0.5", "ts-lib": "^0.0.5",
"tslib": "^2.7.0", "tslib": "^2.7.0",
"typescript": "^5.6.2" "typescript": "^5.6.3"
}, },
"dependencies": { "dependencies": {
"@abearxiong/use-config": "^0.0.2", "@abearxiong/use-config": "^0.0.2",

38
pnpm-lock.yaml generated
View File

@ -16,7 +16,7 @@ importers:
version: 0.0.2 version: 0.0.2
'@abearxiong/use-file-store': '@abearxiong/use-file-store':
specifier: ^0.0.1 specifier: ^0.0.1
version: 0.0.1(typescript@5.6.2) version: 0.0.1(typescript@5.6.3)
ioredis: ioredis:
specifier: ^5.4.1 specifier: ^5.4.1
version: 5.4.1 version: 5.4.1
@ -35,10 +35,10 @@ importers:
version: 15.3.0(rollup@4.24.0) version: 15.3.0(rollup@4.24.0)
'@rollup/plugin-typescript': '@rollup/plugin-typescript':
specifier: ^12.1.0 specifier: ^12.1.0
version: 12.1.0(rollup@4.24.0)(tslib@2.7.0)(typescript@5.6.2) version: 12.1.0(rollup@4.24.0)(tslib@2.7.0)(typescript@5.6.3)
'@types/node': '@types/node':
specifier: ^22.7.4 specifier: ^22.7.5
version: 22.7.4 version: 22.7.5
cross-env: cross-env:
specifier: ^7.0.3 specifier: ^7.0.3
version: 7.0.3 version: 7.0.3
@ -55,8 +55,8 @@ importers:
specifier: ^2.7.0 specifier: ^2.7.0
version: 2.7.0 version: 2.7.0
typescript: typescript:
specifier: ^5.6.2 specifier: ^5.6.3
version: 5.6.2 version: 5.6.3
packages: packages:
@ -234,8 +234,8 @@ packages:
'@types/json-schema@7.0.15': '@types/json-schema@7.0.15':
resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
'@types/node@22.7.4': '@types/node@22.7.5':
resolution: {integrity: sha512-y+NPi1rFzDs1NdQHHToqeiX2TIS79SWEAw9GYhkkx8bD0ChpfqC+n2j5OXOCpzfojBEBt6DnEnnG9MY0zk1XLg==} resolution: {integrity: sha512-jML7s2NAzMWc//QSJ1a3prpk78cOPchGvXJsC3C6R6PSMoooztvRVQEz89gmBTBY1SPMaqo5teB4uNHPdetShQ==}
'@types/resolve@1.20.2': '@types/resolve@1.20.2':
resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==} resolution: {integrity: sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q==}
@ -738,8 +738,8 @@ packages:
tslib@2.7.0: tslib@2.7.0:
resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==}
typescript@5.6.2: typescript@5.6.3:
resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==}
engines: {node: '>=14.17'} engines: {node: '>=14.17'}
hasBin: true hasBin: true
@ -785,10 +785,10 @@ snapshots:
'@abearxiong/use-config@0.0.2': {} '@abearxiong/use-config@0.0.2': {}
'@abearxiong/use-file-store@0.0.1(typescript@5.6.2)': '@abearxiong/use-file-store@0.0.1(typescript@5.6.3)':
dependencies: dependencies:
json5: 2.2.3 json5: 2.2.3
ts-loader: 9.5.1(typescript@5.6.2)(webpack@5.95.0) ts-loader: 9.5.1(typescript@5.6.3)(webpack@5.95.0)
webpack: 5.95.0 webpack: 5.95.0
transitivePeerDependencies: transitivePeerDependencies:
- '@swc/core' - '@swc/core'
@ -849,11 +849,11 @@ snapshots:
optionalDependencies: optionalDependencies:
rollup: 4.24.0 rollup: 4.24.0
'@rollup/plugin-typescript@12.1.0(rollup@4.24.0)(tslib@2.7.0)(typescript@5.6.2)': '@rollup/plugin-typescript@12.1.0(rollup@4.24.0)(tslib@2.7.0)(typescript@5.6.3)':
dependencies: dependencies:
'@rollup/pluginutils': 5.1.2(rollup@4.24.0) '@rollup/pluginutils': 5.1.2(rollup@4.24.0)
resolve: 1.22.8 resolve: 1.22.8
typescript: 5.6.2 typescript: 5.6.3
optionalDependencies: optionalDependencies:
rollup: 4.24.0 rollup: 4.24.0
tslib: 2.7.0 tslib: 2.7.0
@ -918,7 +918,7 @@ snapshots:
'@types/json-schema@7.0.15': {} '@types/json-schema@7.0.15': {}
'@types/node@22.7.4': '@types/node@22.7.5':
dependencies: dependencies:
undici-types: 6.19.8 undici-types: 6.19.8
@ -1210,7 +1210,7 @@ snapshots:
jest-worker@27.5.1: jest-worker@27.5.1:
dependencies: dependencies:
'@types/node': 22.7.4 '@types/node': 22.7.5
merge-stream: 2.0.0 merge-stream: 2.0.0
supports-color: 8.1.1 supports-color: 8.1.1
@ -1399,19 +1399,19 @@ snapshots:
ts-lib@0.0.5: {} ts-lib@0.0.5: {}
ts-loader@9.5.1(typescript@5.6.2)(webpack@5.95.0): ts-loader@9.5.1(typescript@5.6.3)(webpack@5.95.0):
dependencies: dependencies:
chalk: 4.1.2 chalk: 4.1.2
enhanced-resolve: 5.17.1 enhanced-resolve: 5.17.1
micromatch: 4.0.8 micromatch: 4.0.8
semver: 7.6.3 semver: 7.6.3
source-map: 0.7.4 source-map: 0.7.4
typescript: 5.6.2 typescript: 5.6.3
webpack: 5.95.0 webpack: 5.95.0
tslib@2.7.0: {} tslib@2.7.0: {}
typescript@5.6.2: {} typescript@5.6.3: {}
undefsafe@2.0.5: {} undefsafe@2.0.5: {}

View File

@ -9,9 +9,9 @@ import { pipeline } from 'stream';
import { promisify } from 'util'; import { promisify } from 'util';
const pipelineAsync = promisify(pipeline); const pipelineAsync = promisify(pipeline);
const { resources, api } = useConfig<{ resources: string; api: { host: string; testHost: string } }>(); const { resources, api } = useConfig<{ resources: string; api: { host: string; path: string } }>();
const fileStore = useFileStore('upload'); const fileStore = useFileStore('upload');
const status: { [key: string]: boolean } = {};
const demoData = { const demoData = {
user: 'root', user: 'root',
key: 'codeflow', key: 'codeflow',
@ -40,6 +40,7 @@ type UserAppOptions = {
user: string; user: string;
app: string; app: string;
}; };
export class UserApp { export class UserApp {
user: string; user: string;
app: string; app: string;
@ -82,10 +83,8 @@ export class UserApp {
} }
// 获取域名对应的用户和应用 // 获取域名对应的用户和应用
const isDev = process.env.NODE_ENV === 'development'; const fetchUrl = 'http://' + api.host + api.path;
const fetchTestUrl = 'http://' + api.testHost + '/api/router'; const fetchRes = await fetch(fetchUrl, {
const fetchUrl = 'http://' + api.host + '/api/router';
const fetchRes = await fetch(isDev ? fetchTestUrl : fetchUrl, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@ -114,14 +113,26 @@ export class UserApp {
redis.set(key, data.user + ':' + data.app, 'EX', 60 * 60 * 24 * 7); // 24小时 redis.set(key, data.user + ':' + data.app, 'EX', 60 * 60 * 24 * 7); // 24小时
return data; return data;
} }
async setLoaded() {
const app = this.app;
const user = this.user;
const key = 'user:app:' + app + ':' + user;
if (status[key]) {
status[key] = false;
}
}
async setCacheData() { async setCacheData() {
const app = this.app; const app = this.app;
const user = this.user; const user = this.user;
const key = 'user:app:' + app + ':' + user; const key = 'user:app:' + app + ':' + user;
const isDev = process.env.NODE_ENV === 'development'; const fetchUrl = 'http://' + api.host + api.path;
const fetchTestUrl = 'http://' + api.testHost + '/api/router'; if (status[key]) {
const fetchUrl = 'http://' + api.host + '/api/router'; return {
const fetchRes = await fetch(isDev ? fetchTestUrl : fetchUrl, { loading: true,
};
}
status[key] = true;
const fetchRes = await fetch(fetchUrl, {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
@ -137,7 +148,8 @@ export class UserApp {
}).then((res) => res.json()); }).then((res) => res.json());
if (fetchRes?.code !== 200) { if (fetchRes?.code !== 200) {
console.log('fetchRes is error', fetchRes); console.log('fetchRes is error', fetchRes);
return false; this.setLoaded();
return { code: 500, message: 'fetchRes is error' };
} }
const fetchData = fetchRes.data; const fetchData = fetchRes.data;
if (!fetchData.type) { if (!fetchData.type) {
@ -147,12 +159,17 @@ export class UserApp {
} }
if (fetchData.status !== 'running') { if (fetchData.status !== 'running') {
console.error('fetchData status is not running', fetchData.user, fetchData.key); console.error('fetchData status is not running', fetchData.user, fetchData.key);
return null; this.setLoaded();
return {
code: 500,
message: 'fetchData status is not running',
};
} }
const value = await downloadUserAppFiles(user, app, fetchData); const value = await downloadUserAppFiles(user, app, fetchData);
if (value.data.files.length === 0) { if (value.data.files.length === 0) {
console.error('root files length is zero', user, app); console.error('root files length is zero', user, app);
return false; this.setLoaded();
return { code: 404 };
} }
let valueIndexHtml = value.data.files.find((file) => file.name === 'index.html'); let valueIndexHtml = value.data.files.find((file) => file.name === 'index.html');
if (!valueIndexHtml) { if (!valueIndexHtml) {
@ -172,8 +189,9 @@ export class UserApp {
data[file.name] = file.path; data[file.name] = file.path;
}); });
await redis.hset('user:app:set:' + app + ':' + user, data); await redis.hset('user:app:set:' + app + ':' + user, data);
this.setLoaded();
return true; return { code: 200, data: valueIndexHtml.path };
} }
async getAllCacheData() { async getAllCacheData() {
const app = this.app; const app = this.app;

View File

@ -7,6 +7,7 @@ import fs from 'fs';
import { useConfig } from '@abearxiong/use-config'; import { useConfig } from '@abearxiong/use-config';
import { redis } from './redis/redis.ts'; import { redis } from './redis/redis.ts';
import { getContentType } from './get-content-type.ts'; import { getContentType } from './get-content-type.ts';
import { sleep } from '@/utils/sleep.ts';
const { api, domain, allowedOrigins } = useConfig<{ const { api, domain, allowedOrigins } = useConfig<{
api: { api: {
host: string; host: string;
@ -128,30 +129,38 @@ export const handleRequest = async (req: http.IncomingMessage, res: http.ServerR
let isExist = await userApp.getExist(); let isExist = await userApp.getExist();
if (!isExist) { if (!isExist) {
try { try {
const hasApp = await userApp.setCacheData(); const { code, loading, data } = await userApp.setCacheData();
if (!hasApp) { if (loading) {
res.writeHead(200, { 'Content-Type': 'text/html' });
res.write('Loading App\n');
res.end();
return;
}
if (code !== 200) {
res.writeHead(404, { 'Content-Type': 'text/html' }); res.writeHead(404, { 'Content-Type': 'text/html' });
res.write('Not Found App\n'); res.write('Not Found App\n');
res.end(); res.end();
return; return;
} }
await sleep(1000);
isExist = data; // 设置缓存后再次获取
if (!isExist) {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.write('Not Found App Index Page\n');
res.end();
return;
}
} catch (error) { } catch (error) {
console.error('setCacheData error', error); console.error('setCacheData error', error);
res.writeHead(500, { 'Content-Type': 'text/html' }); res.writeHead(500, { 'Content-Type': 'text/html' });
res.write('Server Error\n'); res.write('Server Error\n');
res.end(); res.end();
return; userApp.setLoaded();
}
isExist = await userApp.getExist();
if (!isExist) {
res.writeHead(404, { 'Content-Type': 'text/html' });
res.write('Not Found App Index Page\n');
res.end();
return; return;
} }
} }
const indexFile = isExist; const indexFile = isExist; // 已经必定存在了
let appFileUrl: string; let appFileUrl: string;
if (domainApp) { if (domainApp) {
appFileUrl = (url + '').replace(`/`, ''); appFileUrl = (url + '').replace(`/`, '');

1
src/utils/sleep.ts Normal file
View File

@ -0,0 +1 @@
export const sleep = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));