【重要】重构 并实现新的架构

This commit is contained in:
编码猿
2025-09-30 10:00:10 +08:00
parent 04754a1f77
commit a3ba6c2944
31 changed files with 459 additions and 302 deletions

View File

@@ -5,9 +5,9 @@ export const app = new App<State>();
app.use(cors({ app.use(cors({
origin: "*", origin: "*",
allowHeaders: [ "*" ], allowHeaders: ["*"],
allowMethods: [ "POST", "GET", "OPTIONS" ], allowMethods: ["POST", "GET", "OPTIONS"],
exposeHeaders: [ "Content-Length", "X-Kuma-Revision" ], exposeHeaders: ["Content-Length", "X-Kuma-Revision"],
maxAge: 600, maxAge: 600,
credentials: true, credentials: true,
})) }))
@@ -74,8 +74,17 @@ app.get("/api/productsInfo", (req: Request, ctx: { params: { id: number; }; }) =
return new Response(Res(result)); return new Response(Res(result));
}); });
// test 测试接口
app.get("/nacos", (ctx: { params: { name: any; }; }) => {
console.log("test 测试接口 被运行");
return new Response(JSON.stringify({
title: "test测试接口"
}));
});
const exampleLoggerMiddleware = define.middleware((ctx: { req: { method: any; url: any; }; next: () => any; }) => { const exampleLoggerMiddleware = define.middleware((ctx: { req: { method: any; url: any; }; next: () => any; }) => {
console.log(`${ ctx.req.method } ${ ctx.req.url }`); console.log(`${ctx.req.method} ${ctx.req.url}`);
return ctx.next(); return ctx.next();
}); });

View File

@@ -98,7 +98,7 @@ ufw status
### 运行启动 ### 运行启动
将本文件夹内所有文件上传到 1号服务器然后执行命令 将本文件夹内所有文件上传到 1号服务器然后在文件夹内执行命令:
```bash ```bash
# 部署栈,这个过程有点慢,因为需要拉取镜像,等! # 部署栈,这个过程有点慢,因为需要拉取镜像,等!

View File

@@ -0,0 +1,17 @@
version: '3.8'
services:
nginx-proxy:
image: nginx:latest
container_name: nginx-proxy
ports:
- "80:80"
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
restart: unless-stopped
networks:
- proxy-network
networks:
proxy-network:
driver: bridge

61
docker/nginx/nginx.conf Normal file
View File

@@ -0,0 +1,61 @@
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 上游服务器配置
upstream nacos_cluster {
server 192.168.139.84:8848;
server 192.168.139.15:8848;
server 192.168.139.178:8848;
}
upstream nacos_ui {
server 192.168.139.84:8080;
server 192.168.139.15:8080;
server 192.168.139.178:8080;
}
server {
listen 80;
server_name cluster.nacos.com;
# 反向代理配置
location / {
proxy_pass http://nacos_cluster;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 超时设置
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
}
}
server {
listen 80;
server_name ui.nacos.com;
# 反向代理配置
location / {
proxy_pass http://nacos_ui;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
# 超时设置
proxy_connect_timeout 30s;
proxy_send_timeout 30s;
proxy_read_timeout 30s;
}
}
}

View File

@@ -8,8 +8,7 @@ services:
- traefik-net - traefik-net
ports: ports:
- "80:80" - "80:80"
- "8880:8080" - "8080:8080"
# network_mode: "host" # 主机网络模式
volumes: volumes:
- ./traefik.yml:/etc/traefik/traefik.yml - ./traefik.yml:/etc/traefik/traefik.yml
- ./dynamic.yml:/etc/traefik/dynamic.yml # 动态配置文件 - ./dynamic.yml:/etc/traefik/dynamic.yml # 动态配置文件

View File

@@ -1,15 +1,13 @@
# 请求协议 http or https建议线上配置证书后使用https # 请求协议 http or https建议线上配置证书后使用https
VITE_PREFIX=http:// VITE_PREFIX=http://
# Nacos 配置中心ip:端口 # Nacos 配置中心 ip:端口 | 域名
VITE_NACOS_ADDRESS=192.168.31.101:8848 #VITE_NACOS_ADDRESS=cluster.nacos.com
VITE_NACOS_ADDRESS=192.168.139.84:8848
# traefik 网关的地址
VITE_TRAEFIK_ADDRESS=192.168.31.101
# 注册到 Nacos 的服务名称(微前端模块名称 # 注册到 Nacos 的服务名称(微前端模块名称
VITE_SERVER_NAME=app-product VITE_SERVER_NAME=app-product
# 注册到 Nacos 的端口,也是前端运行的端口 # 注册到 Nacos 的端口,也是前端运行的端口
VITE_SERVER_PORT=1301 VITE_SERVER_PORT=1301
@@ -25,3 +23,4 @@ VITE_MODEL_SHARED_NAME=app-shared

View File

