From b060d8ee164b9a697fe3d9b75c7f599e57214cb8 Mon Sep 17 00:00:00 2001 From: zlt Date: Wed, 4 Sep 2019 12:08:38 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E8=87=AA=E5=AE=9A=E4=B9=89?= =?UTF-8?q?=E8=B4=9F=E8=BD=BD=E5=9D=87=E8=A1=A1=E8=A7=84=E5=88=99=EF=BC=8C?= =?UTF-8?q?=E5=AE=9E=E7=8E=B0=E6=9C=8D=E5=8A=A1=E5=AE=9E=E4=BE=8B=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=8F=B7=E9=9A=94=E7=A6=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/resources/bootstrap.yml | 4 +- .../common/config/DefaultWebMvcConfig.java | 21 +++++- .../common/constant/CommonConstant.java | 8 +++ .../common/constant/ConfigConstants.java | 14 ++++ .../context/LbIsolationContextHolder.java | 25 ++++++++ .../interceptor/LbIsolationInterceptor.java | 26 ++++++++ .../zlt-ribbon-spring-boot-starter/pom.xml | 5 ++ .../common/ribbon/RibbonAutoConfigure.java | 13 +++- .../ribbon/config/FeignInterceptorConfig.java | 1 + .../common/ribbon/config/RuleConfigure.java | 16 +++++ .../ribbon/rule/CustomIsolationRule.java | 64 +++++++++++++++++++ .../main/resources/application-dev.properties | 5 +- .../gateway/filter/pre/LbIsolationFilter.java | 52 +++++++++++++++ .../src/main/resources/static/module/admin.js | 4 ++ .../main/resources/static/module/config.js | 1 + 15 files changed, 254 insertions(+), 5 deletions(-) create mode 100644 zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/constant/ConfigConstants.java create mode 100644 zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/context/LbIsolationContextHolder.java create mode 100644 zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/interceptor/LbIsolationInterceptor.java create mode 100644 zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/config/RuleConfigure.java create mode 100644 zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/rule/CustomIsolationRule.java create mode 100644 zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/filter/pre/LbIsolationFilter.java diff --git a/zlt-business/user-center/src/main/resources/bootstrap.yml b/zlt-business/user-center/src/main/resources/bootstrap.yml index 1f06082..897e3c5 100644 --- a/zlt-business/user-center/src/main/resources/bootstrap.yml +++ b/zlt-business/user-center/src/main/resources/bootstrap.yml @@ -16,4 +16,6 @@ spring: shared-dataids: common.yml refreshable-dataids: common.yml discovery: - server-addr: ${zlt.nacos.server-addr} \ No newline at end of file + server-addr: ${zlt.nacos.server-addr} +# metadata: +# version: zlt \ No newline at end of file diff --git a/zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/config/DefaultWebMvcConfig.java b/zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/config/DefaultWebMvcConfig.java index e8472c1..36a715a 100644 --- a/zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/config/DefaultWebMvcConfig.java +++ b/zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/config/DefaultWebMvcConfig.java @@ -1,11 +1,15 @@ package com.central.common.config; +import com.central.common.constant.ConfigConstants; import com.central.common.feign.UserService; +import com.central.common.interceptor.LbIsolationInterceptor; import com.central.common.interceptor.TenantInterceptor; import com.central.common.interceptor.TraceInterceptor; import com.central.common.resolver.ClientArgumentResolver; import com.central.common.resolver.TokenArgumentResolver; +import com.central.log.properties.TraceProperties; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; import org.springframework.web.method.support.HandlerMethodArgumentResolver; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; @@ -23,6 +27,12 @@ public class DefaultWebMvcConfig extends WebMvcConfigurationSupport { @Autowired private UserService userService; + @Autowired + private TraceProperties traceProperties; + + @Value("${" + ConfigConstants.CONFIG_RIBBON_ISOLATION_ENABLED + ":false}") + private boolean enableIsolation; + /** * 配置SpringMVC拦截器,添加租户拦截器 */ @@ -31,8 +41,15 @@ public class DefaultWebMvcConfig extends WebMvcConfigurationSupport { //租户拦截器 registry.addInterceptor(new TenantInterceptor()).addPathPatterns("/**"); - //日志链路追踪拦截器 - registry.addInterceptor(new TraceInterceptor()).addPathPatterns("/**"); + if (traceProperties.getEnable()) { + //日志链路追踪拦截器 + registry.addInterceptor(new TraceInterceptor()).addPathPatterns("/**"); + } + + if (enableIsolation) { + //负债均衡隔离规则拦截器 + registry.addInterceptor(new LbIsolationInterceptor()).addPathPatterns("/**"); + } super.addInterceptors(registry); } diff --git a/zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/constant/CommonConstant.java b/zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/constant/CommonConstant.java index c0ef79f..b8d70c6 100644 --- a/zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/constant/CommonConstant.java +++ b/zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/constant/CommonConstant.java @@ -117,4 +117,12 @@ public interface CommonConstant { * 日志链路追踪id日志标志 */ String LOG_TRACE_ID = "traceId"; + /** + * 负载均衡策略-版本号 信息头 + */ + String Z_L_T_VERSION = "z-l-t-version"; + /** + * 注册中心元数据 版本号 + */ + String METADATA_VERSION = "version"; } diff --git a/zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/constant/ConfigConstants.java b/zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/constant/ConfigConstants.java new file mode 100644 index 0000000..cd8375c --- /dev/null +++ b/zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/constant/ConfigConstants.java @@ -0,0 +1,14 @@ +package com.central.common.constant; + +/** + * 配置项常量 + * + * @author zlt + * @date 2019/9/3 + */ +public interface ConfigConstants { + /** + * 是否开启自定义隔离规则 + */ + String CONFIG_RIBBON_ISOLATION_ENABLED = "zlt.ribbon.isolation.enabled"; +} diff --git a/zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/context/LbIsolationContextHolder.java b/zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/context/LbIsolationContextHolder.java new file mode 100644 index 0000000..3b430e2 --- /dev/null +++ b/zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/context/LbIsolationContextHolder.java @@ -0,0 +1,25 @@ +package com.central.common.context; + +import com.alibaba.ttl.TransmittableThreadLocal; + +/** + * 负载均衡策略Holder + * + * @author zlt + * @date 2019/9/2 + */ +public class LbIsolationContextHolder { + private static final ThreadLocal VERSION_CONTEXT = new TransmittableThreadLocal<>(); + + public static void setVersion(String version) { + VERSION_CONTEXT.set(version); + } + + public static String getVersion() { + return VERSION_CONTEXT.get(); + } + + public static void clear() { + VERSION_CONTEXT.remove(); + } +} diff --git a/zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/interceptor/LbIsolationInterceptor.java b/zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/interceptor/LbIsolationInterceptor.java new file mode 100644 index 0000000..00db34a --- /dev/null +++ b/zlt-commons/zlt-common-spring-boot-starter/src/main/java/com/central/common/interceptor/LbIsolationInterceptor.java @@ -0,0 +1,26 @@ +package com.central.common.interceptor; + +import cn.hutool.core.util.StrUtil; +import com.central.common.constant.CommonConstant; +import com.central.common.context.LbIsolationContextHolder; +import org.springframework.web.servlet.HandlerInterceptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * 负载均衡隔离规则截器 + * + * @author zlt + * @date 2019/8/5 + */ +public class LbIsolationInterceptor implements HandlerInterceptor { + @Override + public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { + String version = request.getHeader(CommonConstant.Z_L_T_VERSION); + if(StrUtil.isNotEmpty(version)){ + LbIsolationContextHolder.setVersion(version); + } + return true; + } +} diff --git a/zlt-commons/zlt-ribbon-spring-boot-starter/pom.xml b/zlt-commons/zlt-ribbon-spring-boot-starter/pom.xml index 740957c..2e0e0b4 100644 --- a/zlt-commons/zlt-ribbon-spring-boot-starter/pom.xml +++ b/zlt-commons/zlt-ribbon-spring-boot-starter/pom.xml @@ -24,6 +24,11 @@ org.springframework.cloud spring-cloud-starter-netflix-ribbon + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + true + org.apache.httpcomponents httpclient diff --git a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/RibbonAutoConfigure.java b/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/RibbonAutoConfigure.java index be4ec44..68e2b1c 100644 --- a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/RibbonAutoConfigure.java +++ b/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/RibbonAutoConfigure.java @@ -1,9 +1,14 @@ package com.central.common.ribbon; +import com.central.common.constant.ConfigConstants; import com.central.common.ribbon.config.RestTemplateProperties; +import com.central.common.ribbon.config.RuleConfigure; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.cloud.netflix.ribbon.DefaultPropertiesFactory; +import org.springframework.cloud.netflix.ribbon.RibbonClients; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; /** * Ribbon扩展配置类 @@ -12,9 +17,15 @@ import org.springframework.context.annotation.Bean; * @date 2018/11/17 9:24 */ @EnableConfigurationProperties(RestTemplateProperties.class) -public class RibbonAutoConfigure { +public class RibbonAutoConfigure { @Bean public DefaultPropertiesFactory defaultPropertiesFactory() { return new DefaultPropertiesFactory(); } + + @Configuration + @ConditionalOnProperty(value = ConfigConstants.CONFIG_RIBBON_ISOLATION_ENABLED, havingValue = "true") + @RibbonClients(defaultConfiguration = {RuleConfigure.class}) + public class LbIsolationConfig { + } } diff --git a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/config/FeignInterceptorConfig.java b/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/config/FeignInterceptorConfig.java index 35d55fe..49041d7 100644 --- a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/config/FeignInterceptorConfig.java +++ b/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/config/FeignInterceptorConfig.java @@ -29,6 +29,7 @@ public class FeignInterceptorConfig { requestHeaders.add(SecurityConstants.USER_ID_HEADER); requestHeaders.add(SecurityConstants.USER_HEADER); requestHeaders.add(SecurityConstants.ROLE_HEADER); + requestHeaders.add(CommonConstant.Z_L_T_VERSION); } /** diff --git a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/config/RuleConfigure.java b/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/config/RuleConfigure.java new file mode 100644 index 0000000..4f5b7d4 --- /dev/null +++ b/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/config/RuleConfigure.java @@ -0,0 +1,16 @@ +package com.central.common.ribbon.config; + +import com.central.common.ribbon.rule.CustomIsolationRule; +import com.netflix.loadbalancer.IRule; +import org.springframework.context.annotation.Bean; + +/** + * @author zlt + * @date 2019/9/3 + */ +public class RuleConfigure { + @Bean + public IRule isolationRule() { + return new CustomIsolationRule(); + } +} diff --git a/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/rule/CustomIsolationRule.java b/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/rule/CustomIsolationRule.java new file mode 100644 index 0000000..5f17057 --- /dev/null +++ b/zlt-commons/zlt-ribbon-spring-boot-starter/src/main/java/com/central/common/ribbon/rule/CustomIsolationRule.java @@ -0,0 +1,64 @@ +package com.central.common.ribbon.rule; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; +import com.alibaba.cloud.nacos.ribbon.NacosServer; +import com.central.common.constant.CommonConstant; +import com.central.common.context.LbIsolationContextHolder; +import com.netflix.loadbalancer.*; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 自定义隔离随机规则 + * + * @author zlt + * @date 2019/9/3 + */ +public class CustomIsolationRule extends RoundRobinRule { + /** + * 优先根据版本号取实例 + */ + @Override + public Server choose(ILoadBalancer lb, Object key) { + if (lb == null) { + return null; + } + String version = LbIsolationContextHolder.getVersion(); + List targetList = null; + List upList = lb.getReachableServers(); + if (StrUtil.isNotEmpty(version)) { + //取指定版本号的实例 + targetList = upList.stream().filter( + server -> version.equals( + ((NacosServer) server).getMetadata().get(CommonConstant.METADATA_VERSION) + ) + ).collect(Collectors.toList()); + } + + if (CollUtil.isEmpty(targetList)) { + //只取无版本号的实例 + targetList = upList.stream().filter( + server -> { + String metadataVersion = ((NacosServer) server).getMetadata().get(CommonConstant.METADATA_VERSION); + return StrUtil.isEmpty(metadataVersion); + } + ).collect(Collectors.toList()); + } + + if (CollUtil.isNotEmpty(targetList)) { + return getServer(targetList); + } + return super.choose(lb, key); + } + + /** + * 随机取一个实例 + */ + private Server getServer(List upList) { + int nextInt = RandomUtil.randomInt(upList.size()); + return upList.get(nextInt); + } +} diff --git a/zlt-config/src/main/resources/application-dev.properties b/zlt-config/src/main/resources/application-dev.properties index adc12cb..e184944 100644 --- a/zlt-config/src/main/resources/application-dev.properties +++ b/zlt-config/src/main/resources/application-dev.properties @@ -21,4 +21,7 @@ zlt.fdfs.web-url=192.168.28.130 zlt.fdfs.trackerList=${zlt.fdfs.web-url}:22122 ##### 日志链路追踪 -zlt.trace.enable=true \ No newline at end of file +zlt.trace.enable=true + +##### 负载均衡隔离(version隔离,只适用于开发环境) +zlt.ribbon.isolation.enabled=true \ No newline at end of file diff --git a/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/filter/pre/LbIsolationFilter.java b/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/filter/pre/LbIsolationFilter.java new file mode 100644 index 0000000..80ec55b --- /dev/null +++ b/zlt-gateway/zuul-gateway/src/main/java/com/central/gateway/filter/pre/LbIsolationFilter.java @@ -0,0 +1,52 @@ +package com.central.gateway.filter.pre; + +import cn.hutool.core.util.StrUtil; +import com.central.common.constant.CommonConstant; +import com.central.common.constant.ConfigConstants; +import com.central.common.context.LbIsolationContextHolder; +import com.netflix.zuul.ZuulFilter; +import com.netflix.zuul.context.RequestContext; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.cloud.netflix.zuul.filters.support.FilterConstants; +import org.springframework.stereotype.Component; + +/** + * 保存负载均衡隔离值 + * + * @author zlt + * @date 2019/8/13 + */ +@Component +public class LbIsolationFilter extends ZuulFilter { + @Value("${" + ConfigConstants.CONFIG_RIBBON_ISOLATION_ENABLED + ":false}") + private boolean enableIsolation; + + @Override + public String filterType() { + return FilterConstants.PRE_TYPE; + } + + @Override + public int filterOrder() { + return 0; + } + + @Override + public boolean shouldFilter() { + //根据配置控制是否开启过滤器 + return enableIsolation; + } + + @Override + public Object run() { + RequestContext ctx = RequestContext.getCurrentContext(); + String version = ctx.getRequest().getHeader(CommonConstant.Z_L_T_VERSION); + if (StrUtil.isNotEmpty(version)) { + LbIsolationContextHolder.setVersion(version); + } else { + LbIsolationContextHolder.clear(); + } + return null; + } +} diff --git a/zlt-web/back-web/src/main/resources/static/module/admin.js b/zlt-web/back-web/src/main/resources/static/module/admin.js index a240495..b818f62 100644 --- a/zlt-web/back-web/src/main/resources/static/module/admin.js +++ b/zlt-web/back-web/src/main/resources/static/module/admin.js @@ -105,6 +105,10 @@ layui.define(['config', 'layer'], function (exports) { xhr.setRequestHeader('Authorization', 'bearer ' + token.access_token); } } + let isolationVersion = config.isolationVersion; + if (isolationVersion) { + xhr.setRequestHeader('z-l-t-version', isolationVersion); + } } }); }, diff --git a/zlt-web/back-web/src/main/resources/static/module/config.js b/zlt-web/back-web/src/main/resources/static/module/config.js index 400715a..38c8f6f 100644 --- a/zlt-web/back-web/src/main/resources/static/module/config.js +++ b/zlt-web/back-web/src/main/resources/static/module/config.js @@ -19,6 +19,7 @@ layui.define(function (exports) { base_server: apiUrl, tableName: 'easyweb', // 存储表名 clientId: 'webApp', // 应用id + isolationVersion: '', // 隔离版本 clientSecret: 'webApp', // 应用秘钥 autoRender: false, // 窗口大小改变后是否自动重新渲染表格,解决layui数据表格非响应式的问题,目前实现的还不是很好,暂时关闭该功能 pageTabs: true, // 是否开启多标签 -- GitLab