diff --git a/common/mall-spring-boot-starter-dubbo/pom.xml b/common/mall-spring-boot-starter-dubbo/pom.xml index 6fc1417d7..31acd196e 100644 --- a/common/mall-spring-boot-starter-dubbo/pom.xml +++ b/common/mall-spring-boot-starter-dubbo/pom.xml @@ -26,9 +26,15 @@ + + + + + + - com.alibaba.cloud - spring-cloud-starter-dubbo + org.apache.dubbo + dubbo-spring-boot-starter diff --git a/common/mall-spring-boot-starter-dubbo/src/main/java/cn/iocoder/mall/dubbo/config/DubboWebAutoConfiguration.java b/common/mall-spring-boot-starter-dubbo/src/main/java/cn/iocoder/mall/dubbo/config/DubboWebAutoConfiguration.java index 7d6c9757a..92fbbd9cd 100644 --- a/common/mall-spring-boot-starter-dubbo/src/main/java/cn/iocoder/mall/dubbo/config/DubboWebAutoConfiguration.java +++ b/common/mall-spring-boot-starter-dubbo/src/main/java/cn/iocoder/mall/dubbo/config/DubboWebAutoConfiguration.java @@ -1,6 +1,8 @@ package cn.iocoder.mall.dubbo.config; import cn.iocoder.mall.dubbo.core.web.DubboRouterTagWebInterceptor; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.NoSuchBeanDefinitionException; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.context.annotation.Configuration; @@ -11,14 +13,18 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) public class DubboWebAutoConfiguration implements WebMvcConfigurer { + private Logger logger = LoggerFactory.getLogger(DubboWebAutoConfiguration.class); + // ========== 拦截器相关 ========== @Override public void addInterceptors(InterceptorRegistry registry) { try { + // 设置为 -1000 的原因,保证在比较前面就处理该逻辑。例如说,认证拦截器; registry.addInterceptor(new DubboRouterTagWebInterceptor()).order(-1000); + logger.info("[addInterceptors][加载 DubboRouterTagWebInterceptor 拦截器完成]"); } catch (NoSuchBeanDefinitionException e) { -// logger.warn("[addInterceptors][无法获取 AccessLogInterceptor 拦截器,因此不启动 AccessLog 的记录]"); + logger.warn("[addInterceptors][无法获取 DubboRouterTagWebInterceptor 拦截器,无法使用 Dubbo 标签路由]"); } } diff --git a/common/mall-spring-boot-starter-dubbo/src/main/java/cn/iocoder/mall/dubbo/core/cluster/interceptor/DubboConsumerRouterTagClusterInterceptor.java b/common/mall-spring-boot-starter-dubbo/src/main/java/cn/iocoder/mall/dubbo/core/cluster/interceptor/DubboConsumerRouterTagClusterInterceptor.java new file mode 100644 index 000000000..4991f0da2 --- /dev/null +++ b/common/mall-spring-boot-starter-dubbo/src/main/java/cn/iocoder/mall/dubbo/core/cluster/interceptor/DubboConsumerRouterTagClusterInterceptor.java @@ -0,0 +1,37 @@ +package cn.iocoder.mall.dubbo.core.cluster.interceptor; + +import cn.iocoder.common.framework.util.StringUtils; +import cn.iocoder.mall.dubbo.core.filter.DubboProviderRouterTagFilter; +import cn.iocoder.mall.dubbo.core.router.DubboRouterTagContextHolder; +import org.apache.dubbo.common.constants.CommonConstants; +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.rpc.Invocation; +import org.apache.dubbo.rpc.cluster.interceptor.ClusterInterceptor; +import org.apache.dubbo.rpc.cluster.support.AbstractClusterInvoker; + +/** + * Consumer 方,在调用 Provider 时,将 {@link DubboRouterTagContextHolder} 中的 Tag 通过 Dubbo 隐式传参。 + * + * 完整逻辑说明,见 {@link DubboProviderRouterTagFilter} + * + * 注意,这里需要设置到 order = 1 的原因,是需要保证排在 ConsumerContextClusterInterceptor 之后 + */ +@Activate(group = CommonConstants.CONSUMER, order = 1) +public class DubboConsumerRouterTagClusterInterceptor implements ClusterInterceptor { + + @Override + public void before(AbstractClusterInvoker clusterInvoker, Invocation invocation) { + // 设置 Dubbo Tag 到 Dubbo 隐式传参 + String dubboTag = DubboRouterTagContextHolder.getTag(); + if (StringUtils.hasText(dubboTag)) { + invocation.setAttachment(CommonConstants.TAG_KEY, dubboTag); + } + } + + @Override + public void after(AbstractClusterInvoker clusterInvoker, Invocation invocation) { + // 清空 Dubbo Tag 的隐式传参 + invocation.setAttachment(CommonConstants.TAG_KEY, null); + } + +} diff --git a/common/mall-spring-boot-starter-dubbo/src/main/java/cn/iocoder/mall/dubbo/core/filter/DubboProviderRouterTagFilter.java b/common/mall-spring-boot-starter-dubbo/src/main/java/cn/iocoder/mall/dubbo/core/filter/DubboProviderRouterTagFilter.java new file mode 100644 index 000000000..7051e16f8 --- /dev/null +++ b/common/mall-spring-boot-starter-dubbo/src/main/java/cn/iocoder/mall/dubbo/core/filter/DubboProviderRouterTagFilter.java @@ -0,0 +1,44 @@ +package cn.iocoder.mall.dubbo.core.filter; + +import cn.iocoder.common.framework.util.StringUtils; +import cn.iocoder.mall.dubbo.core.cluster.interceptor.DubboConsumerRouterTagClusterInterceptor; +import cn.iocoder.mall.dubbo.core.router.DubboRouterTagContextHolder; +import org.apache.dubbo.common.constants.CommonConstants; +import org.apache.dubbo.common.extension.Activate; +import org.apache.dubbo.rpc.*; +import org.apache.dubbo.rpc.cluster.router.tag.TagRouter; + +/** + * 基于 Dubbo 标签路由规则(http://dubbo.apache.org/zh-cn/docs/user/demos/routing-rule.html),实现如下功能: + * 1. 本地开发调试时,在带有 Dubbo Tag 的情况下,优先调用指定 Tag 的服务提供者。这样,我们可以将本地启动的服务提供者打上相应的 Tag,即可优先调用本地; + * 2. TODO 优化点:蓝绿发布、灰度发布 + * + * 实现逻辑为: + * 1. 对于 Consumer 方,在调用 Provider 时,{@link DubboConsumerRouterTagClusterInterceptor} 会将 {@link DubboRouterTagContextHolder} 中的 Tag 通过 Dubbo 隐式传参。 + * 同时,Dubbo 自带 {@link TagRouter},会根据该参数,会选择符合该 Tag 的 Provider。 + * 2. 对于 Provider 方,在通过 Dubbo 隐式传参获得到 Tag 时,会设置到 {@link DubboRouterTagContextHolder} 中。 + * 这样,在 Provider 作为 Consumer 角色时,调用其它 Provider 时,可以继续实现标签路由的功能。 + */ +@Activate(group = {CommonConstants.PROVIDER, CommonConstants.CONSUMER}, order = -1000) +public class DubboProviderRouterTagFilter implements Filter { + + @Override + public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { + // 从 Dubbo 隐式传参获得 Dubbo Tag + String dubboTag = invocation.getAttachment(CommonConstants.TAG_KEY); + boolean hasDubboTag = StringUtils.hasText(dubboTag); + if (hasDubboTag) { + invocation.setAttachment(CommonConstants.TAG_KEY, dubboTag); + } + // 继续调用 + try { + return invoker.invoke(invocation); + } finally { + // 清理 + if (hasDubboTag) { + DubboRouterTagContextHolder.clear(); + } + } + } + +} diff --git a/common/mall-spring-boot-starter-dubbo/src/main/java/cn/iocoder/mall/dubbo/core/filter/DubboRouterTagFilter.java b/common/mall-spring-boot-starter-dubbo/src/main/java/cn/iocoder/mall/dubbo/core/filter/DubboRouterTagFilter.java deleted file mode 100644 index 002a332cc..000000000 --- a/common/mall-spring-boot-starter-dubbo/src/main/java/cn/iocoder/mall/dubbo/core/filter/DubboRouterTagFilter.java +++ /dev/null @@ -1,62 +0,0 @@ -package cn.iocoder.mall.dubbo.core.filter; - -import cn.iocoder.common.framework.util.StringUtils; -import cn.iocoder.mall.dubbo.core.router.DubboRouterTagContextHolder; -import org.apache.dubbo.common.constants.CommonConstants; -import org.apache.dubbo.common.extension.Activate; -import org.apache.dubbo.rpc.*; - -/** - * 基于 Dubbo 标签路由规则(http://dubbo.apache.org/zh-cn/docs/user/demos/routing-rule.html),实现如下功能: - * 1. 本地开发调试时,在带有 Dubbo Tag 的情况下,优先调用指定 Tag 的服务提供者。这样,我们可以将本地启动的服务提供者打上相应的 Tag,即可优先调用本地; - * 2. TODO 优化点:蓝绿发布、灰度发布 - * - * 实现逻辑为: - * 1. 对于 Consumer 方,在调用 Provider 时,会将 {@link DubboRouterTagContextHolder} 中的 Tag 通过 Dubbo 隐式传参。 - * 同时,Dubbo 自带 {@link org.apache.dubbo.rpc.cluster.router.tag.TagRouter},会根据该参数,会选择符合该 Tag 的 Provider。 - * 2. 对于 Provider 方,在通过 Dubbo 隐式传参获得到 Tag 时,会设置到 {@link DubboRouterTagContextHolder} 中。 - * 这样,在 Provider 作为 Consumer 角色时,调用其它 Provider 时,可以继续实现标签路由的功能。 - */ -@Activate(group = {CommonConstants.PROVIDER, CommonConstants.CONSUMER}, order = -1000) -public class DubboRouterTagFilter implements Filter { - - @Override - public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { - // 消费端 - if (RpcContext.getContext().isConsumerSide()) { - // 设置 Dubbo Tag 到 Dubbo 隐式传参 - String dubboTag = DubboRouterTagContextHolder.getTag(); - boolean hasDubboTag = StringUtils.hasText(dubboTag); - if (hasDubboTag) { - invocation.setAttachment(CommonConstants.TAG_KEY, dubboTag); - } - // 继续调用 - try { - return invoker.invoke(invocation); - } finally { - // 解决极端情况下,本地 injvm 调用时,消费端会调用 DubboRouterTagContextHolder.clear() 上下文,导致消费端也被清理了,因为在同一个 JVM 进程内。 - if (hasDubboTag) { - DubboRouterTagContextHolder.setTag(dubboTag); - } - } - // 提供端 - } else { - // 从 Dubbo 隐式传参获得 Dubbo Tag - String dubboTag = invocation.getAttachment(CommonConstants.TAG_KEY); - boolean hasDubboTag = StringUtils.hasText(dubboTag); - if (hasDubboTag) { - invocation.setAttachment(CommonConstants.TAG_KEY, dubboTag); - } - // 继续调用 - try { - return invoker.invoke(invocation); - } finally { - // 清理 - if (hasDubboTag) { - DubboRouterTagContextHolder.clear(); - } - } - } - } - -} diff --git a/common/mall-spring-boot-starter-dubbo/src/main/java/cn/iocoder/mall/dubbo/core/router/DubboRouterTagContextHolder.java b/common/mall-spring-boot-starter-dubbo/src/main/java/cn/iocoder/mall/dubbo/core/router/DubboRouterTagContextHolder.java index c25bd1c2e..2a658b6b2 100644 --- a/common/mall-spring-boot-starter-dubbo/src/main/java/cn/iocoder/mall/dubbo/core/router/DubboRouterTagContextHolder.java +++ b/common/mall-spring-boot-starter-dubbo/src/main/java/cn/iocoder/mall/dubbo/core/router/DubboRouterTagContextHolder.java @@ -1,9 +1,11 @@ package cn.iocoder.mall.dubbo.core.router; +import cn.iocoder.mall.dubbo.core.filter.DubboProviderRouterTagFilter; + /** * Dubbo 路由 Tag 的上下文 * - * @see cn.iocoder.mall.dubbo.core.filter.DubboRouterTagFilter + * @see DubboProviderRouterTagFilter * @see cn.iocoder.mall.dubbo.core.web.DubboRouterTagWebInterceptor */ public class DubboRouterTagContextHolder { diff --git a/common/mall-spring-boot-starter-dubbo/src/main/java/cn/iocoder/mall/dubbo/core/web/DubboRouterTagWebInterceptor.java b/common/mall-spring-boot-starter-dubbo/src/main/java/cn/iocoder/mall/dubbo/core/web/DubboRouterTagWebInterceptor.java index aab2363cb..c7ce62713 100644 --- a/common/mall-spring-boot-starter-dubbo/src/main/java/cn/iocoder/mall/dubbo/core/web/DubboRouterTagWebInterceptor.java +++ b/common/mall-spring-boot-starter-dubbo/src/main/java/cn/iocoder/mall/dubbo/core/web/DubboRouterTagWebInterceptor.java @@ -1,7 +1,11 @@ package cn.iocoder.mall.dubbo.core.web; import cn.iocoder.common.framework.util.StringUtils; +import cn.iocoder.mall.dubbo.core.cluster.interceptor.DubboConsumerRouterTagClusterInterceptor; +import cn.iocoder.mall.dubbo.core.filter.DubboProviderRouterTagFilter; import cn.iocoder.mall.dubbo.core.router.DubboRouterTagContextHolder; +import org.apache.dubbo.common.constants.CommonConstants; +import org.apache.dubbo.rpc.RpcContext; import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; @@ -11,7 +15,8 @@ import javax.servlet.http.HttpServletResponse; /** * Dubbo 路由标签的 Web 拦截器,将请求 Header 中的 {@link #HEADER_DUBBO_TAG} 设置到 {@link DubboRouterTagContextHolder} 中。 * - * @see cn.iocoder.mall.dubbo.core.filter.DubboRouterTagFilter + * @see DubboProviderRouterTagFilter + * @see DubboConsumerRouterTagClusterInterceptor */ public class DubboRouterTagWebInterceptor implements HandlerInterceptor { @@ -22,6 +27,7 @@ public class DubboRouterTagWebInterceptor implements HandlerInterceptor { String tag = request.getHeader(HEADER_DUBBO_TAG); if (StringUtils.hasText(tag)) { DubboRouterTagContextHolder.setTag(tag); + RpcContext.getContext().setAttachment(CommonConstants.TAG_KEY, tag); } return true; } diff --git a/common/mall-spring-boot-starter-dubbo/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter b/common/mall-spring-boot-starter-dubbo/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter index b1f85b26f..f81a93f70 100644 --- a/common/mall-spring-boot-starter-dubbo/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter +++ b/common/mall-spring-boot-starter-dubbo/src/main/resources/META-INF/dubbo/com.alibaba.dubbo.rpc.Filter @@ -1,2 +1,2 @@ dubboExceptionFilter=cn.iocoder.mall.dubbo.core.filter.DubboProviderExceptionFilter -dubboRouterTagFilter=cn.iocoder.mall.dubbo.core.filter.DubboRouterTagFilter +dubboProviderRouterTagFilter=cn.iocoder.mall.dubbo.core.filter.DubboProviderRouterTagFilter diff --git a/common/mall-spring-boot-starter-dubbo/src/main/resources/META-INF/dubbo/org.apache.dubbo.rpc.cluster.interceptor.ClusterInterceptor b/common/mall-spring-boot-starter-dubbo/src/main/resources/META-INF/dubbo/org.apache.dubbo.rpc.cluster.interceptor.ClusterInterceptor new file mode 100644 index 000000000..bba113398 --- /dev/null +++ b/common/mall-spring-boot-starter-dubbo/src/main/resources/META-INF/dubbo/org.apache.dubbo.rpc.cluster.interceptor.ClusterInterceptor @@ -0,0 +1 @@ +dubboConsumerRouterTagClusterInterceptor=cn.iocoder.mall.dubbo.core.cluster.interceptor.DubboConsumerRouterTagClusterInterceptor diff --git a/mall-dependencies/pom.xml b/mall-dependencies/pom.xml index 78e5b952e..64255fcc6 100644 --- a/mall-dependencies/pom.xml +++ b/mall-dependencies/pom.xml @@ -41,7 +41,7 @@ 3.1.1 3.2.5.RELEASE - 2.7.6 + 2.7.7 2.0.1 diff --git a/management-web-app/src/main/resources/application-local.yml b/management-web-app/src/main/resources/application-local.yml index 2e9e99973..b03881072 100644 --- a/management-web-app/src/main/resources/application-local.yml +++ b/management-web-app/src/main/resources/application-local.yml @@ -11,4 +11,10 @@ spring: dubbo: # Dubbo 注册中心 registry: - address: spring-cloud://400-infra.server.iocoder.cn:8848 # 指定 Dubbo 服务注册中心的地址 +# address: spring-cloud://400-infra.server.iocoder.cn:8848 # 指定 Dubbo 服务注册中心的地址 +# address: nacos://400-infra.server.iocoder.cn:8848?namespace=local # 指定 Dubbo 服务注册中心的地址 + protocol: nacos + address: 400-infra.server.iocoder.cn:8848?namespace=local + timeout: 20000 + register: true + subscribe: true diff --git a/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/SystemServiceApplication.java b/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/SystemServiceApplication.java index 13349c96a..7c1623bbb 100644 --- a/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/SystemServiceApplication.java +++ b/system-service-project/system-service-app/src/main/java/cn/iocoder/mall/systemservice/SystemServiceApplication.java @@ -2,12 +2,14 @@ package cn.iocoder.mall.systemservice; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.ConfigurableApplicationContext; @SpringBootApplication public class SystemServiceApplication { public static void main(String[] args) { - SpringApplication.run(SystemServiceApplication.class, args); + ConfigurableApplicationContext context = SpringApplication.run(SystemServiceApplication.class, args); + System.out.println(context); } } diff --git a/system-service-project/system-service-app/src/main/resources/application-local.yaml b/system-service-project/system-service-app/src/main/resources/application-local.yaml index bf97cae95..e15afd4d1 100644 --- a/system-service-project/system-service-app/src/main/resources/application-local.yaml +++ b/system-service-project/system-service-app/src/main/resources/application-local.yaml @@ -17,4 +17,5 @@ spring: dubbo: # Dubbo 注册中心 registry: - address: spring-cloud://400-infra.server.iocoder.cn:8848 # 指定 Dubbo 服务注册中心的地址 +# address: spring-cloud://400-infra.server.iocoder.cn:8848 # 指定 Dubbo 服务注册中心的地址 + address: nacos://400-infra.server.iocoder.cn:8848?namespace=local # 指定 Dubbo 服务注册中心的地址