UNPKG

multi-lane-manager

Version:

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

1 lines 31.5 kB
{"version":3,"sources":["../src/utils/proxy.ts"],"sourcesContent":["import axios, { type Method as AxiosMethod, type AxiosRequestConfig, AxiosError } from 'axios';\nimport type { H3Event } from 'h3';\nimport { TLSSocket } from 'tls';\nimport type { ServiceInstanceInfo } from '../types';\nimport { getConfig } from './config';\nimport {\n DEFAULT_LANE_TARGET_HEADER,\n HEADER_PROXIED_BY,\n HEADER_ORIGINAL_LANE,\n HEADER_PROXIED_BY_VALUE,\n HEADER_LANE_DEBUG,\n HEADER_LANE_DETAIL,\n getSafeHeaderValue\n} from './defaults';\nimport { logger } from './logger';\nimport { getNacosLaneInstances } from './nacos';\n\n/**\n * 负载均衡策略枚举\n * 定义了可用的负载均衡算法\n */\nexport enum LoadBalanceStrategy {\n RANDOM = 'random', // 随机选择实例\n ROUND_ROBIN = 'round-robin', // 轮询选择实例\n LEAST_CONNECTIONS = 'least-connections', // 选择连接数最少的实例\n}\n\n// 记录每个服务的当前实例索引,用于轮询策略\nconst serviceIndices: Record<string, number> = {};\n\n// 记录每个实例的连接数,用于最少连接策略\nconst instanceConnections: Record<string, number> = {};\n\n// 记录故障实例,用于故障转移\nconst failedInstances: Record<string, { count: number; lastFailTime: number; blacklistUntil: number }> = {};\n\n// 故障转移配置\nconst FAILURE_CONFIG = {\n MAX_RETRY_ATTEMPTS: 2, // 最大重试次数\n FAILURE_THRESHOLD: 3, // 故障阈值(连续失败次数)\n BLACKLIST_DURATION: 30000, // 黑名单持续时间(30秒)\n HEALTH_CHECK_INTERVAL: 60000, // 健康检查间隔(60秒)\n CONNECTION_TIMEOUT: 5000, // 连接超时时间(5秒)\n RETRY_DELAY: 1000, // 重试延迟(1秒)\n};\n\n/**\n * 根据负载均衡策略选择实例\n * 从多个可用实例中选择一个用于处理请求\n *\n * @param instances 实例列表\n * @param serviceName 服务名称\n * @param strategy 负载均衡策略\n * @returns 选择的实例\n */\nexport function selectInstance(\n instances: ServiceInstanceInfo[],\n serviceName: string,\n strategy: LoadBalanceStrategy = LoadBalanceStrategy.ROUND_ROBIN\n): ServiceInstanceInfo {\n // 检查实例列表是否为空\n if (instances.length === 0) {\n throw new Error('🚫 没有可用的服务实例');\n }\n\n // 如果只有一个实例,直接返回\n if (instances.length === 1) {\n return instances[0];\n }\n\n // 根据策略选择实例\n switch (strategy) {\n case LoadBalanceStrategy.RANDOM:\n // 随机选择一个实例\n return instances[Math.floor(Math.random() * instances.length)];\n\n case LoadBalanceStrategy.ROUND_ROBIN:\n // 轮询选择实例\n if (!serviceIndices[serviceName]) {\n serviceIndices[serviceName] = 0;\n }\n\n // 计算当前索引并更新下一个索引\n const index = serviceIndices[serviceName] % instances.length;\n serviceIndices[serviceName] = (serviceIndices[serviceName] + 1) % instances.length;\n\n return instances[index];\n\n case LoadBalanceStrategy.LEAST_CONNECTIONS:\n // 选择连接数最少的实例\n let minConnections = Number.MAX_SAFE_INTEGER;\n let selectedInstance = instances[0];\n\n // 遍历所有实例,找出连接数最少的\n for (const instance of instances) {\n const instanceKey = `${instance.serviceName}:${instance.ip}:${instance.port}`;\n const connections = instanceConnections[instanceKey] || 0;\n\n if (connections < minConnections) {\n minConnections = connections;\n selectedInstance = instance;\n }\n }\n\n return selectedInstance;\n\n default:\n // 默认返回第一个实例\n return instances[0];\n }\n}\n\n/**\n * 更新实例连接计数\n * 用于跟踪每个实例的当前连接数,支持最少连接负载均衡策略\n *\n * @param instance 服务实例\n * @param increment 是否增加连接数\n */\nfunction updateInstanceConnections(instance: ServiceInstanceInfo, increment: boolean): void {\n // 创建实例的唯一键\n const instanceKey = `${instance.serviceName}:${instance.ip}:${instance.port}`;\n\n // 确保连接计数存在\n if (!instanceConnections[instanceKey]) {\n instanceConnections[instanceKey] = 0;\n }\n\n // 增加或减少连接计数\n if (increment) {\n instanceConnections[instanceKey]++;\n } else {\n instanceConnections[instanceKey] = Math.max(0, instanceConnections[instanceKey] - 1);\n }\n}\n\n/**\n * 检查实例是否在黑名单中\n * @param instance 服务实例\n * @returns 是否在黑名单中\n */\nfunction isInstanceBlacklisted(instance: ServiceInstanceInfo): boolean {\n const instanceKey = `${instance.serviceName}:${instance.ip}:${instance.port}`;\n const failureInfo = failedInstances[instanceKey];\n\n if (!failureInfo) {\n return false;\n }\n\n const now = Date.now();\n return now < failureInfo.blacklistUntil;\n}\n\n/**\n * 记录实例故障\n * @param instance 服务实例\n * @param error 错误信息\n */\nfunction recordInstanceFailure(instance: ServiceInstanceInfo, error: Error): void {\n const instanceKey = `${instance.serviceName}:${instance.ip}:${instance.port}`;\n const now = Date.now();\n\n if (!failedInstances[instanceKey]) {\n failedInstances[instanceKey] = {\n count: 0,\n lastFailTime: 0,\n blacklistUntil: 0\n };\n }\n\n const failureInfo = failedInstances[instanceKey];\n failureInfo.count++;\n failureInfo.lastFailTime = now;\n\n // 如果故障次数达到阈值,将实例加入黑名单\n if (failureInfo.count >= FAILURE_CONFIG.FAILURE_THRESHOLD) {\n failureInfo.blacklistUntil = now + FAILURE_CONFIG.BLACKLIST_DURATION;\n logger.warn(`⚠️ 实例 ${instanceKey} 故障次数达到阈值 ${FAILURE_CONFIG.FAILURE_THRESHOLD},加入黑名单 ${FAILURE_CONFIG.BLACKLIST_DURATION/1000} 秒`);\n logger.warn(`⚠️ 故障原因: ${error.message}`);\n } else {\n logger.warn(`⚠️ 实例 ${instanceKey} 故障 (${failureInfo.count}/${FAILURE_CONFIG.FAILURE_THRESHOLD}): ${error.message}`);\n }\n}\n\n/**\n * 重置实例故障计数(当实例恢复正常时调用)\n * @param instance 服务实例\n */\nfunction resetInstanceFailure(instance: ServiceInstanceInfo): void {\n const instanceKey = `${instance.serviceName}:${instance.ip}:${instance.port}`;\n if (failedInstances[instanceKey]) {\n const wasBlacklisted = isInstanceBlacklisted(instance);\n delete failedInstances[instanceKey];\n if (wasBlacklisted) {\n logger.info(`✅ 实例 ${instanceKey} 已恢复,从黑名单中移除`);\n }\n }\n}\n\n/**\n * 检查错误类型是否应该触发故障转移\n * @param error 错误对象\n * @returns 是否应该故障转移\n */\nfunction shouldFailover(error: Error): boolean {\n if (error instanceof AxiosError) {\n // 网络连接错误\n if (error.code === 'ECONNREFUSED' || error.code === 'ENOTFOUND' || error.code === 'ETIMEDOUT') {\n return true;\n }\n\n // HTTP状态码错误\n if (error.response) {\n const status = error.response.status;\n // 502, 503, 504 等服务器错误应该触发故障转移\n return status >= 502 && status <= 504;\n }\n\n // 请求超时\n if (error.code === 'ECONNABORTED') {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * 过滤健康实例(排除黑名单中的实例)\n * @param instances 实例列表\n * @returns 健康实例列表\n */\nfunction filterHealthyInstances(instances: ServiceInstanceInfo[]): ServiceInstanceInfo[] {\n return instances.filter(instance => !isInstanceBlacklisted(instance));\n}\n\n/**\n * 带故障转移的代理请求函数\n * 支持自动重试和故障转移到其他健康实例\n *\n * @param event H3 事件\n * @param targetInstance 目标服务实例\n * @param isDebugMode 是否启用调试模式\n * @param allInstances 所有可用实例(用于故障转移)\n * @returns 是否成功处理\n */\nexport async function proxyRequestWithFailover(\n event: H3Event,\n targetInstance: ServiceInstanceInfo,\n isDebugMode: boolean = false,\n allInstances: ServiceInstanceInfo[] = []\n): Promise<boolean> {\n let lastError: Error | null = null;\n\n // 首先尝试原始目标实例\n const instancesToTry = [targetInstance];\n\n // 如果有其他实例可用,添加到重试列表中\n if (allInstances.length > 1) {\n const healthyBackups = filterHealthyInstances(\n allInstances.filter(inst =>\n inst.ip !== targetInstance.ip || inst.port !== targetInstance.port\n )\n );\n instancesToTry.push(...healthyBackups.slice(0, FAILURE_CONFIG.MAX_RETRY_ATTEMPTS));\n }\n\n logger.info(`🔄 开始代理请求,可尝试实例数: ${instancesToTry.length}`);\n\n for (let attempt = 0; attempt < instancesToTry.length; attempt++) {\n const currentInstance = instancesToTry[attempt];\n const isRetry = attempt > 0;\n\n // 检查实例是否在黑名单中\n if (isInstanceBlacklisted(currentInstance)) {\n logger.warn(`⚠️ 跳过黑名单实例: ${currentInstance.ip}:${currentInstance.port}`);\n continue;\n }\n\n try {\n if (isRetry) {\n logger.info(`🔄 故障转移到实例: ${currentInstance.ip}:${currentInstance.port} (尝试 ${attempt + 1}/${instancesToTry.length})`);\n // 重试前等待一段时间\n await new Promise(resolve => setTimeout(resolve, FAILURE_CONFIG.RETRY_DELAY));\n }\n\n const success = await proxyRequestToInstance(event, currentInstance, isDebugMode, isRetry);\n\n if (success) {\n // 请求成功,重置故障计数\n resetInstanceFailure(currentInstance);\n\n if (isRetry) {\n logger.info(`✅ 故障转移成功,使用实例: ${currentInstance.ip}:${currentInstance.port}`);\n }\n\n return true;\n }\n } catch (error) {\n lastError = error instanceof Error ? error : new Error(String(error));\n\n // 记录实例故障\n recordInstanceFailure(currentInstance, lastError);\n\n // 检查是否应该继续故障转移\n if (!shouldFailover(lastError)) {\n logger.warn(`⚠️ 错误类型不适合故障转移: ${lastError.message}`);\n break;\n }\n\n logger.warn(`⚠️ 实例 ${currentInstance.ip}:${currentInstance.port} 请求失败: ${lastError.message}`);\n\n // 如果这是最后一次尝试,不再继续\n if (attempt === instancesToTry.length - 1) {\n logger.error(`❌ 所有实例都已尝试,请求最终失败`);\n break;\n }\n }\n }\n\n // 所有实例都失败了,返回最后的错误\n return handleFinalFailure(event, lastError, isDebugMode);\n}\n\n/**\n * 处理最终失败的情况\n * @param event H3 事件\n * @param error 最后的错误\n * @param isDebugMode 是否启用调试模式\n * @returns 是否成功处理\n */\nasync function handleFinalFailure(event: H3Event, error: Error | null, isDebugMode: boolean): Promise<boolean> {\n // 检查响应头是否已发送\n if (event.node.res.headersSent) {\n logger.warn(`⚠️ 响应头已发送,无法返回错误响应`);\n event.context._laneManagerHandled = true;\n return false;\n }\n\n try {\n // 如果启用了调试模式,添加调试信息\n if (isDebugMode) {\n const debugInfo: string[] = [];\n debugInfo.push(`故障转移时间: ${new Date().toISOString()}`);\n debugInfo.push(`最终错误: ${error?.message || '所有实例都不可用'}`);\n debugInfo.push(`状态: 所有实例故障转移失败`);\n\n try {\n event.node.res.setHeader(HEADER_LANE_DETAIL, getSafeHeaderValue(debugInfo));\n } catch (headerError) {\n logger.error(`❌ 设置调试响应头时出错: ${headerError instanceof Error ? headerError.message : String(headerError)}`);\n }\n }\n\n // 设置状态码和内容类型\n event.node.res.statusCode = 503; // Service Unavailable\n event.node.res.setHeader('Content-Type', 'text/plain');\n\n // 发送错误响应\n const errorMessage = `服务暂时不可用,所有实例都无法访问。请稍后重试。`;\n event.node.res.end(errorMessage);\n\n logger.error(`❌ 发送服务不可用响应`);\n } catch (responseError) {\n logger.error(`❌ 发送错误响应时出错: ${responseError instanceof Error ? responseError.message : String(responseError)}`);\n }\n\n // 标记请求已处理\n event.context._laneManagerHandled = true;\n return false;\n}\n\n/**\n * 转发请求到指定实例(原始实现)\n * 将当前请求转发到目标服务实例,并将响应返回给客户端\n *\n * @param event H3 事件\n * @param targetInstance 目标服务实例\n * @param isDebugMode 是否启用调试模式\n * @param isRetry 是否为重试请求\n * @returns 是否成功处理\n */\nasync function proxyRequestToInstance(\n event: H3Event,\n targetInstance: ServiceInstanceInfo,\n isDebugMode: boolean = false,\n isRetry: boolean = false\n): Promise<boolean> {\n const config = getConfig();\n const requestPath = event.node.req.url || '';\n const targetUrl = `http://${targetInstance.ip}:${targetInstance.port}${requestPath}`;\n\n logger.info(`🔄 代理请求到目标实例: ${targetInstance.serviceName}@${targetInstance.ip}:${targetInstance.port} (泳道: ${targetInstance.metadata.laneId}), URL: ${targetUrl}`);\n\n // 更新实例连接计数\n updateInstanceConnections(targetInstance, true);\n\n try {\n // 准备请求头\n const newHeaders: Record<string, string | string[] | undefined> = {};\n\n // 复制原始请求头,排除 host 和目标泳道头\n for (const key in event.node.req.headers) {\n if (key !== 'host' && key !== config.targetLaneHeaderKey) {\n newHeaders[key] = event.node.req.headers[key];\n }\n }\n\n // 添加转发相关的头\n const remoteAddress = event.node.req.socket?.remoteAddress;\n newHeaders['X-Forwarded-For'] = remoteAddress || event.node.req.headers['x-forwarded-for'] || '';\n newHeaders['X-Forwarded-Host'] = event.node.req.headers['host'] || '';\n\n // 确定协议\n const proto = event.node.req.headers['x-forwarded-proto'] ||\n (event.node.req.socket instanceof TLSSocket && event.node.req.socket.encrypted ? 'https' : 'http');\n newHeaders['X-Forwarded-Proto'] = proto;\n\n // 准备请求配置\n const proxyRequestConfig: AxiosRequestConfig = {\n method: event.node.req.method as AxiosMethod,\n url: targetUrl,\n headers: newHeaders,\n responseType: 'stream',\n validateStatus: () => true, // 接受任何状态码\n timeout: config.proxyTimeout,\n };\n\n // 对于包含请求体的方法,添加请求体\n const bodyMethods = ['POST', 'PUT', 'PATCH', 'DELETE'];\n if (event.node.req.method && bodyMethods.includes(event.node.req.method.toUpperCase())) {\n proxyRequestConfig.data = event.node.req;\n }\n\n logger.debug('📋 代理请求配置:', {\n ...proxyRequestConfig,\n data: proxyRequestConfig.data ? '<REQUEST_STREAM>' : undefined,\n });\n\n // 发送代理请求\n const proxyRes = await axios.request(proxyRequestConfig);\n logger.info(`📥 代理响应状态: ${proxyRes.status}`);\n\n // 处理响应\n await new Promise<void>((resolve, reject) => {\n try {\n // 1. 设置状态码\n event.node.res.statusCode = proxyRes.status;\n\n // 2. 设置响应头\n const headersToSet = new Map<string, string | string[] | number>();\n\n // 复制原始响应头\n Object.entries(proxyRes.headers).forEach(([key, value]) => {\n if (value !== undefined && value !== null) {\n headersToSet.set(key, value as string | string[] | number);\n }\n });\n\n // 添加自定义头\n headersToSet.set(HEADER_PROXIED_BY, HEADER_PROXIED_BY_VALUE);\n headersToSet.set(HEADER_ORIGINAL_LANE, config.currentLaneId);\n headersToSet.set(DEFAULT_LANE_TARGET_HEADER, targetInstance.metadata.laneId);\n\n // 如果启用了调试模式,添加调试信息\n if (isDebugMode) {\n const debugInfo: string[] = [];\n debugInfo.push(`代理时间: ${new Date().toISOString()}`);\n debugInfo.push(`代理目标: ${targetInstance.ip}:${targetInstance.port}`);\n debugInfo.push(`目标泳道: ${targetInstance.metadata.laneId}`);\n debugInfo.push(`响应状态: ${proxyRes.status}`);\n debugInfo.push(`响应大小: ${proxyRes.headers['content-length'] || '未知'}`);\n\n try {\n headersToSet.set(HEADER_LANE_DETAIL, getSafeHeaderValue(debugInfo));\n } catch (error) {\n logger.error(`❌ 设置调试响应头时出错: ${error instanceof Error ? error.message : String(error)}`);\n }\n }\n\n // 注意:根据需求,我们不设置泳道ID的cookie\n // 只保留原始响应中的cookie(如果有)\n\n // 检查响应头是否已发送\n if (event.node.res.headersSent) {\n logger.warn(`⚠️ 响应头已发送,无法设置新的响应头`);\n console.log(`[multi-lane-manager] ⚠️ 响应头已发送,无法设置新的响应头`);\n }\n\n // 设置所有头\n for (const [key, value] of headersToSet.entries()) {\n try {\n if (!event.node.res.headersSent) {\n event.node.res.setHeader(key, value);\n logger.debug(`✅ 成功设置响应头: ${key}=${value}`);\n } else {\n logger.warn(`⚠️ 无法设置响应头 ${key},响应头已发送`);\n console.log(`[multi-lane-manager] ⚠️ 无法设置响应头 ${key},响应头已发送`);\n }\n } catch (headerError) {\n logger.warn(`⚠️ 设置响应头 ${key} 失败: ${headerError instanceof Error ? headerError.message : String(headerError)}`);\n console.log(`[multi-lane-manager] ⚠️ 设置响应头 ${key} 失败: ${headerError instanceof Error ? headerError.message : String(headerError)}`);\n }\n }\n\n // 3. 设置数据流\n proxyRes.data.on('error', (err: Error) => {\n logger.error(`❌ 代理响应数据流错误: ${err.message}`);\n updateInstanceConnections(targetInstance, false);\n reject(err);\n });\n\n proxyRes.data.on('end', () => {\n logger.debug('✅ 代理响应数据流结束');\n updateInstanceConnections(targetInstance, false);\n resolve();\n });\n\n // 标记请求已处理,确保不会再次处理\n event.context._laneManagerHandled = true;\n\n // 将代理响应的数据流式传输到原始响应\n // 确保在设置所有响应头之后再开始发送响应体\n proxyRes.data.pipe(event.node.res);\n } catch (err) {\n updateInstanceConnections(targetInstance, false);\n reject(err);\n }\n });\n\n // 请求已在数据流处理中被标记为已处理\n return true;\n } catch (error) {\n // 更新实例连接计数\n updateInstanceConnections(targetInstance, false);\n\n logger.error(`❌ 代理请求到 ${targetUrl} 失败: ${error instanceof Error ? error.message : String(error)}`);\n\n // 检查响应头是否已发送\n if (event.node.res.headersSent) {\n logger.warn(`⚠️ 响应头已发送,无法返回错误响应`);\n console.log(`[multi-lane-manager] ⚠️ 响应头已发送,无法返回错误响应`);\n\n // 标记请求已处理\n event.context._laneManagerHandled = true;\n return false;\n }\n\n // 如果响应头尚未发送,返回错误响应\n try {\n // 如果启用了调试模式,添加调试信息\n if (isDebugMode) {\n const debugInfo: string[] = [];\n debugInfo.push(`代理时间: ${new Date().toISOString()}`);\n debugInfo.push(`代理目标: ${targetInstance.ip}:${targetInstance.port}`);\n debugInfo.push(`目标泳道: ${targetInstance.metadata.laneId}`);\n debugInfo.push(`错误: ${error instanceof Error ? error.message : '未知代理错误'}`);\n\n try {\n event.node.res.setHeader(HEADER_LANE_DETAIL, getSafeHeaderValue(debugInfo));\n logger.debug(`✅ 成功设置错误响应头: ${HEADER_LANE_DETAIL}`);\n } catch (headerError) {\n logger.error(`❌ 设置调试响应头时出错: ${headerError instanceof Error ? headerError.message : String(headerError)}`);\n console.log(`[multi-lane-manager] ❌ 设置调试响应头时出错: ${headerError instanceof Error ? headerError.message : String(headerError)}`);\n }\n }\n\n // 设置状态码和内容类型\n event.node.res.statusCode = 502; // Bad Gateway\n event.node.res.setHeader('Content-Type', 'text/plain');\n\n // 发送错误响应\n const errorMessage = `代理请求到泳道 ${targetInstance.metadata.laneId} 时出错。原因: ${error instanceof Error ? error.message : '未知代理错误'}`;\n event.node.res.end(errorMessage);\n\n logger.debug(`✅ 成功发送错误响应`);\n } catch (responseError) {\n logger.error(`❌ 发送错误响应时出错: ${responseError instanceof Error ? responseError.message : String(responseError)}`);\n console.log(`[multi-lane-manager] ❌ 发送错误响应时出错: ${responseError instanceof Error ? responseError.message : String(responseError)}`);\n }\n\n // 标记请求已处理\n event.context._laneManagerHandled = true;\n return false;\n }\n}\n\n/**\n * 兼容性导出:原始的代理请求函数\n * 为了保持向后兼容性,保留原始的proxyRequest函数\n *\n * @param event H3 事件\n * @param targetInstance 目标服务实例\n * @param isDebugMode 是否启用调试模式\n * @returns 是否成功处理\n */\nexport async function proxyRequest(\n event: H3Event,\n targetInstance: ServiceInstanceInfo,\n isDebugMode: boolean = false\n): Promise<boolean> {\n // 调用带故障转移的版本,但不提供其他实例(保持原始行为)\n return proxyRequestToInstance(event, targetInstance, isDebugMode, false);\n}\n"],"mappings":";;;;;;;;;;;;;;;;AAAA,OAAO,SAA8D,kBAAkB;AAEvF,SAAS,iBAAiB;AAmBnB,IAAK,sBAAL,kBAAKA,yBAAL;AACL,EAAAA,qBAAA,YAAS;AACT,EAAAA,qBAAA,iBAAc;AACd,EAAAA,qBAAA,uBAAoB;AAHV,SAAAA;AAAA,GAAA;AAOZ,IAAM,iBAAyC,CAAC;AAGhD,IAAM,sBAA8C,CAAC;AAGrD,IAAM,kBAAmG,CAAC;AAG1G,IAAM,iBAAiB;AAAA,EACrB,oBAAoB;AAAA;AAAA,EACpB,mBAAmB;AAAA;AAAA,EACnB,oBAAoB;AAAA;AAAA,EACpB,uBAAuB;AAAA;AAAA,EACvB,oBAAoB;AAAA;AAAA,EACpB,aAAa;AAAA;AACf;AAWO,SAAS,eACd,WACA,aACA,WAAgC,iCACX;AAErB,MAAI,UAAU,WAAW,GAAG;AAC1B,UAAM,IAAI,MAAM,kEAAc;AAAA,EAChC;AAGA,MAAI,UAAU,WAAW,GAAG;AAC1B,WAAO,UAAU,CAAC;AAAA,EACpB;AAGA,UAAQ,UAAU;AAAA,IAChB,KAAK;AAEH,aAAO,UAAU,KAAK,MAAM,KAAK,OAAO,IAAI,UAAU,MAAM,CAAC;AAAA,IAE/D,KAAK;AAEH,UAAI,CAAC,eAAe,WAAW,GAAG;AAChC,uBAAe,WAAW,IAAI;AAAA,MAChC;AAGA,YAAM,QAAQ,eAAe,WAAW,IAAI,UAAU;AACtD,qBAAe,WAAW,KAAK,eAAe,WAAW,IAAI,KAAK,UAAU;AAE5E,aAAO,UAAU,KAAK;AAAA,IAExB,KAAK;AAEH,UAAI,iBAAiB,OAAO;AAC5B,UAAI,mBAAmB,UAAU,CAAC;AAGlC,iBAAW,YAAY,WAAW;AAChC,cAAM,cAAc,GAAG,SAAS,WAAW,IAAI,SAAS,EAAE,IAAI,SAAS,IAAI;AAC3E,cAAM,cAAc,oBAAoB,WAAW,KAAK;AAExD,YAAI,cAAc,gBAAgB;AAChC,2BAAiB;AACjB,6BAAmB;AAAA,QACrB;AAAA,MACF;AAEA,aAAO;AAAA,IAET;AAEE,aAAO,UAAU,CAAC;AAAA,EACtB;AACF;AASA,SAAS,0BAA0B,UAA+B,WAA0B;AAE1F,QAAM,cAAc,GAAG,SAAS,WAAW,IAAI,SAAS,EAAE,IAAI,SAAS,IAAI;AAG3E,MAAI,CAAC,oBAAoB,WAAW,GAAG;AACrC,wBAAoB,WAAW,IAAI;AAAA,EACrC;AAGA,MAAI,WAAW;AACb,wBAAoB,WAAW;AAAA,EACjC,OAAO;AACL,wBAAoB,WAAW,IAAI,KAAK,IAAI,GAAG,oBAAoB,WAAW,IAAI,CAAC;AAAA,EACrF;AACF;AAOA,SAAS,sBAAsB,UAAwC;AACrE,QAAM,cAAc,GAAG,SAAS,WAAW,IAAI,SAAS,EAAE,IAAI,SAAS,IAAI;AAC3E,QAAM,cAAc,gBAAgB,WAAW;AAE/C,MAAI,CAAC,aAAa;AAChB,WAAO;AAAA,EACT;AAEA,QAAM,MAAM,KAAK,IAAI;AACrB,SAAO,MAAM,YAAY;AAC3B;AAOA,SAAS,sBAAsB,UAA+B,OAAoB;AAChF,QAAM,cAAc,GAAG,SAAS,WAAW,IAAI,SAAS,EAAE,IAAI,SAAS,IAAI;AAC3E,QAAM,MAAM,KAAK,IAAI;AAErB,MAAI,CAAC,gBAAgB,WAAW,GAAG;AACjC,oBAAgB,WAAW,IAAI;AAAA,MAC7B,OAAO;AAAA,MACP,cAAc;AAAA,MACd,gBAAgB;AAAA,IAClB;AAAA,EACF;AAEA,QAAM,cAAc,gBAAgB,WAAW;AAC/C,cAAY;AACZ,cAAY,eAAe;AAG3B,MAAI,YAAY,SAAS,eAAe,mBAAmB;AACzD,gBAAY,iBAAiB,MAAM,eAAe;AAClD,WAAO,KAAK,6BAAS,WAAW,qDAAa,eAAe,iBAAiB,wCAAU,eAAe,qBAAmB,GAAI,SAAI;AACjI,WAAO,KAAK,0CAAY,MAAM,OAAO,EAAE;AAAA,EACzC,OAAO;AACL,WAAO,KAAK,6BAAS,WAAW,kBAAQ,YAAY,KAAK,IAAI,eAAe,iBAAiB,MAAM,MAAM,OAAO,EAAE;AAAA,EACpH;AACF;AAMA,SAAS,qBAAqB,UAAqC;AACjE,QAAM,cAAc,GAAG,SAAS,WAAW,IAAI,SAAS,EAAE,IAAI,SAAS,IAAI;AAC3E,MAAI,gBAAgB,WAAW,GAAG;AAChC,UAAM,iBAAiB,sBAAsB,QAAQ;AACrD,WAAO,gBAAgB,WAAW;AAClC,QAAI,gBAAgB;AAClB,aAAO,KAAK,uBAAQ,WAAW,qEAAc;AAAA,IAC/C;AAAA,EACF;AACF;AAOA,SAAS,eAAe,OAAuB;AAC7C,MAAI,iBAAiB,YAAY;AAE/B,QAAI,MAAM,SAAS,kBAAkB,MAAM,SAAS,eAAe,MAAM,SAAS,aAAa;AAC7F,aAAO;AAAA,IACT;AAGA,QAAI,MAAM,UAAU;AAClB,YAAM,SAAS,MAAM,SAAS;AAE9B,aAAO,UAAU,OAAO,UAAU;AAAA,IACpC;AAGA,QAAI,MAAM,SAAS,gBAAgB;AACjC,aAAO;AAAA,IACT;AAAA,EACF;AAEA,SAAO;AACT;AAOA,SAAS,uBAAuB,WAAyD;AACvF,SAAO,UAAU,OAAO,cAAY,CAAC,sBAAsB,QAAQ,CAAC;AACtE;AAYA,eAAsB,yBACpB,OACA,gBACA,cAAuB,OACvB,eAAsC,CAAC,GACrB;AAClB,MAAI,YAA0B;AAG9B,QAAM,iBAAiB,CAAC,cAAc;AAGtC,MAAI,aAAa,SAAS,GAAG;AAC3B,UAAM,iBAAiB;AAAA,MACrB,aAAa;AAAA,QAAO,UAClB,KAAK,OAAO,eAAe,MAAM,KAAK,SAAS,eAAe;AAAA,MAChE;AAAA,IACF;AACA,mBAAe,KAAK,GAAG,eAAe,MAAM,GAAG,eAAe,kBAAkB,CAAC;AAAA,EACnF;AAEA,SAAO,KAAK,6FAAqB,eAAe,MAAM,EAAE;AAExD,WAAS,UAAU,GAAG,UAAU,eAAe,QAAQ,WAAW;AAChE,UAAM,kBAAkB,eAAe,OAAO;AAC9C,UAAM,UAAU,UAAU;AAG1B,QAAI,sBAAsB,eAAe,GAAG;AAC1C,aAAO,KAAK,4DAAe,gBAAgB,EAAE,IAAI,gBAAgB,IAAI,EAAE;AACvE;AAAA,IACF;AAEA,QAAI;AACF,UAAI,SAAS;AACX,eAAO,KAAK,yDAAe,gBAAgB,EAAE,IAAI,gBAAgB,IAAI,kBAAQ,UAAU,CAAC,IAAI,eAAe,MAAM,GAAG;AAEpH,cAAM,IAAI,QAAQ,aAAW,WAAW,SAAS,eAAe,WAAW,CAAC;AAAA,MAC9E;AAEA,YAAM,UAAU,MAAM,uBAAuB,OAAO,iBAAiB,aAAa,OAAO;AAEzF,UAAI,SAAS;AAEX,6BAAqB,eAAe;AAEpC,YAAI,SAAS;AACX,iBAAO,KAAK,8EAAkB,gBAAgB,EAAE,IAAI,gBAAgB,IAAI,EAAE;AAAA,QAC5E;AAEA,eAAO;AAAA,MACT;AAAA,IACF,SAAS,OAAO;AACd,kBAAY,iBAAiB,QAAQ,QAAQ,IAAI,MAAM,OAAO,KAAK,CAAC;AAGpE,4BAAsB,iBAAiB,SAAS;AAGhD,UAAI,CAAC,eAAe,SAAS,GAAG;AAC9B,eAAO,KAAK,oFAAmB,UAAU,OAAO,EAAE;AAClD;AAAA,MACF;AAEA,aAAO,KAAK,6BAAS,gBAAgB,EAAE,IAAI,gBAAgB,IAAI,8BAAU,UAAU,OAAO,EAAE;AAG5F,UAAI,YAAY,eAAe,SAAS,GAAG;AACzC,eAAO,MAAM,mGAAmB;AAChC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAGA,SAAO,mBAAmB,OAAO,WAAW,WAAW;AACzD;AASA,eAAe,mBAAmB,OAAgB,OAAqB,aAAwC;AAE7G,MAAI,MAAM,KAAK,IAAI,aAAa;AAC9B,WAAO,KAAK,yGAAoB;AAChC,UAAM,QAAQ,sBAAsB;AACpC,WAAO;AAAA,EACT;AAEA,MAAI;AAEF,QAAI,aAAa;AACf,YAAM,YAAsB,CAAC;AAC7B,gBAAU,KAAK,0CAAW,oBAAI,KAAK,GAAE,YAAY,CAAC,EAAE;AACpD,gBAAU,KAAK,6BAAS,OAAO,WAAW,kDAAU,EAAE;AACtD,gBAAU,KAAK,4EAAgB;AAE/B,UAAI;AACF,cAAM,KAAK,IAAI,UAAU,oBAAoB,mBAAmB,SAAS,CAAC;AAAA,MAC5E,SAAS,aAAa;AACpB,eAAO,MAAM,wEAAiB,uBAAuB,QAAQ,YAAY,UAAU,OAAO,WAAW,CAAC,EAAE;AAAA,MAC1G;AAAA,IACF;AAGA,UAAM,KAAK,IAAI,aAAa;AAC5B,UAAM,KAAK,IAAI,UAAU,gBAAgB,YAAY;AAGrD,UAAM,eAAe;AACrB,UAAM,KAAK,IAAI,IAAI,YAAY;AAE/B,WAAO,MAAM,+DAAa;AAAA,EAC5B,SAAS,eAAe;AACtB,WAAO,MAAM,kEAAgB,yBAAyB,QAAQ,cAAc,UAAU,OAAO,aAAa,CAAC,EAAE;AAAA,EAC/G;AAGA,QAAM,QAAQ,sBAAsB;AACpC,SAAO;AACT;AAYA,eAAe,uBACb,OACA,gBACA,cAAuB,OACvB,UAAmB,OACD;AAClB,QAAM,SAAS,UAAU;AACzB,QAAM,cAAc,MAAM,KAAK,IAAI,OAAO;AAC1C,QAAM,YAAY,UAAU,eAAe,EAAE,IAAI,eAAe,IAAI,GAAG,WAAW;AAElF,SAAO,KAAK,qEAAiB,eAAe,WAAW,IAAI,eAAe,EAAE,IAAI,eAAe,IAAI,mBAAS,eAAe,SAAS,MAAM,WAAW,SAAS,EAAE;AAGhK,4BAA0B,gBAAgB,IAAI;AAE9C,MAAI;AAEF,UAAM,aAA4D,CAAC;AAGnE,eAAW,OAAO,MAAM,KAAK,IAAI,SAAS;AACxC,UAAI,QAAQ,UAAU,QAAQ,OAAO,qBAAqB;AACxD,mBAAW,GAAG,IAAI,MAAM,KAAK,IAAI,QAAQ,GAAG;AAAA,MAC9C;AAAA,IACF;AAGA,UAAM,gBAAgB,MAAM,KAAK,IAAI,QAAQ;AAC7C,eAAW,iBAAiB,IAAI,iBAAiB,MAAM,KAAK,IAAI,QAAQ,iBAAiB,KAAK;AAC9F,eAAW,kBAAkB,IAAI,MAAM,KAAK,IAAI,QAAQ,MAAM,KAAK;AAGnE,UAAM,QAAQ,MAAM,KAAK,IAAI,QAAQ,mBAAmB,MACrD,MAAM,KAAK,IAAI,kBAAkB,aAAa,MAAM,KAAK,IAAI,OAAO,YAAY,UAAU;AAC7F,eAAW,mBAAmB,IAAI;AAGlC,UAAM,qBAAyC;AAAA,MAC7C,QAAQ,MAAM,KAAK,IAAI;AAAA,MACvB,KAAK;AAAA,MACL,SAAS;AAAA,MACT,cAAc;AAAA,MACd,gBAAgB,MAAM;AAAA;AAAA,MACtB,SAAS,OAAO;AAAA,IAClB;AAGA,UAAM,cAAc,CAAC,QAAQ,OAAO,SAAS,QAAQ;AACrD,QAAI,MAAM,KAAK,IAAI,UAAU,YAAY,SAAS,MAAM,KAAK,IAAI,OAAO,YAAY,CAAC,GAAG;AACtF,yBAAmB,OAAO,MAAM,KAAK;AAAA,IACvC;AAEA,WAAO,MAAM,mDAAc;AAAA,MACzB,GAAG;AAAA,MACH,MAAM,mBAAmB,OAAO,qBAAqB;AAAA,IACvD,CAAC;AAGD,UAAM,WAAW,MAAM,MAAM,QAAQ,kBAAkB;AACvD,WAAO,KAAK,mDAAc,SAAS,MAAM,EAAE;AAG3C,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,UAAI;AAEF,cAAM,KAAK,IAAI,aAAa,SAAS;AAGrC,cAAM,eAAe,oBAAI,IAAwC;AAGjE,eAAO,QAAQ,SAAS,OAAO,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,MAAM;AACzD,cAAI,UAAU,UAAa,UAAU,MAAM;AACzC,yBAAa,IAAI,KAAK,KAAmC;AAAA,UAC3D;AAAA,QACF,CAAC;AAGD,qBAAa,IAAI,mBAAmB,uBAAuB;AAC3D,qBAAa,IAAI,sBAAsB,OAAO,aAAa;AAC3D,qBAAa,IAAI,4BAA4B,eAAe,SAAS,MAAM;AAG3E,YAAI,aAAa;AACf,gBAAM,YAAsB,CAAC;AAC7B,oBAAU,KAAK,8BAAS,oBAAI,KAAK,GAAE,YAAY,CAAC,EAAE;AAClD,oBAAU,KAAK,6BAAS,eAAe,EAAE,IAAI,eAAe,IAAI,EAAE;AAClE,oBAAU,KAAK,6BAAS,eAAe,SAAS,MAAM,EAAE;AACxD,oBAAU,KAAK,6BAAS,SAAS,MAAM,EAAE;AACzC,oBAAU,KAAK,6BAAS,SAAS,QAAQ,gBAAgB,KAAK,cAAI,EAAE;AAEpE,cAAI;AACF,yBAAa,IAAI,oBAAoB,mBAAmB,SAAS,CAAC;AAAA,UACpE,SAAS,OAAO;AACd,mBAAO,MAAM,wEAAiB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAAA,UACxF;AAAA,QACF;AAMA,YAAI,MAAM,KAAK,IAAI,aAAa;AAC9B,iBAAO,KAAK,+GAAqB;AACjC,kBAAQ,IAAI,oIAA0C;AAAA,QACxD;AAGA,mBAAW,CAAC,KAAK,KAAK,KAAK,aAAa,QAAQ,GAAG;AACjD,cAAI;AACF,gBAAI,CAAC,MAAM,KAAK,IAAI,aAAa;AAC/B,oBAAM,KAAK,IAAI,UAAU,KAAK,KAAK;AACnC,qBAAO,MAAM,sDAAc,GAAG,IAAI,KAAK,EAAE;AAAA,YAC3C,OAAO;AACL,qBAAO,KAAK,2DAAc,GAAG,4CAAS;AACtC,sBAAQ,IAAI,gFAAmC,GAAG,4CAAS;AAAA,YAC7D;AAAA,UACF,SAAS,aAAa;AACpB,mBAAO,KAAK,+CAAY,GAAG,kBAAQ,uBAAuB,QAAQ,YAAY,UAAU,OAAO,WAAW,CAAC,EAAE;AAC7G,oBAAQ,IAAI,oEAAiC,GAAG,kBAAQ,uBAAuB,QAAQ,YAAY,UAAU,OAAO,WAAW,CAAC,EAAE;AAAA,UACpI;AAAA,QACF;AAGA,iBAAS,KAAK,GAAG,SAAS,CAAC,QAAe;AACxC,iBAAO,MAAM,kEAAgB,IAAI,OAAO,EAAE;AAC1C,oCAA0B,gBAAgB,KAAK;AAC/C,iBAAO,GAAG;AAAA,QACZ,CAAC;AAED,iBAAS,KAAK,GAAG,OAAO,MAAM;AAC5B,iBAAO,MAAM,+DAAa;AAC1B,oCAA0B,gBAAgB,KAAK;AAC/C,kBAAQ;AAAA,QACV,CAAC;AAGD,cAAM,QAAQ,sBAAsB;AAIpC,iBAAS,KAAK,KAAK,MAAM,KAAK,GAAG;AAAA,MACnC,SAAS,KAAK;AACZ,kCAA0B,gBAAgB,KAAK;AAC/C,eAAO,GAAG;AAAA,MACZ;AAAA,IACF,CAAC;AAGD,WAAO;AAAA,EACT,SAAS,OAAO;AAEd,8BAA0B,gBAAgB,KAAK;AAE/C,WAAO,MAAM,yCAAW,SAAS,kBAAQ,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC,EAAE;AAGjG,QAAI,MAAM,KAAK,IAAI,aAAa;AAC9B,aAAO,KAAK,yGAAoB;AAChC,cAAQ,IAAI,8HAAyC;AAGrD,YAAM,QAAQ,sBAAsB;AACpC,aAAO;AAAA,IACT;AAGA,QAAI;AAEF,UAAI,aAAa;AACf,cAAM,YAAsB,CAAC;AAC7B,kBAAU,KAAK,8BAAS,oBAAI,KAAK,GAAE,YAAY,CAAC,EAAE;AAClD,kBAAU,KAAK,6BAAS,eAAe,EAAE,IAAI,eAAe,IAAI,EAAE;AAClE,kBAAU,KAAK,6BAAS,eAAe,SAAS,MAAM,EAAE;AACxD,kBAAU,KAAK,iBAAO,iBAAiB,QAAQ,MAAM,UAAU,sCAAQ,EAAE;AAEzE,YAAI;AACF,gBAAM,KAAK,IAAI,UAAU,oBAAoB,mBAAmB,SAAS,CAAC;AAC1E,iBAAO,MAAM,kEAAgB,kBAAkB,EAAE;AAAA,QACnD,SAAS,aAAa;AACpB,iBAAO,MAAM,wEAAiB,uBAAuB,QAAQ,YAAY,UAAU,OAAO,WAAW,CAAC,EAAE;AACxG,kBAAQ,IAAI,6FAAsC,uBAAuB,QAAQ,YAAY,UAAU,OAAO,WAAW,CAAC,EAAE;AAAA,QAC9H;AAAA,MACF;AAGA,YAAM,KAAK,IAAI,aAAa;AAC5B,YAAM,KAAK,IAAI,UAAU,gBAAgB,YAAY;AAGrD,YAAM,eAAe,8CAAW,eAAe,SAAS,MAAM,0CAAY,iBAAiB,QAAQ,MAAM,UAAU,sCAAQ;AAC3H,YAAM,KAAK,IAAI,IAAI,YAAY;AAE/B,aAAO,MAAM,yDAAY;AAAA,IAC3B,SAAS,eAAe;AACtB,aAAO,MAAM,kEAAgB,yBAAyB,QAAQ,cAAc,UAAU,OAAO,aAAa,CAAC,EAAE;AAC7G,cAAQ,IAAI,uFAAqC,yBAAyB,QAAQ,cAAc,UAAU,OAAO,aAAa,CAAC,EAAE;AAAA,IACnI;AAGA,UAAM,QAAQ,sBAAsB;AACpC,WAAO;AAAA,EACT;AACF;AAWA,eAAsB,aACpB,OACA,gBACA,cAAuB,OACL;AAElB,SAAO,uBAAuB,OAAO,gBAAgB,aAAa,KAAK;AACzE;","names":["LoadBalanceStrategy"]}