UNPKG

multi-lane-manager

Version:

Nacos 泳道管理与请求路由组件

1 lines 33.4 kB
{"version":3,"sources":["../src/runtime/server-utils.ts"],"sourcesContent":["import type { H3Event } from \"h3\";\nimport type { ServiceInstanceInfo } from \"../types\";\nimport { getConfig, getGlobalState, updateConfigPort } from \"../utils/config\";\nimport {\n DEFAULT_PORT,\n DEFAULT_LANE_TARGET_HEADER,\n HEADER_LANE_DEBUG,\n HEADER_LANE_DETAIL,\n getSafeHeaderValue\n} from \"../utils/defaults\";\nimport { logger } from \"../utils/logger\";\nimport { deregisterServiceInstance, getNacosLaneInstances, registerServiceInstance, startHeartbeat, stopHeartbeat } from \"../utils/nacos\";\nimport { LoadBalanceStrategy, proxyRequestWithFailover, selectInstance } from \"../utils/proxy\";\n\n/**\n * 从cookie字符串中解析指定key的值\n *\n * @param cookieHeader cookie字符串,格式如 \"key1=value1; key2=value2\"\n * @param key 要查找的cookie键名\n * @returns 找到的cookie值,如果未找到则返回undefined\n */\nfunction parseCookieValue(cookieHeader: string, key: string): string | undefined {\n console.log(`[multi-lane-manager] 解析Cookie: cookieHeader=${cookieHeader}, key=${key}`);\n\n if (!cookieHeader || !key) {\n console.log(`[multi-lane-manager] Cookie解析失败: cookieHeader或key为空`);\n return undefined;\n }\n\n // 将cookie字符串分割成键值对数组\n const cookies = cookieHeader.split(';');\n console.log(`[multi-lane-manager] Cookie分割结果: ${JSON.stringify(cookies)}`);\n\n // 查找匹配的cookie\n for (const cookie of cookies) {\n console.log(`[multi-lane-manager] 处理Cookie片段: ${cookie}`);\n\n const parts = cookie.split('=');\n if (parts.length < 2) {\n console.log(`[multi-lane-manager] 无效的Cookie格式: ${cookie}`);\n continue;\n }\n\n const cookieKey = parts[0].trim();\n const cookieValue = parts.slice(1).join('=').trim(); // 处理值中可能包含等号的情况\n\n console.log(`[multi-lane-manager] Cookie键值对: key=${cookieKey}, value=${cookieValue}`);\n\n // 不区分大小写比较cookie键名\n if (cookieKey.toLowerCase() === key.toLowerCase()) {\n console.log(`[multi-lane-manager] 找到匹配的Cookie: ${cookieKey}=${cookieValue}`);\n return cookieValue;\n }\n }\n\n console.log(`[multi-lane-manager] 未找到匹配的Cookie: ${key}`);\n return undefined;\n}\n\n/**\n * 从环境变量或 Nitro 配置中获取服务器端口\n * 优先使用环境变量中的端口配置\n *\n * @returns 服务器端口\n */\nexport function getServerPort(): number {\n // 优先使用环境变量中的端口\n const port = parseInt(process.env.NITRO_PORT || process.env.PORT || String(DEFAULT_PORT), 10);\n logger.debug(`🔍 从环境变量获取服务器端口: ${port}`);\n return port;\n}\n\n/**\n * 自动检测和注册服务实例\n * 这个函数主要负责服务注册和设置进程退出处理程序\n *\n * @returns 是否尝试了注册\n */\nexport function detectPortAndRegisterOnFirstRequest(): boolean {\n logger.info(`====== 🚀 开始端口检测和服务注册流程 ======`);\n\n // 获取配置\n logger.debug(`📋 正在加载配置...`);\n const config = getConfig();\n\n try {\n // 检查泳道功能是否启用\n logger.debug(`🔍 检查泳道功能是否启用: ${config.isLaneEnabled}`);\n if (!config.isLaneEnabled) {\n logger.debug(`🚫 泳道功能未启用,跳过初始注册`);\n return false;\n }\n\n // 获取服务器端口\n const serverPort = getServerPort();\n logger.info(`🔌 检测到服务器端口: ${serverPort}`);\n\n // 检查全局注册状态\n const globalState = getGlobalState();\n\n if (!globalState._laneMgrRegistered) {\n logger.info(`🆕 首次检测到端口 ${serverPort},准备注册服务...`);\n\n // 设置全局注册状态\n globalState._laneMgrRegistered = true;\n globalState._laneMgrPort = serverPort;\n\n // 更新配置中的端口\n updateConfigPort(serverPort);\n\n // 注册服务实例\n registerServiceInstance(serverPort)\n .then((success) => {\n if (success) {\n logger.info(`✅ 服务注册成功,设置进程退出处理程序`);\n\n // 创建优雅退出处理函数\n const gracefulShutdown = async () => {\n logger.info(`🛑 应用退出/信号,正在注销服务,端口=${serverPort}...`);\n await deregisterServiceInstance(serverPort);\n };\n\n // 注册进程退出事件处理程序\n process.on(\"beforeExit\", gracefulShutdown);\n process.on(\"SIGINT\", async () => {\n logger.info(\"🔴 收到 SIGINT 信号,执行注销...\");\n await gracefulShutdown();\n process.exit(0);\n });\n process.on(\"SIGTERM\", async () => {\n logger.info(\"🔴 收到 SIGTERM 信号,执行注销...\");\n await gracefulShutdown();\n process.exit(0);\n });\n } else {\n logger.warn(`⚠️ 服务注册失败,不设置退出处理程序`);\n }\n })\n .catch((err) => {\n logger.error(`❌ 注册过程中出错: ${err.message}`, err.stack);\n });\n\n logger.info(`====== ✅ 端口检测和服务注册流程完成,返回 true ======`);\n return true;\n } else {\n logger.info(`ℹ️ 服务已注册 (端口: ${globalState._laneMgrPort}),跳过初始注册`);\n }\n\n logger.info(`====== ✅ 端口检测和服务注册流程完成,返回 false ======`);\n return false;\n } catch (error) {\n logger.error(\n `❌ 端口检测或初始注册错误: ${error instanceof Error ? error.message : String(error)}`,\n error instanceof Error ? error.stack : \"\",\n );\n return false;\n }\n}\n\n/**\n * 创建服务器中间件以处理跨泳道代理\n * 这个中间件负责检测请求的目标泳道,并在必要时将请求转发到目标泳道的实例\n * 注意:服务注册逻辑已移至 Nitro 插件\n *\n * @returns 中间件处理函数\n */\nexport function createServerMiddleware() {\n const config = getConfig();\n\n // 使用 console.log 确保日志一定会输出\n console.log(`[multi-lane-manager] 🔧 服务器中间件初始化: 启用状态=${config.isLaneEnabled}, 当前泳道ID=${config.currentLaneId}, 服务名=${config.serviceName}, 目标泳道Header=${config.targetLaneHeaderKey || DEFAULT_LANE_TARGET_HEADER.toLowerCase()}, Cookie检测=${config.isLaneCookieEnabled ? '启用' : '禁用'}`);\n\n logger.info(\n `🔧 服务器中间件初始化: 启用状态=${config.isLaneEnabled}, 当前泳道ID=${config.currentLaneId}, 服务名=${config.serviceName}, 目标泳道Header=${config.targetLaneHeaderKey || DEFAULT_LANE_TARGET_HEADER.toLowerCase()}, Cookie检测=${config.isLaneCookieEnabled ? '启用' : '禁用'}`,\n );\n\n return async (event: H3Event) => {\n // 检查是否启用了调试模式\n const debugHeaderKey = HEADER_LANE_DEBUG.toLowerCase();\n const debugHeaderValue = event.node.req.headers[debugHeaderKey];\n const isDebugMode = !!debugHeaderValue;\n\n // 记录调试模式检测过程\n console.log(`[multi-lane-manager] 调试模式检测: headerKey=${HEADER_LANE_DEBUG}, headerKeyLower=${debugHeaderKey}, value=${debugHeaderValue}, isDebugMode=${isDebugMode}`);\n const debugInfo: string[] = [];\n\n // 如果启用了调试模式,收集调试信息\n if (isDebugMode) {\n debugInfo.push(`请求时间: ${new Date().toISOString()}`);\n debugInfo.push(`请求路径: ${event.node.req.method} ${event.node.req.url || \"\"}`);\n debugInfo.push(`当前泳道: ${config.currentLaneId}`);\n debugInfo.push(`服务名称: ${config.serviceName}`);\n }\n\n // 检查请求是否已经被处理过\n if (event.context._laneManagerHandled) {\n if (isDebugMode) debugInfo.push(`状态: 请求已被泳道管理器处理,跳过`);\n logger.debug(`⏭️ 请求已被泳道管理器处理,跳过`);\n return;\n }\n\n const requestPath = event.node.req.url || \"\";\n\n // 特别记录 /_nuxt/ 路径的请求\n if (event.path.startsWith('/_nuxt/')) {\n console.log(`[multi-lane-manager] Intercepting _nuxt request2: ${event.path}`);\n logger.info(`🔍 拦截到静态资源请求: ${event.path}`);\n }\n\n // 特别记录 /api/ 路径的请求\n if (event.path.startsWith('/api/')) {\n console.log(`[multi-lane-manager] Intercepting API request: ${event.path}`);\n logger.info(`🔍 拦截到API请求: ${event.path}, 方法: ${event.node.req.method}, 头部: ${JSON.stringify(event.node.req.headers)}`);\n }\n\n // 强制使用 console.log 确保日志一定会输出\n console.log(`[multi-lane-manager] 📥 中间件接收请求: ${event.node.req.method} ${requestPath}`);\n logger.debug(`📥 中间件接收请求: ${event.node.req.method} ${requestPath}`);\n\n if (!config.isLaneEnabled) {\n if (isDebugMode) debugInfo.push(`状态: 泳道功能未启用,跳过代理逻辑`);\n logger.debug(`🚫 泳道功能未启用,跳过代理逻辑`);\n\n // 如果启用了调试模式,添加调试响应头\n if (isDebugMode && !event.node.res.headersSent) {\n try {\n event.node.res.setHeader(HEADER_LANE_DETAIL, getSafeHeaderValue(debugInfo));\n } catch (error) {\n logger.error(`❌ 设置调试响应头时出错: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n return;\n }\n\n // 标记请求正在被处理\n event.context._laneManagerProcessing = true;\n\n // 获取目标泳道ID\n const headerKey = config.targetLaneHeaderKey || DEFAULT_LANE_TARGET_HEADER;\n\n // 记录请求头信息,帮助调试\n console.log(`[multi-lane-manager] 请求头信息:`, event.node.req.headers);\n\n // HTTP 请求头是不区分大小写的,在 Node.js 中会被自动转换为小写\n const headerKeyLower = headerKey.toLowerCase();\n\n // 1. 首先尝试从header中获取泳道ID\n const requestTargetLaneIdHeaderValue = event.node.req.headers[headerKeyLower];\n let requestTargetLaneId = Array.isArray(requestTargetLaneIdHeaderValue)\n ? requestTargetLaneIdHeaderValue[0]?.trim()\n : requestTargetLaneIdHeaderValue?.trim();\n\n // 记录泳道ID获取过程\n console.log(`[multi-lane-manager] 尝试从请求头获取泳道ID: headerKey=${headerKey}, headerKeyLower=${headerKeyLower}, value=${requestTargetLaneIdHeaderValue}`);\n\n if (isDebugMode) {\n debugInfo.push(`目标泳道Header键: ${headerKey} (实际查找: ${headerKeyLower})`);\n }\n\n // 2. 如果header中没有泳道ID,且启用了cookie功能,则尝试从cookie中获取\n if (!requestTargetLaneId && config.isLaneCookieEnabled) {\n const cookieHeader = event.node.req.headers.cookie;\n if (cookieHeader) {\n // 记录 Cookie 信息,帮助调试\n console.log(`[multi-lane-manager] Cookie信息: ${cookieHeader}`);\n\n // 从cookie字符串中解析出目标泳道ID\n // 使用与header完全相同的键名,不再移除'x-'前缀\n const cookieKey = headerKey;\n const cookieValue = parseCookieValue(cookieHeader, cookieKey);\n\n // 记录 Cookie 解析过程\n console.log(`[multi-lane-manager] 尝试从Cookie获取泳道ID: cookieKey=${cookieKey}, value=${cookieValue}`);\n\n if (cookieValue) {\n requestTargetLaneId = cookieValue.trim();\n console.log(`[multi-lane-manager] 🍪 从Cookie中获取到泳道ID: ${requestTargetLaneId}, Cookie键名: ${cookieKey}`);\n logger.debug(`🍪 从Cookie中获取到泳道ID: ${requestTargetLaneId}, Cookie键名: ${cookieKey}`);\n if (isDebugMode) debugInfo.push(`泳道ID来源: Cookie, 值: ${requestTargetLaneId}`);\n } else if (isDebugMode) {\n debugInfo.push(`Cookie中未找到泳道ID, Cookie键: ${cookieKey}`);\n }\n } else if (isDebugMode) {\n debugInfo.push(`请求中没有Cookie`);\n }\n } else if (requestTargetLaneId) {\n console.log(`[multi-lane-manager] 🔤 从Header中获取到泳道ID: ${requestTargetLaneId}`);\n logger.debug(`🔤 从Header中获取到泳道ID: ${requestTargetLaneId}`);\n if (isDebugMode) debugInfo.push(`泳道ID来源: Header, 值: ${requestTargetLaneId}`);\n } else if (isDebugMode) {\n debugInfo.push(`未找到泳道ID`);\n }\n\n // 处理请求的泳道路由逻辑\n if (requestTargetLaneId && requestTargetLaneId !== config.currentLaneId) {\n // 1. 如果请求指定了目标泳道,且与当前泳道不同,则进行代理\n // 确定泳道ID的来源\n const sourceType = event.node.req.headers[headerKey] ? 'Header' : 'Cookie';\n\n // 使用 console.log 确保日志一定会输出\n console.log(`[multi-lane-manager] 🔀 检测到跨泳道请求: 当前泳道=${config.currentLaneId}, 目标泳道=${requestTargetLaneId} (来源: ${sourceType}), 路径: ${event.path}`);\n logger.info(`🔀 检测到跨泳道请求: 当前泳道=${config.currentLaneId}, 目标泳道=${requestTargetLaneId} (来源: ${sourceType}), 路径: ${event.path}`);\n\n if (isDebugMode) {\n debugInfo.push(`处理: 跨泳道请求`);\n debugInfo.push(`目标泳道: ${requestTargetLaneId}`);\n }\n } else if (!requestTargetLaneId) {\n // 2. 如果请求没有指定泳道ID,尝试路由到 baseline 泳道\n logger.debug(`🔍 请求未指定泳道ID,尝试查找 baseline 泳道实例`);\n\n if (isDebugMode) {\n debugInfo.push(`处理: 未指定泳道ID,尝试路由到 baseline 泳道`);\n }\n\n // 如果当前已经是 baseline 泳道,直接在本地处理\n if (config.currentLaneId === 'baseline') {\n logger.debug(`✅ 当前已是 baseline 泳道,在本地处理请求`);\n\n if (isDebugMode) {\n debugInfo.push(`状态: 当前已是 baseline 泳道,在本地处理`);\n\n // 如果启用了调试模式,添加调试响应头\n if (!event.node.res.headersSent) {\n try {\n event.node.res.setHeader(HEADER_LANE_DETAIL, getSafeHeaderValue(debugInfo));\n } catch (error) {\n logger.error(`❌ 设置调试响应头时出错: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n }\n\n // 清除处理中标记,但不标记为已处理,因为请求将由后续中间件处理\n event.context._laneManagerProcessing = false;\n return;\n }\n\n // 设置目标泳道ID为 baseline\n requestTargetLaneId = 'baseline';\n\n if (isDebugMode) {\n debugInfo.push(`目标泳道: ${requestTargetLaneId} (默认)`);\n }\n\n }\n\n // 如果有目标泳道ID(可能是请求中指定的,也可能是默认的 baseline),且与当前泳道不同,则尝试进行代理\n if (requestTargetLaneId && requestTargetLaneId !== config.currentLaneId) {\n try {\n // 获取目标泳道的服务实例(按心跳时间排序)\n const targetInstances = await getNacosLaneInstances(config.serviceName, requestTargetLaneId, true);\n\n if (isDebugMode) {\n debugInfo.push(`找到实例数量: ${targetInstances.length}`);\n }\n\n if (targetInstances.length > 0) {\n // 使用负载均衡策略选择一个实例\n const targetInstance = selectInstance(\n targetInstances,\n config.serviceName,\n LoadBalanceStrategy.ROUND_ROBIN\n );\n\n if (isDebugMode) {\n debugInfo.push(`选择实例: ${targetInstance.ip}:${targetInstance.port}`);\n debugInfo.push(`负载均衡策略: ROUND_ROBIN`);\n\n // 添加调试响应头\n if (!event.node.res.headersSent) {\n try {\n event.node.res.setHeader(HEADER_LANE_DETAIL, getSafeHeaderValue(debugInfo));\n } catch (error) {\n logger.error(`❌ 设置调试响应头时出错: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n }\n\n // 转发请求到目标实例(使用故障转移)\n await proxyRequestWithFailover(event, targetInstance, isDebugMode, targetInstances);\n return;\n } else {\n // 如果是尝试路由到 baseline 但没找到实例,则在当前实例处理\n if (requestTargetLaneId === 'baseline' && !event.node.req.headers[headerKey]) {\n logger.info(`🔄 未找到 baseline 泳道实例,在当前泳道 ${config.currentLaneId} 处理请求`);\n\n if (isDebugMode) {\n debugInfo.push(`状态: 未找到 baseline 泳道实例,在当前泳道处理`);\n\n // 如果启用了调试模式,添加调试响应头\n if (!event.node.res.headersSent) {\n try {\n event.node.res.setHeader(HEADER_LANE_DETAIL, getSafeHeaderValue(debugInfo));\n } catch (error) {\n logger.error(`❌ 设置调试响应头时出错: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n }\n\n // 清除处理中标记,但不标记为已处理,因为请求将由后续中间件处理\n event.context._laneManagerProcessing = false;\n return;\n }\n\n // 对于明确指定了泳道ID的请求,如果找不到实例,尝试查找 baseline 泳道\n logger.warn(`⚠️ 未找到泳道 ${requestTargetLaneId} 的健康实例,尝试查找 baseline 泳道`);\n\n if (isDebugMode) {\n debugInfo.push(`警告: 未找到泳道 ${requestTargetLaneId} 的健康实例,尝试查找 baseline 泳道`);\n }\n\n // 如果当前请求的泳道不是 baseline,则尝试查找 baseline 泳道\n if (requestTargetLaneId !== 'baseline') {\n console.log(`[multi-lane-manager] 尝试查找 baseline 泳道实例`);\n\n // 获取 baseline 泳道的服务实例(按心跳时间排序)\n const baselineInstances = await getNacosLaneInstances(config.serviceName, 'baseline', true);\n\n if (baselineInstances.length > 0) {\n console.log(`[multi-lane-manager] 找到 baseline 泳道实例,将请求转发到 baseline 泳道`);\n logger.info(`✅ 找到 baseline 泳道实例,将请求转发到 baseline 泳道`);\n\n if (isDebugMode) {\n debugInfo.push(`状态: 找到 baseline 泳道实例,将请求转发到 baseline 泳道`);\n }\n\n // 使用负载均衡策略选择一个实例\n const baselineInstance = selectInstance(\n baselineInstances,\n config.serviceName,\n LoadBalanceStrategy.ROUND_ROBIN\n );\n\n if (isDebugMode) {\n debugInfo.push(`选择实例: ${baselineInstance.ip}:${baselineInstance.port}`);\n debugInfo.push(`负载均衡策略: ROUND_ROBIN`);\n\n // 添加调试响应头\n if (!event.node.res.headersSent) {\n try {\n event.node.res.setHeader(HEADER_LANE_DETAIL, getSafeHeaderValue(debugInfo));\n } catch (error) {\n logger.error(`❌ 设置调试响应头时出错: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n }\n\n // 转发请求到 baseline 实例(使用故障转移)\n await proxyRequestWithFailover(event, baselineInstance, isDebugMode, baselineInstances);\n return;\n } else {\n console.log(`[multi-lane-manager] 未找到 baseline 泳道实例,在当前泳道处理请求`);\n logger.info(`🔄 未找到 baseline 泳道实例,在当前泳道 ${config.currentLaneId} 处理请求`);\n\n if (isDebugMode) {\n debugInfo.push(`状态: 未找到 baseline 泳道实例,在当前泳道处理请求`);\n }\n\n // 清除处理中标记,但不标记为已处理,因为请求将由后续中间件处理\n event.context._laneManagerProcessing = false;\n\n // 如果启用了调试模式,添加调试响应头\n if (isDebugMode && !event.node.res.headersSent) {\n try {\n event.node.res.setHeader(HEADER_LANE_DETAIL, getSafeHeaderValue(debugInfo));\n } catch (error) {\n logger.error(`❌ 设置调试响应头时出错: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n return;\n }\n } else {\n // 如果当前请求的泳道已经是 baseline,但仍然没有找到实例,则在当前泳道处理\n console.log(`[multi-lane-manager] 已经是 baseline 泳道但未找到实例,在当前泳道处理请求`);\n logger.info(`🔄 已经是 baseline 泳道但未找到实例,在当前泳道 ${config.currentLaneId} 处理请求`);\n\n if (isDebugMode) {\n debugInfo.push(`状态: 已经是 baseline 泳道但未找到实例,在当前泳道处理请求`);\n }\n\n // 清除处理中标记,但不标记为已处理,因为请求将由后续中间件处理\n event.context._laneManagerProcessing = false;\n\n // 如果启用了调试模式,添加调试响应头\n if (isDebugMode && !event.node.res.headersSent) {\n try {\n event.node.res.setHeader(HEADER_LANE_DETAIL, getSafeHeaderValue(debugInfo));\n } catch (error) {\n logger.error(`❌ 设置调试响应头时出错: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n return;\n }\n }\n } catch (e) {\n logger.error(`❌ 处理跨泳道请求时发生错误: ${e instanceof Error ? e.message : String(e)}`);\n\n if (isDebugMode) {\n debugInfo.push(`错误: ${e instanceof Error ? e.message : String(e)}`);\n }\n\n if (!event.node.res.headersSent) {\n // 如果启用了调试模式,添加调试响应头\n if (isDebugMode) {\n try {\n event.node.res.setHeader(HEADER_LANE_DETAIL, getSafeHeaderValue(debugInfo));\n } catch (error) {\n logger.error(`❌ 设置调试响应头时出错: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n event.node.res.statusCode = 500; // Internal Server Error\n event.node.res.setHeader(\"Content-Type\", \"text/plain\");\n event.node.res.end(\"处理泳道请求时发生内部服务器错误。\");\n }\n // 标记请求已处理\n event.context._laneManagerHandled = true;\n return;\n }\n }\n\n logger.debug(`✅ 请求在当前泳道 (${config.currentLaneId}) 处理或无目标泳道指定,将由后续处理程序处理。`);\n\n if (isDebugMode) {\n debugInfo.push(`处理: 在当前泳道处理请求`);\n\n // 如果启用了调试模式,添加调试响应头\n if (!event.node.res.headersSent) {\n try {\n event.node.res.setHeader(HEADER_LANE_DETAIL, getSafeHeaderValue(debugInfo));\n } catch (error) {\n logger.error(`❌ 设置调试响应头时出错: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n }\n\n // 清除处理中标记,但不标记为已处理,因为请求将由后续中间件处理\n event.context._laneManagerProcessing = false;\n };\n}\n\n// 导出 Nacos 相关函数,方便其他模块使用\nexport { getNacosLaneInstances };\n"],"mappings":"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","names":[]}