@@ -1,11 +1,9 @@
# 请求协议 http or https建议线上配置证书后使用https # 请求协议 http or https建议线上配置证书后使用https
VITE_PREFIX=http:// VITE_PREFIX=http://
# Nacos 配置中心ip:端口 # Nacos 配置中心 ip:端口 | 域名
VITE_NACOS_ADDRESS=192.168.31.101:8848 VITE_NACOS_ADDRESS=192.168.139.84:8848
# traefik 网关的地址
VITE_TRAEFIK_ADDRESS=192.168.31.101
# 注册到 Nacos 的服务名称(微前端模块名称 # 注册到 Nacos 的服务名称(微前端模块名称
VITE_SERVER_NAME=app-product VITE_SERVER_NAME=app-product
@@ -13,7 +11,6 @@ VITE_SERVER_NAME=app-product
# 注册到 Nacos 的端口,也是前端运行的端口 # 注册到 Nacos 的端口,也是前端运行的端口
VITE_SERVER_PORT=1301 VITE_SERVER_PORT=1301
# #
# 说明:项目要调用哪些 微前端 模块 # 说明:项目要调用哪些 微前端 模块
# 格式VITE_MODEL_XXXX_NAME=微前端模块名称(模块 的 开发者提供) # 格式VITE_MODEL_XXXX_NAME=微前端模块名称(模块 的 开发者提供)
@@ -22,6 +19,3 @@ VITE_SERVER_PORT=1301
# shared 共享资源模块 # shared 共享资源模块
VITE_MODEL_SHARED_NAME=app-shared VITE_MODEL_SHARED_NAME=app-shared

View File

@@ -14,8 +14,8 @@
"type-check": "vue-tsc --build" "type-check": "vue-tsc --build"
}, },
"dependencies": { "dependencies": {
"nacos-federation": "link:../nacos-federation",
"pinia": "^3.0.3", "pinia": "^3.0.3",
"vite-plugin-nacos": "link:../vite-plugin-nacos",
"vue": "^3.5.21", "vue": "^3.5.21",
"vue-router": "^4.5.1" "vue-router": "^4.5.1"
}, },

View File

