feat(网络请求): 增加超时和重试机制提升稳定性

为 asyncHttpsGet 添加超时参数,默认10秒
在 TuneHub 服务中实现3次重试机制,每次增加超时时间
优化错误日志记录,包含更多调试信息
This commit is contained in:
史悦
2026-01-08 16:29:07 +08:00
parent b3ac229e91
commit 2ddf2ec749
3 changed files with 43 additions and 13 deletions

View File

@@ -65,7 +65,7 @@ async function createJob(req, res) {
} }
let meta = {}; let meta = {};
const songId = request.urlJob && request.urlJob.meta.songId ? request.urlJob.meta.songId : ""; let songId = request.urlJob && request.urlJob.meta.songId ? request.urlJob.meta.songId : "";
logger.info(`[createJob] songId=${songId}`); logger.info(`[createJob] songId=${songId}`);
// 先从 TuneHub 获取歌曲信息,避免显示原始 URL // 先从 TuneHub 获取歌曲信息,避免显示原始 URL

View File

@@ -8,16 +8,21 @@ function buildApiUrl(params = {}) {
return `${BaseUrl}?${searchParams.toString()}`; return `${BaseUrl}?${searchParams.toString()}`;
} }
async function fetchJson(params = {}) { async function fetchJson(params = {}, attempt = 1) {
const url = buildApiUrl(params); const url = buildApiUrl(params);
const raw = await asyncHttpsGet(url); // 重试时增加超时时间
const timeoutMs = Math.min(30000, 5000 + attempt * 5000);
logger.info(`[TuneHub] Fetching: ${url} (attempt ${attempt}, timeout ${timeoutMs}ms)`);
const raw = await asyncHttpsGet(url, timeoutMs);
if (!raw) { if (!raw) {
logger.warn(`[TuneHub] No data received for ${url}`);
return null; return null;
} }
try { try {
return JSON.parse(raw); return JSON.parse(raw);
} catch (err) { } catch (err) {
logger.error(`TuneHub 返回非 JSON: ${url}`); logger.error(`[TuneHub] Failed to parse JSON from ${url}: ${err.message}`);
logger.error(`[TuneHub] Raw response: ${raw.substring(0, 200)}...`);
return null; return null;
} }
} }
@@ -35,15 +40,33 @@ async function getPlaylistDetail(source, playlistId) {
} }
async function getSongInfo(source, songId) { async function getSongInfo(source, songId) {
const response = await fetchJson({ // 增加重试机制,提高成功率
source, const maxRetries = 3;
id: songId, for (let attempt = 1; attempt <= maxRetries; attempt++) {
type: 'info', try {
}); logger.info(`[TuneHub] Attempt ${attempt}/${maxRetries} to get song info: ${source}:${songId}`);
if (!response || response.code !== 200 || !response.data) { const response = await fetchJson({
return false; source,
id: songId,
type: 'info',
}, attempt);
if (response && response.code === 200 && response.data) {
logger.info(`[TuneHub] Success: ${source}:${songId}`);
return response.data;
}
logger.warn(`[TuneHub] Failed attempt ${attempt}: code=${response?.code || 'no response'}, data=${!!response?.data}`);
if (attempt < maxRetries) {
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
}
} catch (error) {
logger.error(`[TuneHub] Error on attempt ${attempt}: ${error.message}`);
if (attempt < maxRetries) {
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
}
}
} }
return response.data; logger.error(`[TuneHub] All ${maxRetries} attempts failed for ${source}:${songId}`);
return false;
} }
async function searchSongs(keyword, { async function searchSongs(keyword, {

View File

@@ -1,7 +1,12 @@
const https = require('https'); const https = require('https');
function asyncHttpsGet(url) { function asyncHttpsGet(url, timeoutMs = 10000) {
return new Promise((resolve) => { return new Promise((resolve) => {
const timeout = setTimeout(() => {
console.error(`[asyncHttpsGet] Timeout after ${timeoutMs}ms: ${url}`);
resolve(null);
}, timeoutMs);
https.get(url, res => { https.get(url, res => {
let data = ''; let data = '';
@@ -10,10 +15,12 @@ function asyncHttpsGet(url) {
}); });
res.on('end', () => { res.on('end', () => {
clearTimeout(timeout);
resolve(data.toString()); resolve(data.toString());
}); });
}).on('error', err => { }).on('error', err => {
clearTimeout(timeout);
console.error(err); console.error(err);
resolve(null); resolve(null);
}); });