133 lines
4.1 KiB
JavaScript
133 lines
4.1 KiB
JavaScript
const axios = require('axios');
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
const yaml = require('js-yaml');
|
||
|
||
// 配置
|
||
const NACOS_SERVER = 'http://192.168.31.101:8848'; // 你的 Nacos 地址
|
||
const NAMESPACE = 'public';
|
||
const SERVICES = ['main-app', 'app-product', 'app-shared']; // 三个模块的服务名
|
||
const TRAEFIK_DYNAMIC_CONFIG = path.join(__dirname, './dynamic.yml');
|
||
const CHECK_INTERVAL = 5000; // 10秒检查一次服务变化
|
||
|
||
console.log('配置文件路径:', TRAEFIK_DYNAMIC_CONFIG);
|
||
if (!TRAEFIK_DYNAMIC_CONFIG) {
|
||
throw new Error('配置文件路径生成失败,路径为空');
|
||
}
|
||
|
||
// 通用中间件配置(可根据服务特性调整)
|
||
const commonMiddlewares = {
|
||
// 限流中间件(独立配置,关键修复)
|
||
rateLimit: {
|
||
rateLimit: {
|
||
average: 100,
|
||
burst: 200,
|
||
period: '1s',
|
||
sourceCriterion: {
|
||
ipStrategy: {depth: 2}
|
||
}
|
||
}
|
||
},
|
||
// 压缩中间件
|
||
compress: {
|
||
compress: {
|
||
excludedContentTypes: ['text/event-stream'],
|
||
minResponseBodyBytes: 1024
|
||
}
|
||
},
|
||
// IP白名单中间件(示例:允许本地和192.168.31网段)
|
||
ipWhiteList: {
|
||
ipWhiteList: {
|
||
sourceRange: ['127.0.0.1/32', '192.168.31.0/24']
|
||
}
|
||
},
|
||
// // 重定向中间件(HTTP→HTTPS)
|
||
// redirectToHttps: {
|
||
// redirectScheme: {
|
||
// scheme: 'https',
|
||
// permanent: true
|
||
// }
|
||
// },
|
||
// 重试中间件(关键修复:将重试配置移到这里)
|
||
retries: {
|
||
retry: {
|
||
attempts: 3,
|
||
initialInterval: '200ms'
|
||
}
|
||
}
|
||
};
|
||
|
||
// 从 Nacos 获取服务实例
|
||
async function getServiceInstances(serviceName) {
|
||
try {
|
||
const res = await axios.get(`${NACOS_SERVER}/nacos/v1/ns/instance/list`, {
|
||
params: {serviceName, namespaceId: NAMESPACE},
|
||
});
|
||
return res.data.hosts || [];
|
||
} catch (err) {
|
||
console.error(`获取 ${serviceName} 实例失败:`, err.message);
|
||
return [];
|
||
}
|
||
}
|
||
|
||
// 生成 traefik 动态配置
|
||
async function generateTraefikConfig() {
|
||
const config = {
|
||
http: {
|
||
routers: {},
|
||
services: {},
|
||
middlewares: commonMiddlewares
|
||
}
|
||
};
|
||
|
||
for (const serviceName of SERVICES) {
|
||
const instances = await getServiceInstances(serviceName);
|
||
if (instances.length === 0) {
|
||
console.warn(`服务 ${serviceName} 无可用实例`);
|
||
continue;
|
||
}
|
||
|
||
// 1. 配置服务(服务名 + 实例列表)
|
||
config.http.services[`${serviceName}-service`] = {
|
||
loadBalancer: {
|
||
servers: instances.map(inst => ({
|
||
url: `http://${inst.ip}:${inst.port}`,
|
||
})),
|
||
// 熔断配置
|
||
// healthCheck: {
|
||
// path: '/health', // 假设服务有健康检查接口
|
||
// interval: '30s',
|
||
// timeout: '5s',
|
||
// unhealthyThreshold: 3,
|
||
// healthyThreshold: 2
|
||
// }
|
||
}
|
||
};
|
||
|
||
// 2. 配置路由(路径前缀 + 绑定服务)
|
||
config.http.routers[`${serviceName}-router`] = {
|
||
rule: `PathPrefix(\`/${serviceName}\`)`, // 如 /main、/product
|
||
service: `${serviceName}-service`,
|
||
entryPoints: ['web'],
|
||
// 应用中间件(压缩、IP白名单、限流、重试等)
|
||
middlewares: ['compress', 'ipWhiteList', 'rateLimit', 'retries'],
|
||
};
|
||
}
|
||
// 将对象转换为 YAML 字符串
|
||
const yamlString = yaml.dump(config, {
|
||
indent: 2, // 缩进空格数
|
||
skipInvalid: true, // 跳过无效类型
|
||
noRefs: true // 禁止引用(防止循环引用问题)
|
||
});
|
||
|
||
console.log("yamlString: ", yamlString)
|
||
|
||
// 写入动态配置文件
|
||
fs.writeFileSync(TRAEFIK_DYNAMIC_CONFIG, yamlString, "utf8");
|
||
console.log('动态配置已更新');
|
||
}
|
||
|
||
// 定时检查并更新配置
|
||
setInterval(generateTraefikConfig, CHECK_INTERVAL);
|
||
// 立即执行一次
|
||
generateTraefikConfig(); |