From 330ec6dea91972e22d7e9ab0cfa52f7e13fa3a0f Mon Sep 17 00:00:00 2001 From: zhangjf Date: Thu, 19 Feb 2026 21:18:58 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E7=AE=80=E5=8C=96=E5=A4=9A?= =?UTF-8?q?=E7=A7=9F=E6=88=B7=E8=B7=AF=E7=94=B1=E9=85=8D=E7=BD=AE=EF=BC=8C?= =?UTF-8?q?=E5=9F=BA=E4=BA=8E=20Nacos=20=E5=85=83=E6=95=B0=E6=8D=AE?= =?UTF-8?q?=E5=8A=A8=E6=80=81=E5=8C=B9=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 问题:tenant.routing.services 配置在每个服务中重复定义 vip-tenants 解决方案: 1. TenantRoutingProperties 简化 - 移除 services 映射(vip-tenants 列表) - 保留全局配置:enabled, fallback-to-shared, shared-services - 路由逻辑改为基于实例元数据动态匹配 2. 配置简化 - Gateway: 只需全局配置,无需定义各服务的 vip-tenants - 服务实例: 只需在 Nacos metadata 中声明 tenant-group - 负载均衡器: 从实例 metadata 读取 tenant-group 进行匹配 3. 架构变化 修改前:配置文件定义 vip-tenants 列表 修改后:实例注册时声明 tenant-group,负载均衡器动态匹配 示例: 共享实例 metadata: { tenant-group: "" } VIP 实例 metadata: { tenant-group: "TENANT_VIP_001" } 请求匹配 → 路由到对应实例 --- .../config/TenantRoutingProperties.java | 260 +++--------------- .../loadbalancer/TenantAwareLoadBalancer.java | 2 +- ...nantRoutingProperties$DatabaseConfig.class | Bin 1466 -> 0 bytes ...tingProperties$ServiceInstanceConfig.class | Bin 1478 -> 0 bytes ...outingProperties$TenantServiceConfig.class | Bin 3025 -> 0 bytes .../config/TenantRoutingProperties.class | Bin 5725 -> 3757 bytes .../TenantAwareLoadBalancer.class | Bin 11494 -> 11490 bytes ...adbalancer.TenantAwareLoadBalancerTest.xml | 24 +- ...adbalancer.TenantAwareLoadBalancerTest.txt | 2 +- .../src/main/resources/application.yml | 19 +- fund-gateway/target/classes/application.yml | 19 +- fund-sys/src/main/resources/application.yml | 12 +- fund-sys/target/classes/application.yml | 12 +- logs/fund-sys-shared-8100.log | 18 ++ logs/fund-sys-tenant-vip001-8101.log | 18 ++ logs/fund-sys/error.log | 32 +++ logs/fund-sys/info.log | 4 + 17 files changed, 136 insertions(+), 286 deletions(-) delete mode 100644 fund-common/target/classes/com/fundplatform/common/config/TenantRoutingProperties$DatabaseConfig.class delete mode 100644 fund-common/target/classes/com/fundplatform/common/config/TenantRoutingProperties$ServiceInstanceConfig.class delete mode 100644 fund-common/target/classes/com/fundplatform/common/config/TenantRoutingProperties$TenantServiceConfig.class diff --git a/fund-common/src/main/java/com/fundplatform/common/config/TenantRoutingProperties.java b/fund-common/src/main/java/com/fundplatform/common/config/TenantRoutingProperties.java index 3f3e459..3e5e514 100644 --- a/fund-common/src/main/java/com/fundplatform/common/config/TenantRoutingProperties.java +++ b/fund-common/src/main/java/com/fundplatform/common/config/TenantRoutingProperties.java @@ -3,24 +3,36 @@ package com.fundplatform.common.config; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; -import java.util.*; +import java.util.Arrays; +import java.util.List; /** * 多租户路由配置属性 * - *

支持从 YAML 配置文件读取租户路由配置

+ *

租户路由基于 Nacos 服务实例的 metadata.tenant-group 元数据进行匹配

* - *

配置示例:

+ *

工作原理:

+ *
+ * 1. 服务实例注册到 Nacos 时,在 metadata 中声明 tenant-group
+ *    - 共享实例: tenant-group 为空或不存在
+ *    - VIP 实例: tenant-group = "TENANT_VIP_001"
+ * 
+ * 2. Gateway 从请求中提取租户组,负载均衡器匹配实例元数据
+ *    - 请求 tenantGroup = "TENANT_VIP_001" → 路由到匹配的 VIP 实例
+ *    - 无匹配实例 → 回退到共享实例
+ * 
+ * + *

配置示例:

*
  * tenant:
  *   routing:
  *     enabled: true
- *     default-tenant-id: 1
- *     services:
- *       fund-sys:
- *         vip-tenants:
- *           - TENANT_VIP_001
- *         fallback-to-shared: true
+ *     tenant-header: X-Tenant-Id
+ *     default-tenant-id: "1"
+ *     fallback-to-shared: true
+ *     shared-services:
+ *       - fund-gateway
+ *       - fund-report
  * 
*/ @Component @@ -49,13 +61,8 @@ public class TenantRoutingProperties { "fund-file" ); - /** 租户服务配置映射 */ - private Map services = new HashMap<>(); - - /** 租户服务配置(旧版兼容) */ - private Map tenantConfigs = new HashMap<>(); - - // Getters and Setters + /** 是否回退到共享实例(当找不到租户专属实例时) */ + private boolean fallbackToShared = true; public boolean isEnabled() { return enabled; @@ -105,24 +112,19 @@ public class TenantRoutingProperties { this.sharedServices = sharedServices; } - public Map getServices() { - return services; + public boolean isFallbackToShared() { + return fallbackToShared; } - public void setServices(Map services) { - this.services = services; - } - - public Map getTenantConfigs() { - return tenantConfigs; - } - - public void setTenantConfigs(Map tenantConfigs) { - this.tenantConfigs = tenantConfigs; + public void setFallbackToShared(boolean fallbackToShared) { + this.fallbackToShared = fallbackToShared; } /** * 构建租户组名称 + * + * @param tenantId 租户 ID + * @return 租户组名称,如 "TENANT_VIP_001" */ public String buildTenantGroup(String tenantId) { if (tenantId == null || tenantId.isEmpty()) { @@ -133,205 +135,11 @@ public class TenantRoutingProperties { /** * 判断是否为共享服务 + * + * @param serviceName 服务名 + * @return 是否为共享服务 */ public boolean isSharedService(String serviceName) { - return sharedServices.contains(serviceName); - } - - /** - * 获取服务的 VIP 租户列表 - */ - public List getVipTenants(String serviceName) { - TenantServiceConfig config = services.get(serviceName); - if (config != null && config.getVipTenants() != null) { - return config.getVipTenants(); - } - return Collections.emptyList(); - } - - /** - * 判断租户是否为某个服务的 VIP 租户 - */ - public boolean isVipTenant(String serviceName, String tenantGroup) { - if (tenantGroup == null || tenantGroup.isEmpty()) { - return false; - } - List vipTenants = getVipTenants(serviceName); - return vipTenants.contains(tenantGroup); - } - - /** - * 判断服务是否启用了共享实例回退 - */ - public boolean isFallbackToShared(String serviceName) { - TenantServiceConfig config = services.get(serviceName); - if (config != null) { - return config.isFallbackToShared(); - } - return true; // 默认启用回退 - } - - /** - * 租户服务配置 - */ - public static class TenantServiceConfig { - /** VIP 租户列表(优先路由到专属实例) */ - private List vipTenants = new ArrayList<>(); - - /** 是否回退到共享实例 */ - private boolean fallbackToShared = true; - - /** 租户 ID */ - private String tenantId; - - /** 服务实例配置 */ - private Map instances = new HashMap<>(); - - /** 数据库配置(一库一租户模式) */ - private DatabaseConfig database; - - // Getters and Setters - - public List getVipTenants() { - return vipTenants; - } - - public void setVipTenants(List vipTenants) { - this.vipTenants = vipTenants != null ? vipTenants : new ArrayList<>(); - } - - public boolean isFallbackToShared() { - return fallbackToShared; - } - - public void setFallbackToShared(boolean fallbackToShared) { - this.fallbackToShared = fallbackToShared; - } - - public String getTenantId() { - return tenantId; - } - - public void setTenantId(String tenantId) { - this.tenantId = tenantId; - } - - public Map getInstances() { - return instances; - } - - public void setInstances(Map instances) { - this.instances = instances; - } - - public DatabaseConfig getDatabase() { - return database; - } - - public void setDatabase(DatabaseConfig database) { - this.database = database; - } - } - - /** - * 服务实例配置 - */ - public static class ServiceInstanceConfig { - /** 服务名 */ - private String serviceName; - - /** 端口号 */ - private int port = 8080; - - /** 实例数(用于负载均衡) */ - private int replicas = 1; - - /** 权重(用于加权负载均衡) */ - private int weight = 1; - - // Getters and Setters - - public String getServiceName() { - return serviceName; - } - - public void setServiceName(String serviceName) { - this.serviceName = serviceName; - } - - public int getPort() { - return port; - } - - public void setPort(int port) { - this.port = port; - } - - public int getReplicas() { - return replicas; - } - - public void setReplicas(int replicas) { - this.replicas = replicas; - } - - public int getWeight() { - return weight; - } - - public void setWeight(int weight) { - this.weight = weight; - } - } - - /** - * 数据库配置(用于一库一租户) - */ - public static class DatabaseConfig { - /** JDBC URL */ - private String url; - - /** 用户名 */ - private String username; - - /** 密码 */ - private String password; - - /** 驱动类名 */ - private String driverClassName = "com.mysql.cj.jdbc.Driver"; - - // Getters and Setters - - public String getUrl() { - return url; - } - - public void setUrl(String url) { - this.url = url; - } - - public String getUsername() { - return username; - } - - public void setUsername(String username) { - this.username = username; - } - - public String getPassword() { - return password; - } - - public void setPassword(String password) { - this.password = password; - } - - public String getDriverClassName() { - return driverClassName; - } - - public void setDriverClassName(String driverClassName) { - this.driverClassName = driverClassName; - } + return sharedServices != null && sharedServices.contains(serviceName); } } diff --git a/fund-common/src/main/java/com/fundplatform/common/loadbalancer/TenantAwareLoadBalancer.java b/fund-common/src/main/java/com/fundplatform/common/loadbalancer/TenantAwareLoadBalancer.java index 7548599..ff778f8 100644 --- a/fund-common/src/main/java/com/fundplatform/common/loadbalancer/TenantAwareLoadBalancer.java +++ b/fund-common/src/main/java/com/fundplatform/common/loadbalancer/TenantAwareLoadBalancer.java @@ -180,7 +180,7 @@ public class TenantAwareLoadBalancer implements ReactorServiceInstanceLoadBalanc // 检查是否启用回退到共享实例 boolean fallbackEnabled = true; if (routingProperties != null) { - fallbackEnabled = routingProperties.isFallbackToShared(serviceId); + fallbackEnabled = routingProperties.isFallbackToShared(); } if (!fallbackEnabled) { diff --git a/fund-common/target/classes/com/fundplatform/common/config/TenantRoutingProperties$DatabaseConfig.class b/fund-common/target/classes/com/fundplatform/common/config/TenantRoutingProperties$DatabaseConfig.class deleted file mode 100644 index 2030c3a0bffae3d56c2bb9049ec7accec70fc8fd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1466 zcmb`FTTc@~6vzM5`(>e6xv6+V)V4@B@d2Y4jDmosmPU~GX*-l{w!1hx#rUl>k(l`4 z2k=7~&kRkA+wF_-WzL+LGyn5DbIz~dKYjvu2|I@v;%Ou-B#~m6>GCsfyWH#8`^~Os zDTdT@$8*#sL%dWz${@qA*z$YT-sj+>TWxi#-FCB8-ImUokXhuAPs6e>g=vP@6lWjz zz4ocg)v+&oHXZbQkBoQhbnHXnaZkPX`^xb;4e6f>sT>ikY;(n%JP=zZjG@pr>RT=k zf;#UBhGK2Rd7vac-_W^dEL_ov<9+FB|Eh&q?PvOdkRH9%hw~N|%;71?eez|SA+hDR zNwrXOJW=oWnnE7(rfc%}E$$w1>1cm&m{2E9z_45UFXe5}4|jz6AYF#pQhB^Ex(^2T zbEWa?@)33Iolqx!yTK*ZsDvcCyw)64O`cX5d34tp7O(F{UXkuDntA!VDP-(0ci{JB zOYAuMqn0N0va0(;4XBGi?fHR9;}OI9zkM8o^~UprG+=^ioc?=IaF@Cpqp?oVN^?NF znC`!^)N*5KFv(`Y^t{u$NZwP5V`TG{?_iZ;`W)G>CiohyMv^Tf!Pan{eCr||7P7WX6#vO{%44Y&%d#bW}jzT#7I+E;(QJBAo z8YpOWT`8m5DKR5QGc89l6~j!=BAM<*GZn*3o0RXx1Sb9KL#A27v=YfQ7iM}D$#g%O zX)esPLrl99nDhh;nHCJwgGi=qnCWdK)5B<{vC*j$)BXe|J&i-ADPoGFL~9IXRE%B4 O8rd8L<*vVQ;}?*kkF diff --git a/fund-common/target/classes/com/fundplatform/common/config/TenantRoutingProperties$ServiceInstanceConfig.class b/fund-common/target/classes/com/fundplatform/common/config/TenantRoutingProperties$ServiceInstanceConfig.class deleted file mode 100644 index e3ca2f79fb3a95ca03ddfad6ae6930f349122031..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1478 zcmb`G&2AD=6vzJ;2ByHYQ2M2|+NxE7*5b;Irip1{BtdN2rmm;c3rqtuWad&|!v}Gp ziH(U1AHbJ!V`Ab${oes8;;=Dp?z!j8`S{&)&V2sy@e_b2c#uT`NfU;J6w(5PGyB}G zc5JUzeQ|Oo9VL){?0T-+6-buK#~I8ZVtKMnkpTP@wT&A4!V2mQ=$A3|JRS#^XsoKU{;I^%<7}?E{eOtv&hWA7oK04 z_ZtfMj;pj-gJ@jCDmC^5*N2&_lsa>&@*WFsc}YMg(uIMaW5KCW@C~ffOJnU)>gS{@ zTNhZk#L)jLL=q`sL>ZO`Uc(GJyMDaD24hof7KLDMZt>J<$C=DGG>kC`GnVN#HtF>z z(<+-Z$&_O~YYLMd#E2;uG2Mw}nw@0I$1>fGXPTX4S|HQn6ehhCBc^=BbT5{vFv+wM s%T$VI8e5^k>{iLNHibzq=!mIErXl9%4j^#=i39u-g=cox_GZ1gSQUrNc;20P-{1SpJ2Uy=@t5BK zn8SM+B#_jQ%Ap5ofx&z7P?j6g+$rDLyrvN*Y};grCzH|fAN8s$0L zmiMcM<7S{C*^4Z48v1hR$AG|6-P$W}ADCPF4e4%M_FkEWJTc_@cGhWT+@HX56<{=u46 z+m*JyB_K95j0z03S1n3scUkUdFydoBmBTpB2=p4J<4UuxGs-|H3g>!3&gO6qPYaxK zJ@^Wp=lyA(Xf@YnF5b)wIb6gg&fJo&+?0+kaI4y-A~ymTz8KTQtQk9|bPsHLErc+Y z7&Dzbvt06FXd1JLnbj>_uv3*lxd2kdOwC(cBpj$3roM8px2fA}ar7ib#SGOt2)o7GhIu$u@I13%+&y%~s zV@veTZ>i#jT9>6tzJ98tN1Aeki<*<@p*?9dfC|r1u5xtMtOc?UHGO#(hB`+APSh5Y zPq$TYay90N9|FsVMO9o1-i;Xl2e796%|k9wp*&COrcis<_PkVlLXH)5re+=3b$!85 zC;F^k7#+9yHC5ic1y^*(U9=omLsj5V_VUIr17-1#Wg1pwLvR z`Reo^H)yEwe(KV9Y+(wF#@hLIWcf6M&vHV&rVx0BqXY{4<1^>|=1b)De7RcRFY`O! z9on5iJHqJ)p2w?f^GLCM!1go7 z|AH^&*C^7G^Zj5FC5{B9@GN7h7-f!BjGofx$b8Bl2`EPj0r)nwxF=Zbr}P}ZU*qDW z=j0>yD)Euh)FWt9A0yL-Rd6ZKr~1DI_>Mw+@A)0`*93mKIuPx7&hmkkrNBrp&@zrh zMML&qq9xdCc(K*L%+Ec%mD%}puW-$EyvjD3;@~Ho6i)HSm=pzG!|R--7SFO**y+*) zMQ*r81=~gmnZ^s1!wme-3yn*Q$rirNRzr-@l;LQ%EsUu88nSLeHZznLR z>T5Bb^O$DwR^T>V3a6t?zsE57V!us~b9g5xh1Hf`Wb-OB8QT0m$oA(6Z2pO8Z+@Cv zez{FMPFg?UJdHPSlWlZ&&qt~MilG)*z^ynX&qtN~o3#I&K&#GcOUa95OJb320u?NI a{cS9>KSO&Kx3Pj=++qJN`@8Jd(EA^$wbrr# diff --git a/fund-common/target/classes/com/fundplatform/common/config/TenantRoutingProperties.class b/fund-common/target/classes/com/fundplatform/common/config/TenantRoutingProperties.class index b7e6ac503a9188781972db7a4943d2d599aac8c4..354ff206da66fbd938719e05d48c3c3ef3dabda8 100644 GIT binary patch literal 3757 zcmbVP-B%M=5T8v#62eL#BBD}Sts;g(s;y{6QRG8tBT^Gk&{j9eO;|~G*WDY0ephSz z*w=dcFZf{3DYo^T_R)v_Q9b?LyZIv7^q~)O_s*R=^Sd*@nTdb@^Xe}mnx-E@nqzsbT;wjAMVRk)HZ+^%Qq11cv&;b)*Enz%6G`3SFx{Eb3x>waHdE+$)A7Yt zEK|7Blh=yHjF#O@niu})Ixph_JS4AUWn zx^9oT7{?ZK;^>G|{zpYY4h)*Djr-k68 zN?jBV(WyN#cS9JaQ3P6KaIBzl_DI_a(-?qAiOnq2=3z=87*deeivWL`-V4#0sxcX| zY1`Iz9F^Xuvv{)Rh!84tV&qOkoZKW&o@xr^q=0x%C6)GxuMbq}p&)XW)D5;&E@haV z7Mu|IH?vxCRkL-muLuKtLr40~C;uN2MVfVIuF5*(2)v6pjMqegk#&)`p6iTnn7L(8 zs>C?6;WJub6-HHw*EdpmoFsLPqfyv%(^^$LFwzj0ik|S&S2MYX(0w%nd&IramVySV z_d>N<7K7ewa|Ohp*di1bUEV{Ht#2-{SIz|(ATf93UcgW~Nco28x>g=YU= zBJ5aIGQ@OezNG;713{~_CsWpoIX5dZUJ4C+C8ae_5gMU2mDcGp$`0HqSsXQU7n|9+ z`RSEpIsz7ds?s9O3f226X*7$9;^vBl_BW$BEQSu6RVhbc^}&+1$!1I=tMOYp-@uo{ z(J}?6J@B-`@nl?>%g!k}-M$n@Kf112m4b=t&YpZIGYuvhCKArIJCW zLSb}xuIUDV?lDlUa@0|mBg41$r5gCPJ(lj^A)?_OK8YFuz zrV$Je_4X}UcCCtu8Priq=WH?>B+&aC1=C)^q*yczaJZ;Bj+o+4(BoSk=>ZnmE5-+Ib<6K4^f5&6-3uAUEdK7t z?q^t2uujK+qrP}xm-^$efn7Qrj}7k9P(1e5E**=hC)+xxdJqSRh)Iif2IEC`0(K>;YC6?3nSIb9kt6>VAv;LIfd0H zwBtwc!aCH5lrA6!i_${Bt5vZetczGHbQO0y6l?^h8Mt$U*NExx3mi6LN_t|Nqj_kF z$k#EABHqoImf`HNA)-ZKSOz8F= zaXQwF>8>ZH54|xRYsPdRn6x&SL_O3nMI@$=JTVP7W6F7AN_t}&ZpO3$OnMtkqBd)o zx+JFSv;<1HWqLzWqmfq=&De^b*p|T-f5pg6X=E8jUCx7F69S`OR*cJe3m6}^!RYpl z6mGcX7{nT_qgEyAv}a|WZ>~)4iFU=iG8>wRi*g$%ciNy7-K-YL9*Jp{ZdHmaQ+fR0 z=xd6MzNFjFC>Xo*BL0#-!S3EG$`Fabx*_S7t@SbfOYj6CeA?h}u+|C&DKAr3z^wrJ zx_s59ke9AEka4%JS1PUt(5NO}Q%o$Qf6xOTJ)@4mbH9j?>(=dEKQ_109i?&#%Yy9eq!S{sm)65>o&G literal 5725 zcmcIo{d*Kw6@DjQyV)JGun;gLLWw|=4Pj|*3mBk=5FnVOlnrSE)^?JeBtv#*mYLZ= z>R0QxtsiZz+G=a7*0w4t)dna&`uGz+_&53Z_};m*v$K;)f5=mR**kM`?m6#y&$(yL z&A85777p$slm<#7^tD@Vkp*so^ z{KITXFDg(jCDEZE{p6rj9vmwQC!1AlK~{n4dM3{5T2Z$ZWXJ0O3$9J8LrH8?&=G7u zV_VgVu&`al4q;)7wBX65nIyW1U}|)7cyj7#q1vsYN2qozh|LAPqS=~j*-7joz6XTH zeJb_{jZ9IW*QzB~VjU|c@RB5#LK8xo)&Q1Ku> z%dnh9&DM(r-Cj241pAw#+6g>NqC9Fn6K6GNaYC!4@DQ4Xr6Vei;&Td;PGBPwvO!hP z;V~7*h1w=b`G{8pP71wvRK@2pOx6u&v|MplylP_RM<_ZqdV2Wc_*9C1Mpc}~8A4gm zT`yrlE);C*^MmxN(@(yzJ*MKE*pzmyixpPbi00^7R`mu)h9+Bt;VSiD<-f)P12G-QF0CYvg zk|?08;hfe=rCDumY04^ibxqp^W5Lwis;$#vs4j)KV$uT-4 zJhqHjMdHhh8>T*4Ezj!qlxTsXr&)7aX9%TPThm z@GLEyYHY70hl*>2qCLpm*L&LjHI*5YQ;`SZI=XqQUxxLn77T4~Ow3B9mYn;e>anhb ziL^i(^vo>{7)94qa5XB6v9-Y>h&$X~DW(Ml0jg{`8UacGwBo~#41-TQ#W14F&e8pYgw)sFkSt+o38{zC3lKER5`lwi&GyqX{W7X z>+{BSeyC-D2RY`6iIfpf>W(|1i*D>B@H@KPVAnsTx!NoT4+$cH_gD`bH07VnF>g$n z^DXiuoMhaNYikwRxSb^aprA=nGHtk#!k_Tx1pXpMo}2s#dLLmg;fA?vE$Mk#zC_0< z=(IM+>6SCtYyKe*D@x;MHM3aKoxS6hwN$MPtu2?3#P=XKPBDiu>g$iJVc#SCvKI_j zXUfzu)8r^u(i}%jtXpNp`P4{~na)Pmd17O^OW+pA<~5YWMVi1J1-m|8mGvZP;gulC z>l3_R-iP?qByKI<%DacR@?znwd;FCXITVyUS(*BrtAlrw?;M~*@7wvziy40{?s$XG z3fBv{+fZ^Xt4QRsn^uv@Ww)-PGnf6;Dt6|wpI*i8T-!g;JJX!qTbOCd?k~)=X8Q_v zkXyx}+~4ug%xyffio^BCp$|Mf75?r=JAZr7g57-TMF#uOi9U4mhO&pZlOa6J8T|;Z zQcIO+V8h{?0+%;Ri8RkuAZ^Wkh{O#(G(i{_fq)^_H@60xt5~L%2=fG2L2FyCn<3Vr ziMy(}w6@};XJNb4@+}DKA_)qvQLB|_n~Q?$IKy9IHjF8AlY%g&(O66?c$Qj?ml77x>xV=;9`F-^o``bs>e&M2nGiRr>Rm_%^~n09!Wc<+kgX=fDE z#aK*VjmNYzifM+JF0F$}9Fzc4kB8}Nv6yy8F+CNF>Fe>Bc1JO3#5B7OCUIy3OnW>` z&*2%rB*OJ{D2hps!6Xm;^R)O4d=t%HU7e>fS?jE;Ljqf~cT!}n8^m^H9c;3Ze2Y8e zF>K>yzg$QAApam8ew)JY;JY!X4@Xg#*G2t38u>n6h(moiiux*1+v}i~?JkV^D9k$Q zqXFs{Df~e!>Y*s=>c^t~VLWQD<%PLkA?jz>K`ond81?Zm>f;Tle}o_VT+jN+?HRa- z)W9vAcpYuolQ(m>aEfnd@8S!92u8x_;0ip)zV$phSRhyVCRmsp1OymSZ>~dRu3#osKCn%>J@Ym;V%5tbA6w*6u9p7U3Xv` zHGW1$hEH#CeVYh>?cg`~EnkxK{tn(Hd>Xo^zhfd(+HDq&m+<-XY%&7arg-D&~joKzTu7w9QX@V^&O9Gh>6vjwO4o2hLhA z9JzBMpnECK)k delta 188 zcmXAiyAHts6o$X!(xj#?MB>)QrPMokkB$cKV6zxZmaY=<22EdMSh4e1u~47N=>yaGlo)^hOWkx#a1m(U!Au8(xjuwzuyo? P#W=XR6)8m@_m%qszYHr~ diff --git a/fund-common/target/surefire-reports/TEST-com.fundplatform.common.loadbalancer.TenantAwareLoadBalancerTest.xml b/fund-common/target/surefire-reports/TEST-com.fundplatform.common.loadbalancer.TenantAwareLoadBalancerTest.xml index 0e68515..9c897c6 100644 --- a/fund-common/target/surefire-reports/TEST-com.fundplatform.common.loadbalancer.TenantAwareLoadBalancerTest.xml +++ b/fund-common/target/surefire-reports/TEST-com.fundplatform.common.loadbalancer.TenantAwareLoadBalancerTest.xml @@ -1,5 +1,5 @@ - + @@ -13,7 +13,7 @@ - + @@ -29,7 +29,7 @@ - + @@ -56,17 +56,17 @@ - - - + + - - + \ No newline at end of file diff --git a/fund-common/target/surefire-reports/com.fundplatform.common.loadbalancer.TenantAwareLoadBalancerTest.txt b/fund-common/target/surefire-reports/com.fundplatform.common.loadbalancer.TenantAwareLoadBalancerTest.txt index 42d9b9d..b17f2d0 100644 --- a/fund-common/target/surefire-reports/com.fundplatform.common.loadbalancer.TenantAwareLoadBalancerTest.txt +++ b/fund-common/target/surefire-reports/com.fundplatform.common.loadbalancer.TenantAwareLoadBalancerTest.txt @@ -1,4 +1,4 @@ ------------------------------------------------------------------------------- Test set: com.fundplatform.common.loadbalancer.TenantAwareLoadBalancerTest ------------------------------------------------------------------------------- -Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.567 s -- in com.fundplatform.common.loadbalancer.TenantAwareLoadBalancerTest +Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.310 s -- in com.fundplatform.common.loadbalancer.TenantAwareLoadBalancerTest diff --git a/fund-gateway/src/main/resources/application.yml b/fund-gateway/src/main/resources/application.yml index cee858a..cc5e417 100644 --- a/fund-gateway/src/main/resources/application.yml +++ b/fund-gateway/src/main/resources/application.yml @@ -128,7 +128,7 @@ logging: org.springframework.cloud.gateway: DEBUG com.fundplatform.common.loadbalancer: DEBUG -# 多租户路由配置 +# 多租户路由配置(Gateway 全局配置) tenant: routing: enabled: true @@ -136,21 +136,10 @@ tenant: tenant-group-header: X-Tenant-Group group-separator: TENANT_ default-tenant-id: "1" + # 共享服务列表(不需要租户路由的服务) shared-services: - fund-gateway - fund-report - fund-file - services: - fund-sys: - vip-tenants: - - TENANT_VIP_001 - - TENANT_VIP_002 - fallback-to-shared: true - fund-cust: - vip-tenants: - - TENANT_VIP_001 - fallback-to-shared: true - fund-proj: - vip-tenants: - - TENANT_VIP_001 - fallback-to-shared: true + # 默认回退策略 + fallback-to-shared: true diff --git a/fund-gateway/target/classes/application.yml b/fund-gateway/target/classes/application.yml index cee858a..cc5e417 100644 --- a/fund-gateway/target/classes/application.yml +++ b/fund-gateway/target/classes/application.yml @@ -128,7 +128,7 @@ logging: org.springframework.cloud.gateway: DEBUG com.fundplatform.common.loadbalancer: DEBUG -# 多租户路由配置 +# 多租户路由配置(Gateway 全局配置) tenant: routing: enabled: true @@ -136,21 +136,10 @@ tenant: tenant-group-header: X-Tenant-Group group-separator: TENANT_ default-tenant-id: "1" + # 共享服务列表(不需要租户路由的服务) shared-services: - fund-gateway - fund-report - fund-file - services: - fund-sys: - vip-tenants: - - TENANT_VIP_001 - - TENANT_VIP_002 - fallback-to-shared: true - fund-cust: - vip-tenants: - - TENANT_VIP_001 - fallback-to-shared: true - fund-proj: - vip-tenants: - - TENANT_VIP_001 - fallback-to-shared: true + # 默认回退策略 + fallback-to-shared: true diff --git a/fund-sys/src/main/resources/application.yml b/fund-sys/src/main/resources/application.yml index ab78c4e..73ec11a 100644 --- a/fund-sys/src/main/resources/application.yml +++ b/fund-sys/src/main/resources/application.yml @@ -76,22 +76,18 @@ logging: pattern: console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n" -# 多租户路由配置 +# 多租户路由配置(服务实例只需声明自己的租户组) tenant: routing: enabled: true tenant-header: X-Tenant-Id tenant-group-header: X-Tenant-Group - group-separator: TENANT_ default-tenant-id: "1" + # 共享服务列表 shared-services: - fund-gateway - fund-report - fund-file - services: - fund-sys: - vip-tenants: - - TENANT_VIP_001 - - TENANT_VIP_002 - fallback-to-shared: true + # 回退到共享实例 + fallback-to-shared: true diff --git a/fund-sys/target/classes/application.yml b/fund-sys/target/classes/application.yml index ab78c4e..73ec11a 100644 --- a/fund-sys/target/classes/application.yml +++ b/fund-sys/target/classes/application.yml @@ -76,22 +76,18 @@ logging: pattern: console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n" -# 多租户路由配置 +# 多租户路由配置(服务实例只需声明自己的租户组) tenant: routing: enabled: true tenant-header: X-Tenant-Id tenant-group-header: X-Tenant-Group - group-separator: TENANT_ default-tenant-id: "1" + # 共享服务列表 shared-services: - fund-gateway - fund-report - fund-file - services: - fund-sys: - vip-tenants: - - TENANT_VIP_001 - - TENANT_VIP_002 - fallback-to-shared: true + # 回退到共享实例 + fallback-to-shared: true diff --git a/logs/fund-sys-shared-8100.log b/logs/fund-sys-shared-8100.log index 4259cb1..715d6da 100644 --- a/logs/fund-sys-shared-8100.log +++ b/logs/fund-sys-shared-8100.log @@ -1102,3 +1102,21 @@ java.lang.NullPointerException: Cannot invoke "com.zaxxer.hikari.HikariPoolMXBea at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) at java.base/java.lang.Thread.run(Thread.java:1583) +2026-02-19 21:16:06.988 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - === HikariCP 连接池状态 === +2026-02-19 21:16:06.991 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - 连接池名称: FundSysHikariPool +2026-02-19 21:16:06.991 [scheduling-1] [] ERROR o.s.s.support.TaskUtils$LoggingErrorHandler - Unexpected error occurred in scheduled task +java.lang.NullPointerException: Cannot invoke "com.zaxxer.hikari.HikariPoolMXBean.getActiveConnections()" because the return value of "com.zaxxer.hikari.HikariDataSource.getHikariPoolMXBean()" is null + at com.fundplatform.sys.config.HikariMonitorConfig.monitorHikariPool(HikariMonitorConfig.java:38) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.scheduling.support.ScheduledMethodRunnable.runInternal(ScheduledMethodRunnable.java:130) + at org.springframework.scheduling.support.ScheduledMethodRunnable.lambda$run$2(ScheduledMethodRunnable.java:124) + at io.micrometer.observation.Observation.observe(Observation.java:499) + at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:124) + at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) + at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:358) + at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) + at java.base/java.lang.Thread.run(Thread.java:1583) diff --git a/logs/fund-sys-tenant-vip001-8101.log b/logs/fund-sys-tenant-vip001-8101.log index a31a855..32c1d24 100644 --- a/logs/fund-sys-tenant-vip001-8101.log +++ b/logs/fund-sys-tenant-vip001-8101.log @@ -1066,3 +1066,21 @@ java.lang.NullPointerException: Cannot invoke "com.zaxxer.hikari.HikariPoolMXBea at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) at java.base/java.lang.Thread.run(Thread.java:1583) +2026-02-19 21:14:07.541 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - === HikariCP 连接池状态 === +2026-02-19 21:14:07.550 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - 连接池名称: FundSysHikariPool +2026-02-19 21:14:07.551 [scheduling-1] [] ERROR o.s.s.support.TaskUtils$LoggingErrorHandler - Unexpected error occurred in scheduled task +java.lang.NullPointerException: Cannot invoke "com.zaxxer.hikari.HikariPoolMXBean.getActiveConnections()" because the return value of "com.zaxxer.hikari.HikariDataSource.getHikariPoolMXBean()" is null + at com.fundplatform.sys.config.HikariMonitorConfig.monitorHikariPool(HikariMonitorConfig.java:38) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.scheduling.support.ScheduledMethodRunnable.runInternal(ScheduledMethodRunnable.java:130) + at org.springframework.scheduling.support.ScheduledMethodRunnable.lambda$run$2(ScheduledMethodRunnable.java:124) + at io.micrometer.observation.Observation.observe(Observation.java:499) + at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:124) + at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) + at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:358) + at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) + at java.base/java.lang.Thread.run(Thread.java:1583) diff --git a/logs/fund-sys/error.log b/logs/fund-sys/error.log index b6f0c8f..8e96c1f 100644 --- a/logs/fund-sys/error.log +++ b/logs/fund-sys/error.log @@ -1438,3 +1438,35 @@ java.lang.NullPointerException: Cannot invoke "com.zaxxer.hikari.HikariPoolMXBea at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) at java.base/java.lang.Thread.run(Thread.java:1583) +2026-02-19 21:14:07.551 [scheduling-1] [] ERROR o.s.s.support.TaskUtils$LoggingErrorHandler - Unexpected error occurred in scheduled task +java.lang.NullPointerException: Cannot invoke "com.zaxxer.hikari.HikariPoolMXBean.getActiveConnections()" because the return value of "com.zaxxer.hikari.HikariDataSource.getHikariPoolMXBean()" is null + at com.fundplatform.sys.config.HikariMonitorConfig.monitorHikariPool(HikariMonitorConfig.java:38) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.scheduling.support.ScheduledMethodRunnable.runInternal(ScheduledMethodRunnable.java:130) + at org.springframework.scheduling.support.ScheduledMethodRunnable.lambda$run$2(ScheduledMethodRunnable.java:124) + at io.micrometer.observation.Observation.observe(Observation.java:499) + at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:124) + at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) + at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:358) + at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) + at java.base/java.lang.Thread.run(Thread.java:1583) +2026-02-19 21:16:06.991 [scheduling-1] [] ERROR o.s.s.support.TaskUtils$LoggingErrorHandler - Unexpected error occurred in scheduled task +java.lang.NullPointerException: Cannot invoke "com.zaxxer.hikari.HikariPoolMXBean.getActiveConnections()" because the return value of "com.zaxxer.hikari.HikariDataSource.getHikariPoolMXBean()" is null + at com.fundplatform.sys.config.HikariMonitorConfig.monitorHikariPool(HikariMonitorConfig.java:38) + at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:103) + at java.base/java.lang.reflect.Method.invoke(Method.java:580) + at org.springframework.scheduling.support.ScheduledMethodRunnable.runInternal(ScheduledMethodRunnable.java:130) + at org.springframework.scheduling.support.ScheduledMethodRunnable.lambda$run$2(ScheduledMethodRunnable.java:124) + at io.micrometer.observation.Observation.observe(Observation.java:499) + at org.springframework.scheduling.support.ScheduledMethodRunnable.run(ScheduledMethodRunnable.java:124) + at org.springframework.scheduling.support.DelegatingErrorHandlingRunnable.run(DelegatingErrorHandlingRunnable.java:54) + at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:572) + at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:358) + at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:305) + at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) + at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) + at java.base/java.lang.Thread.run(Thread.java:1583) diff --git a/logs/fund-sys/info.log b/logs/fund-sys/info.log index bf83d99..369e7d4 100644 --- a/logs/fund-sys/info.log +++ b/logs/fund-sys/info.log @@ -324,3 +324,7 @@ 2026-02-19 21:09:07.550 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - 连接池名称: FundSysHikariPool 2026-02-19 21:11:06.988 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - === HikariCP 连接池状态 === 2026-02-19 21:11:06.996 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - 连接池名称: FundSysHikariPool +2026-02-19 21:14:07.541 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - === HikariCP 连接池状态 === +2026-02-19 21:14:07.550 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - 连接池名称: FundSysHikariPool +2026-02-19 21:16:06.988 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - === HikariCP 连接池状态 === +2026-02-19 21:16:06.991 [scheduling-1] [] INFO com.fundplatform.sys.config.HikariMonitorConfig - 连接池名称: FundSysHikariPool