@@ -1,7 +1,12 @@
import { defineComponent } from 'vue' import { defineComponent, onMounted } from 'vue'
export default defineComponent({ export default defineComponent({
setup() { setup() {
onMounted(() => {
console.log("import.meta.env: ", import.meta.env)
})
return () => ( return () => (
<> <>
<div class="order-federation-placeholder"> <div class="order-federation-placeholder">

View File

@@ -22,7 +22,6 @@ export default defineComponent({
onMounted(() => { onMounted(() => {
loadProductList() loadProductList()
}) })

View File

@@ -5,24 +5,18 @@ import vueDevTools from 'vite-plugin-vue-devtools'
import federation from '@originjs/vite-plugin-federation'; import federation from '@originjs/vite-plugin-federation';
import { fileURLToPath, URL } from "node:url"; import { fileURLToPath, URL } from "node:url";
import { visualizer } from 'rollup-plugin-visualizer'; import { visualizer } from 'rollup-plugin-visualizer';
import { reg, init } from 'vite-plugin-nacos';
export default defineConfig(async ({ mode }) => { export default defineConfig(async ({ mode }) => {
const env = loadEnv(mode, process.cwd()); const env = loadEnv(mode, process.cwd());
const { VITE_MODEL_SHARED_URL } = await init(env)
return { return {
base: `${ env.VITE_PREFIX }${ env.VITE_TRAEFIK_ADDRESS }/${ env.VITE_SERVER_NAME }`,
server: {
port: Number(env.VITE_SERVER_PORT),
host: '0.0.0.0',
strictPort: true,
cors: true
},
plugins: [ plugins: [
// nacosRegVitePlugin(), reg(),
federation({ federation({
name: env.VITE_SERVER_NAME, name: env.VITE_SERVER_NAME,
remotes: { remotes: {
shared: `${ env.VITE_PREFIX }${ env.VITE_TRAEFIK_ADDRESS }/${ env.VITE_MODEL_SHARED_NAME }/assets/remoteEntry.js` shared: `${VITE_MODEL_SHARED_URL}/${env.VITE_MODEL_SHARED_NAME}/assets/remoteEntry.js`
}, },
exposes: { // 暴露给主应用的资源(按功能分组) exposes: { // 暴露给主应用的资源(按功能分组)
'./ProductDomain': './src/domains/product/index.ts', // 领域层(实体、服务、接口等) './ProductDomain': './src/domains/product/index.ts', // 领域层(实体、服务、接口等)
@@ -50,7 +44,7 @@ export default defineConfig(async ({ mode }) => {
cssCodeSplit: true, cssCodeSplit: true,
rollupOptions: { rollupOptions: {
// 关键配置:告诉 Rollup 不要处理模块联邦的导入 // 关键配置:告诉 Rollup 不要处理模块联邦的导入
external: [ 'shared/normalize' ], external: ['shared/normalize'],
output: { output: {
minifyInternalExports: false minifyInternalExports: false
} }

View File

@@ -1,11 +1,10 @@
# 请求协议 http or https建议线上配置证书后使用https # 请求协议 http or https建议线上配置证书后使用https
VITE_PREFIX=http:// VITE_PREFIX=http://
# Nacos 配置中心ip:端口 # Nacos 配置中心 ip:端口 | 域名
VITE_NACOS_ADDRESS=192.168.31.101:8848 VITE_NACOS_ADDRESS=cluster.nacos.com
#VITE_NACOS_ADDRESS=192.168.139.84:8848
# traefik 网关的地址
VITE_TRAEFIK_ADDRESS=192.168.31.101
# 注册到 Nacos 的服务名称(微前端模块名称) # 注册到 Nacos 的服务名称(微前端模块名称)
VITE_SERVER_NAME=app-shared VITE_SERVER_NAME=app-shared

View File

@@ -1,11 +1,9 @@
# 请求协议 http or https建议线上配置证书后使用https # 请求协议 http or https建议线上配置证书后使用https
VITE_PREFIX=http:// VITE_PREFIX=http://
# Nacos 配置中心ip:端口 # Nacos 配置中心 ip:端口 | 域名
VITE_NACOS_ADDRESS=192.168.31.101:8848 #VITE_NACOS_ADDRESS=cluster.nacos.com
VITE_NACOS_ADDRESS=192.168.139.84:8848
# traefik 网关的地址
VITE_TRAEFIK_ADDRESS=192.168.31.101
# 注册到 Nacos 的服务名称(微前端模块名称) # 注册到 Nacos 的服务名称(微前端模块名称)
VITE_SERVER_NAME=app-shared VITE_SERVER_NAME=app-shared

View File

@@ -17,7 +17,7 @@
"@originjs/vite-plugin-federation": "1.3.8", "@originjs/vite-plugin-federation": "1.3.8",
"axios": "^1.12.2", "axios": "^1.12.2",
"element-plus": "^2.11.3", "element-plus": "^2.11.3",
"nacos-federation": "link:../nacos-federation", "vite-plugin-nacos": "link:../vite-plugin-nacos",
"vue": "^3.5.21" "vue": "^3.5.21"
}, },
"devDependencies": { "devDependencies": {

View File

@@ -6,21 +6,15 @@ import vueJsx from '@vitejs/plugin-vue-jsx'
// @ts-ignore // @ts-ignore
import vue from '@vitejs/plugin-vue'; import vue from '@vitejs/plugin-vue';
import visualizer from 'rollup-plugin-visualizer'; import visualizer from 'rollup-plugin-visualizer';
import { reg, init } from 'vite-plugin-nacos';
export default defineConfig(async ({ mode }): Promise<UserConfig> => { export default defineConfig(async ({ mode }): Promise<UserConfig> => {
const env = loadEnv(mode, process.cwd()); const env = loadEnv(mode, process.cwd());
init(env)
return { return {
base: `${ env.VITE_PREFIX }${ env.VITE_TRAEFIK_ADDRESS }/${ env.VITE_SERVER_NAME }`,
server: {
port: Number(env.VITE_SERVER_PORT),
host: '0.0.0.0',
strictPort: true,
cors: true
},
plugins: [ plugins: [
// nacosRegVitePlugin(), reg(),
federation({ federation({
name: env.VITE_SERVER_NAME, name: env.VITE_SERVER_NAME,
exposes: { exposes: {

View File

@@ -1,11 +1,17 @@
# 请求协议 http or https建议线上配置证书后使用https # 请求协议 http or https建议线上配置证书后使用https
VITE_PREFIX=http:// VITE_PREFIX=http://
# Nacos 配置中心ip:端口 #
VITE_NACOS_ADDRESS=192.168.139.84:8848 # Nacos 集群的地址
# 注意:
# 1: 内网微前端模块直接填内网Nacos IP:端口 就行
# 2: main-app位于公网填 网关的公网地址
#
# 网关地址
VITE_NACOS_ADDRESS=nacos.ddd.com:80
# VITE_NACOS_ADDRESS=192.168.139.84:8848
# traefik 网关的地址
VITE_TRAEFIK_ADDRESS=192.168.31.101
# 注册到 Nacos 的服务名称(微前端模块名称) # 注册到 Nacos 的服务名称(微前端模块名称)
VITE_SERVER_NAME=main-app VITE_SERVER_NAME=main-app
@@ -24,4 +30,4 @@ VITE_SERVER_PORT=1300
VITE_MODEL_PRODUCT_NAME=app-product VITE_MODEL_PRODUCT_NAME=app-product
# shared 共享资源模块 # shared 共享资源模块
VITE_MODEL_SHARED_NAME=app-shared VITE_MODEL_SHARED_NAME=app-shared

View File

@@ -1,8 +1,9 @@
# 请求协议 http or https建议线上配置证书后使用https # 请求协议 http or https建议线上配置证书后使用https
VITE_PREFIX=http:// VITE_PREFIX=http://
# Nacos 配置中心ip:端口 # Nacos 配置中心 ip:端口 | 域名
VITE_NACOS_ADDRESS=192.168.31.101:8848 VITE_NACOS_ADDRESS=cluster.nacos.com
#VITE_NACOS_ADDRESS=192.168.139.84:8848
# traefik 网关的地址 # traefik 网关的地址
VITE_TRAEFIK_ADDRESS=192.168.31.101 VITE_TRAEFIK_ADDRESS=192.168.31.101
@@ -24,4 +25,4 @@ VITE_SERVER_PORT=1300
VITE_MODEL_PRODUCT_NAME=app-product VITE_MODEL_PRODUCT_NAME=app-product
# shared 共享资源模块 # shared 共享资源模块
VITE_MODEL_SHARED_NAME=app-shared VITE_MODEL_SHARED_NAME=app-shared

View File

@@ -11,8 +11,8 @@
"type-check": "vue-tsc --build" "type-check": "vue-tsc --build"
}, },
"dependencies": { "dependencies": {
"nacos-federation": "link:../nacos-federation",
"pinia": "^3.0.3", "pinia": "^3.0.3",
"vite-plugin-nacos": "link:../vite-plugin-nacos",
"vue": "^3.5.21", "vue": "^3.5.21",
"vue-facing-decorator": "^4.0.1", "vue-facing-decorator": "^4.0.1",
"vue-router": "^4.5.1" "vue-router": "^4.5.1"

View File

@@ -11,10 +11,11 @@ class Main {
const app = createApp(App) const app = createApp(App)
app.use(createPinia()) app.use(createPinia())
.use(router) .use(router)
.mount('#app') .mount('#app')
// TODO 需要解决的地方 // TODO 需要解决的地方
console.log("import.meta.env: ", import.meta.env)
window.__APP_CONFIG__ = import.meta.env; window.__APP_CONFIG__ = import.meta.env;
elementPlus.init(app) elementPlus.init(app)

View File

@@ -1,45 +1,22 @@
import path from 'path' import path from 'path'
import { defineConfig, loadEnv } from 'vite' import { defineConfig, loadEnv, ConfigEnv, mergeConfig } from 'vite'
import vueJsx from '@vue3-oop/plugin-vue-jsx' import vueJsx from '@vue3-oop/plugin-vue-jsx'
import federation from '@originjs/vite-plugin-federation'; import federation from '@originjs/vite-plugin-federation';
import { nacosRegVitePlugin } from "nacos-federation"; import { reg, init } from 'vite-plugin-nacos';
import { visualizer } from 'rollup-plugin-visualizer'; import { visualizer } from 'rollup-plugin-visualizer';
export default defineConfig(async ({ mode }) => { export default defineConfig(async (config: ConfigEnv) => {
const env = loadEnv(mode, process.cwd()); const env = loadEnv(config.mode, process.cwd());
// const nacos-cluster = await nacosServiceManage.create({
// serverList: env.VITE_NACOS_ADDRESS,
// serviceName: env.VITE_SERVER_NAME,
// port: Number(env.VITE_SERVER_PORT)
// });
// const productUrl = await nacos-cluster.find(env.VITE_MODEL_PRODUCT_NAME)
// const sharedUrl = await nacos-cluster.find(env.VITE_MODEL_SHARED_NAME)
return { return mergeConfig({
server: {
port: Number(env.VITE_SERVER_PORT),
host: '::',
strictPort: true,
},
plugins: [ plugins: [
nacosRegVitePlugin(), reg(),
vueJsx(),
federation({ federation({
name: env.VITE_SERVER_NAME, name: env.VITE_SERVER_NAME,
remotes: { remotes: {
// 【弃用】基于 Nacos 服务发现获取 微前端模块的地址 192.168.31.101:1301 product: `${env.VITE_PREFIX}${env.VITE_NACOS_ADDRESS}/${env.VITE_MODEL_PRODUCT_NAME}/assets/remoteEntry.js`,
// product: `${ productUrl }/assets/remoteEntry.js`, shared: `${env.VITE_PREFIX}${env.VITE_NACOS_ADDRESS}/${env.VITE_MODEL_SHARED_NAME}/assets/remoteEntry.js`
// shared: `${ sharedUrl }/assets/remoteEntry.js`
// 实现限流与熔断重试IP 白名单,压缩等
// 基于 c 网关,由网关去寻找 微前端模块的地址
// 网关的地址是 nodejs脚本 定时10秒访问Nacos api 后生成Traefik配置文件dynamic.yml
// 并把配置实时同步到Traefik中如果 xxx微前端模块停止运行那么就会从 Nacos服务中注销
// 那么dynamic.yml配置文件就会自动删减被下线的网关映射
product: `${ env.VITE_PREFIX }${ env.VITE_TRAEFIK_ADDRESS }/${ env.VITE_MODEL_PRODUCT_NAME }/assets/remoteEntry.js`,
shared: `${ env.VITE_PREFIX }${ env.VITE_TRAEFIK_ADDRESS }/${ env.VITE_MODEL_SHARED_NAME }/assets/remoteEntry.js`
}, },
shared: { shared: {
vue: { requiredVersion: '^3.5.18' }, vue: { requiredVersion: '^3.5.18' },
@@ -47,6 +24,7 @@ export default defineConfig(async ({ mode }) => {
pinia: { requiredVersion: '^3.0.3' } pinia: { requiredVersion: '^3.0.3' }
} }
}), }),
vueJsx(),
visualizer({ visualizer({
title: "主应用依赖分析", title: "主应用依赖分析",
filename: 'dist/stats.html', // 分析文件输出路径 filename: 'dist/stats.html', // 分析文件输出路径
@@ -73,5 +51,5 @@ export default defineConfig(async ({ mode }) => {
'@domains': path.resolve(__dirname, './src/domains/') '@domains': path.resolve(__dirname, './src/domains/')
}, },
}, },
} }, await init(env))
}) })

View File

@@ -1,4 +0,0 @@
// 从插件实现文件导入
export * from './vite-plugin-nacos';
export * from './nacosServiceManage';
export * from './utils';

View File

@@ -1,36 +0,0 @@
import { networkInterfaces } from "os";
/**
* 获取本机局域网IPv4地址
* @returns 局域网IP地址如 '192.168.1.100'
*/
export function getLocalIP(): string {
const interfaces = networkInterfaces();
for (const devName in interfaces) {
const iface = interfaces[devName];
if (!iface) continue;
for (const alias of iface) {
if (
alias.family === 'IPv4' &&
alias.address !== '127.0.0.1' &&
!alias.internal
) {
return alias.address;
}
}
}
return '127.0.0.1';
}
/**
* 格式化服务地址
* @param ip IP地址
* @param port 端口号
* @returns 完整服务地址(如 'http://192.168.1.100:1300'
*/
export function formatServiceUrl(ip: string, port: number): string {
return `http://${ ip }:${ port }`;
}

View File

@@ -1,123 +0,0 @@
import { loadEnv, Plugin, UserConfig } from 'vite';
import { getLocalIP, nacosServiceManage, RegisterOptions } from './index';
import chalk from 'chalk';
const log = console.log;
export function nacosRegVitePlugin(options?: Partial<RegisterOptions>): Plugin {
let nacosInstance: nacosServiceManage | null = null;
return {
name: 'vite-plugin-nacos',
async config(userConfig: UserConfig, { mode }) {
// 修改base增加资源来源地址修复资源错误加载的问题
const env = loadEnv(mode, process.cwd());
let baseUrl = ""
// 主模块 main-app 不加 base 前缀,其他模块都加,
// 也是为了命中 traefik 路由代理
if (env.VITE_SERVER_NAME != "main-app") {
baseUrl = `${ env.VITE_SERVER_NAME }/`
}
return {
// http(https)://前端运行的服务器所在ip/前端模块名
base: `${ env.VITE_PREFIX }${ getLocalIP() }/${ baseUrl }`,
}
},
async configResolved(config) {
const env = config.env;
const registerOptions: RegisterOptions = {
serverList: env.VITE_NACOS_ADDRESS || options?.serverList!,
serviceName: env.VITE_SERVER_NAME || options?.serviceName!,
port: Number(env.VITE_SERVER_PORT || options?.port),
namespace: options?.namespace || 'public'
};
// 验证必要配置
if (!registerOptions.serverList) {
throw new Error('[nacos-cluster-federation] 缺少Nacos服务列表配置请设置VITE_NACOS_ADDRESS环境变量或插件选项');
}
if (!registerOptions.serviceName) {
throw new Error('[nacos-cluster-federation] 缺少服务名称配置请设置VITE_SERVER_NAME环境变量或插件选项');
}
if (!registerOptions.port) {
throw new Error('[nacos-cluster-federation] 缺少服务端口配置请设置VITE_SERVER_PORT环境变量或插件选项');
}
// 初始化Nacos实例
nacosInstance = await nacosServiceManage.create(registerOptions);
const baseUrl = await nacosInstance.getConfig("baseUrl", "DEFAULT_GROUP")
const baseUrlConfig = baseUrl ? JSON.parse(baseUrl) : {};
baseUrlConfig.httpTimeOut = await nacosInstance.getConfig("httpTimeOut", "DEFAULT_GROUP")
const customizeEnv: Record<string, string> = {};
Object.keys(baseUrlConfig).forEach(key => {
const envKey = `import.meta.env.VITE_${ key.toUpperCase() }`;
customizeEnv[envKey] = JSON.stringify(baseUrlConfig[key]);
});
// @ts-ignore
Object.assign(config.define, customizeEnv);
Logs(env)
},
async closeBundle() {
// // 服务关闭时注销Nacos实例
// if (nacosInstance) {
// await nacosInstance.destroy();
// }
},
}
}
function Logs(env: Record<string, any>) {
const moduleName = []
for (const key in env) {
if (key.includes("VITE_MODEL_")) {
moduleName.push(env[key]);
}
}
moduleName.length == 0 ? moduleName.push('无') : null
const webUI = env.VITE_NACOS_ADDRESS.split(":")[0]
console.log()
console.log(dedentTemplateString(`
${ chalk.blue.bold('微服务工具:') }
${ chalk.green('➜') } Naco 配置中心: ${ env.VITE_PREFIX }${ env.VITE_NACOS_ADDRESS }
${ chalk.green('➜') } WebUi ${ env.VITE_PREFIX }${ webUI }:8085
${ chalk.green('➜') } Prometheus 监控: ${ env.VITE_PREFIX }${ webUI }:9090
${ chalk.green('➜') } Grafana 面板: ${ env.VITE_PREFIX }${ webUI }:3000
${ chalk.green('➜') } Traefik 网关: ${ env.VITE_PREFIX }${ env.VITE_TRAEFIK_ADDRESS }
${ chalk.green('➜') } WebUi ${ env.VITE_PREFIX }${ env.VITE_TRAEFIK_ADDRESS }:8880
${ chalk.blue.bold('微前端信息:') }
${ chalk.green('➜') } 主模块:${ chalk.green(env.VITE_SERVER_NAME) }
${ chalk.blue.bold('调用模块:') }
${ chalk.green('➜') } [ ${ chalk.yellow(moduleName) } ]
`))
console.log()
}
function dedentTemplateString(str: string) {
// 分割成行
const lines = str.split('\n');
// 移除首尾空行
if (lines[0].trim() === '') lines.shift();
if (lines[lines.length - 1].trim() === '') lines.pop();
// 找到最小缩进量
const minIndent = lines.reduce((min, line) => {
if (line.trim() === '') return min; // 跳过空行
// @ts-ignore
const indent = line.match(/^\s*/)[0].length;
return indent < min ? indent : min;
}, Infinity);
// 移除统一缩进
return lines.map(line => line.slice(minIndent)).join('\n');
}

View File

@@ -6,6 +6,12 @@
主模块main-app -> Traefik -> ConfigServernacos-federation -> Nacos 主模块main-app -> Traefik -> ConfigServernacos-federation -> Nacos
main-app 发送 http 请求
/nacos/reg
参数:
ipport
2 获取配置 2 获取配置
@@ -15,6 +21,9 @@
主模块: main-app -> Traefik -> ConfigServernacos-federation -> Nacos 主模块: main-app -> Traefik -> ConfigServernacos-federation -> Nacos
注意:
增加功能: 增加功能:
认证授权机制 认证授权机制

View File

@@ -1,7 +1,7 @@
{ {
"name": "nacos-federation", "name": "vite-plugin-nacos",
"version": "1.1.0", "version": "1.1.0",
"description": "Nacos服务注册与发现插件用于模块联邦", "description": "Vite 插件,实现Nacos服务注册与发现插件用于模块联邦",
"main": "dist/index.js", "main": "dist/index.js",
"types": "dist/index.d.ts", "types": "dist/index.d.ts",
"files": [ "files": [

View File

@@ -0,0 +1,56 @@
// 从插件实现文件导入
export * from './vite-plugin-nacos';
export * from './utils';
import { log } from 'console';
import { nacosServiceManage } from './nacosServiceManage';
import { mergeConfig } from 'vite';
export const init = async (env: Record<string, string>) => {
const nacos = await nacosServiceManage.create({
serverList: env.VITE_NACOS_ADDRESS,
serviceName: env.VITE_SERVER_NAME,
port: Number(env.VITE_SERVER_PORT)
});
if (env.VITE_SERVER_NAME === "main-app") {
log("不需要服务发现,进行配置发现...")
const baseUrl = await nacos.getConfig("baseUrl", "DEFAULT_GROUP")
const baseUrlConfig = baseUrl ? JSON.parse(baseUrl) : {};
baseUrlConfig.httpTimeOut = await nacos.getConfig("httpTimeOut", "DEFAULT_GROUP")
// 将新配置处理一下,需要 JSON.stringify
const customizeEnv: Record<string, Record<string, string>> = { define: {} };
Object.keys(baseUrlConfig).forEach(key => {
customizeEnv.define[`import.meta.env.VITE_${key.toUpperCase()}`] = JSON.stringify(baseUrlConfig[key]);
});
// 将老配置处理一下,需要 JSON.stringify
const existingDefine: Record<string, Record<string, string>> = { define: {} }
Object.keys(env).forEach(key => {
existingDefine.define[`import.meta.env.${key}`] = JSON.stringify(env[key])
})
return mergeConfig(existingDefine, customizeEnv)
} else {
log("进行服务发现,获取依赖模块的地址...")
let modelName = []
for (const key in env) {
if (key.includes("VITE_MODEL_")) modelName.push(env[key])
}
const modelUrl: Record<string, string> = {}
if (modelName.length !== 0) {
for (const val of modelName) modelUrl[`VITE_MODEL_${val.replace("app-", "").toUpperCase()}_URL`] = await nacos.find(val)
}
log("modelUrl: ", modelUrl)
return modelUrl
}
}

View File

@@ -4,7 +4,7 @@ import chalk from 'chalk';
const logger = console; const logger = console;
export interface RegisterOptions { interface RegisterOptions {
serverList: string; serverList: string;
/** 服务名称(如 'main-app' */ /** 服务名称(如 'main-app' */
serviceName: string; serviceName: string;
@@ -42,21 +42,13 @@ export class nacosServiceManage {
} }
const instance = new nacosServiceManage(option); const instance = new nacosServiceManage(option);
// await instance.initClient(); await instance.initClient();
// await instance.initConfig(); await instance.initConfig();
this.instanceMap.set(configKey, instance); this.instanceMap.set(configKey, instance);
return instance; return instance;
} }
/**
* serverList + namespace + serviceName
*/
private static getConfigKey(option: RegisterOptions): string {
const namespace = option.namespace || 'public';
return `${ option.serverList }-${ namespace }-${ option.serviceName }`;
}
// 服务注册 // 服务注册
public async initClient() { public async initClient() {
this.nacosClient = new NacosNamingClient({ this.nacosClient = new NacosNamingClient({
@@ -69,15 +61,11 @@ export class nacosServiceManage {
} }
/** /**
* * serverList + namespace + serviceName
* @param dataId
* @param group
*/ */
async getConfig(dataId: string, group: string = "DEFAULT_GROUP") { private static getConfigKey(option: RegisterOptions): string {
const res = await this.configClient.getConfig(dataId, group) const namespace = option.namespace || 'public';
console.log() return `${ option.serverList }-${ namespace }-${ option.serviceName }`;
console.log(`${ chalk.blue('[nacos-cluster-federation] 配置发现:') }\n ${ chalk.green('➜') } ${ chalk.green(dataId) } 的配置为: ${ chalk.green(res) }`)
return res;
} }
/** /**
@@ -93,16 +81,18 @@ export class nacosServiceManage {
}) })
} }
/**
*
* @private
*/
private async initConfig() {
this.configClient = new NacosConfigClient({
serverAddr: this.initOptions.serverList,
});
}
/**
*
* @param dataId
* @param group
*/
async getConfig(dataId: string, group: string = "DEFAULT_GROUP") {
const res = await this.configClient.getConfig(dataId, group)
console.log()
console.log(`${ chalk.blue('[nacos-cluster-federation] 配置发现:') }\n ${ chalk.green('➜') } ${ chalk.green(dataId) } 的配置为: ${ chalk.green(res) }`)
return res;
}
/** /**
* Nacos发现服务地址 * Nacos发现服务地址
@@ -118,6 +108,29 @@ export class nacosServiceManage {
return formatServiceUrl(instances[0].ip, instances[0].port); return formatServiceUrl(instances[0].ip, instances[0].port);
} }
/**
*
* @private
*/
private async initConfig() {
this.configClient = new NacosConfigClient({
serverAddr: this.initOptions.serverList,
});
}
/**
* Nacos并返回客户端实例
*/
private async reg() {
const { serviceName, port } = this.initOptions;
const ip = getLocalIP();
// @ts-ignore
await this.nacosClient.registerInstance(serviceName, { port, ip })
logger.log()
logger.log(chalk.blue("[nacos-cluster-federation] 服务注册:"))
logger.log(` ${ chalk.green('➜') } ${ chalk.green(serviceName) } (${ formatServiceUrl(ip, port) }) 已注册到配置中心!`);
}
/** /**
* 线 * 线
*/ */
@@ -134,18 +147,5 @@ export class nacosServiceManage {
nacosServiceManage.instanceMap.delete(configKey); nacosServiceManage.instanceMap.delete(configKey);
} }
/**
* Nacos并返回客户端实例
*/
private async reg() {
const { serviceName, port } = this.initOptions;
const ip = getLocalIP();
// @ts-ignore
await this.nacosClient.registerInstance(serviceName, { port, ip })
logger.log()
logger.log(chalk.blue("[nacos-cluster-federation] 服务注册:"))
logger.log(` ${ chalk.green('➜') } ${ chalk.green(serviceName) } (${ formatServiceUrl(ip, port) }) 已注册到配置中心!`);
}
} }

View File

@@ -0,0 +1,52 @@
import { networkInterfaces } from "os";
class Utils {
/**
* 获取本机局域网IPv4地址
* @returns 局域网IP地址如 '192.168.1.100'
*/
static getLocalIP(): string {
const interfaces = networkInterfaces();
for (const devName in interfaces) {
const iface = interfaces[devName];
if (!iface) continue;
for (const alias of iface) {
if (
alias.family === 'IPv4' &&
alias.address !== '127.0.0.1' &&
!alias.internal
) {
return alias.address;
}
}
}
return '127.0.0.1';
}
/**
* 格式化服务地址
* @param ip IP地址
* @param port 端口号
* @returns 完整服务地址(如 'http://192.168.1.100:1300'
*/
static formatServiceUrl(ip: string, port: number): string {
return `http://${ ip }:${ port }`;
}
/**
* VITE_DEPEND_MODEL_NAME_LIST 配置 格式化成数组
* app-shared,app-test
* @param arg ["app-shared", "app-test"]
*/
static dependModel(arg: string): string[] {
return arg.split(',').map(name => name.trim()).filter(Boolean) || [];
}
}
export const {
getLocalIP, formatServiceUrl,
dependModel
} = Utils

View File

@@ -0,0 +1,144 @@
import { loadEnv, Plugin, UserConfig } from 'vite';
import { NacosConfigClient, NacosNamingClient } from 'nacos';
import { getLocalIP, dependModel, formatServiceUrl } from './utils';
import chalk from 'chalk';
const logger = console;
export interface RegisterOptions {
serverList: string;
/** 服务名称(如 'main-app' */
serviceName: string;
/** 服务端口号 */
port: number;
/** 命名空间(用于环境隔离,默认 'public' */
namespace?: string;
}
export function reg(options?: Partial<RegisterOptions>): Plugin {
return {
name: 'vite-plugin-nacos',
async config(config: UserConfig, { mode }) {
// 修改base增加资源来源地址修复资源错误加载的问题
const env = loadEnv(mode, process.cwd());
let baseUrl = ""
// 主模块 main-app 不加 base 前缀,其他模块都加,
// 也是为了命中 traefik 路由代理
if (env.VITE_SERVER_NAME != "main-app") {
baseUrl = `${env.VITE_SERVER_NAME}/`
}
return {
server: {
allowedHosts: [
'.ddd.com',
],
port: Number(env.VITE_SERVER_PORT),
host: '::',
strictPort: true,
cors: true
},
// TODO dist 上线到服务器后http://192.168.31.101:1301/ 会出问题
// http(https)://前端运行的服务器所在ip/前端模块名
base: `${env.VITE_PREFIX}${getLocalIP()}:${env.VITE_SERVER_PORT}/${baseUrl}`
};
},
async configResolved(config) {
const env = config.env;
// const registerOptions: RegisterOptions = {
// serverList: env.VITE_NACOS_ADDRESS || options?.serverList!,
// serviceName: env.VITE_SERVER_NAME || options?.serviceName!,
// port: Number(env.VITE_SERVER_PORT || options?.port),
// namespace: options?.namespace || 'public'
// };
//
// // 验证必要配置
// if (!registerOptions.serverList) {
// throw new Error('[nacos-cluster-federation] 缺少Nacos服务列表配置请设置VITE_NACOS_ADDRESS环境变量或插件选项');
// }
//
// if (!registerOptions.serviceName) {
// throw new Error('[nacos-cluster-federation] 缺少服务名称配置请设置VITE_SERVER_NAME环境变量或插件选项');
// }
// if (!registerOptions.port) {
// throw new Error('[nacos-cluster-federation] 缺少服务端口配置请设置VITE_SERVER_PORT环境变量或插件选项');
// }
// // 初始化Nacos实例
// nacosInstance = await nacosServiceManage.create(registerOptions);
//
// const baseUrl = await nacosInstance.getConfig("baseUrl", "DEFAULT_GROUP")
// const baseUrlConfig = baseUrl ? JSON.parse(baseUrl) : {};
//
// baseUrlConfig.httpTimeOut = await nacosInstance.getConfig("httpTimeOut", "DEFAULT_GROUP")
//
//
// const customizeEnv: Record<string, string> = {};
// Object.keys(baseUrlConfig).forEach(key => {
// const envKey = `import.meta.env.VITE_${ key.toUpperCase() }`;
// customizeEnv[envKey] = JSON.stringify(baseUrlConfig[key]);
// });
//
// // @ts-ignore
// Object.assign(config.define, customizeEnv);
// Logs(env)
},
async closeBundle() {
// // 服务关闭时注销Nacos实例
// if (nacosInstance) {
// await nacosInstance.destroy();
// }
},
}
}
function Logs(env: Record<string, any>) {
const moduleName = []
for (const key in env) {
if (key.includes("VITE_MODEL_")) {
moduleName.push(env[key]);
}
}
moduleName.length == 0 ? moduleName.push('无') : null
const webUI = env.VITE_NACOS_ADDRESS.split(":")[0]
console.log()
console.log(dedentTemplateString(`
${chalk.blue.bold('微服务工具:')}
${chalk.green('➜')} Naco 配置中心: ${env.VITE_PREFIX}${env.VITE_NACOS_ADDRESS}
${chalk.green('➜')} WebUi ${env.VITE_PREFIX}${webUI}:8085
${chalk.green('➜')} Prometheus 监控: ${env.VITE_PREFIX}${webUI}:9090
${chalk.green('➜')} Grafana 面板: ${env.VITE_PREFIX}${webUI}:3000
${chalk.green('➜')} Traefik 网关: ${env.VITE_PREFIX}${env.VITE_TRAEFIK_ADDRESS}
${chalk.green('➜')} WebUi ${env.VITE_PREFIX}${env.VITE_TRAEFIK_ADDRESS}:8880
${chalk.blue.bold('微前端信息:')}
${chalk.green('➜')} 主模块:${chalk.green(env.VITE_SERVER_NAME)}
${chalk.blue.bold('调用模块:')}
${chalk.green('➜')} [ ${chalk.yellow(moduleName)} ]
`))
console.log()
}
function dedentTemplateString(str: string) {
// 分割成行
const lines = str.split('\n');
// 移除首尾空行
if (lines[0].trim() === '') lines.shift();
if (lines[lines.length - 1].trim() === '') lines.pop();
// 找到最小缩进量
const minIndent = lines.reduce((min, line) => {
if (line.trim() === '') return min; // 跳过空行
// @ts-ignore
const indent = line.match(/^\s*/)[0].length;
return indent < min ? indent : min;
}, Infinity);
// 移除统一缩进
return lines.map(line => line.slice(minIndent)).join('\n');
}

View File

@@ -0,0 +1,5 @@
import { nacosServiceManage } from "./src";
const main = async () => {
const nacos = new nacosServiceManage.create({})
}