1. 重新实现 Dubbo 路由的过滤器,之前的版本有问题

2. Spring Cloud Alibaba Dubbo 的 URL 处理有问题,切回到 Dubbo 原生注册中心,不使用 Spring Cloud 注册中心
This commit is contained in:
YunaiV 2020-07-21 19:45:58 +08:00
parent 1dadf93449
commit 92c2d79dc1
13 changed files with 121 additions and 72 deletions

View File

@ -26,9 +26,15 @@
</dependency> </dependency>
<!-- RPC 相关 --> <!-- RPC 相关 -->
<!-- TODO 优化点Spring Cloud Alibaba Dubbo 的示例 -->
<!-- <dependency>-->
<!-- <groupId>com.alibaba.cloud</groupId>-->
<!-- <artifactId>spring-cloud-starter-dubbo</artifactId>-->
<!-- </dependency>-->
<dependency> <dependency>
<groupId>com.alibaba.cloud</groupId> <groupId>org.apache.dubbo</groupId>
<artifactId>spring-cloud-starter-dubbo</artifactId> <artifactId>dubbo-spring-boot-starter</artifactId>
</dependency> </dependency>
<!-- 日志相关 --> <!-- 日志相关 -->

View File

@ -1,6 +1,8 @@
package cn.iocoder.mall.dubbo.config; package cn.iocoder.mall.dubbo.config;
import cn.iocoder.mall.dubbo.core.web.DubboRouterTagWebInterceptor; 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.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication; import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
@ -11,14 +13,18 @@ import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET) @ConditionalOnWebApplication(type = ConditionalOnWebApplication.Type.SERVLET)
public class DubboWebAutoConfiguration implements WebMvcConfigurer { public class DubboWebAutoConfiguration implements WebMvcConfigurer {
private Logger logger = LoggerFactory.getLogger(DubboWebAutoConfiguration.class);
// ========== 拦截器相关 ========== // ========== 拦截器相关 ==========
@Override @Override
public void addInterceptors(InterceptorRegistry registry) { public void addInterceptors(InterceptorRegistry registry) {
try { try {
// 设置为 -1000 的原因保证在比较前面就处理该逻辑例如说认证拦截器
registry.addInterceptor(new DubboRouterTagWebInterceptor()).order(-1000); registry.addInterceptor(new DubboRouterTagWebInterceptor()).order(-1000);
logger.info("[addInterceptors][加载 DubboRouterTagWebInterceptor 拦截器完成]");
} catch (NoSuchBeanDefinitionException e) { } catch (NoSuchBeanDefinitionException e) {
// logger.warn("[addInterceptors][无法获取 AccessLogInterceptor 拦截器,因此不启动 AccessLog 的记录]"); logger.warn("[addInterceptors][无法获取 DubboRouterTagWebInterceptor 拦截器,无法使用 Dubbo 标签路由]");
} }
} }

View File

@ -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);
}
}

View File

@ -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();
}
}
}
}

View File

@ -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();
}
}
}
}
}

View File

@ -1,9 +1,11 @@
package cn.iocoder.mall.dubbo.core.router; package cn.iocoder.mall.dubbo.core.router;
import cn.iocoder.mall.dubbo.core.filter.DubboProviderRouterTagFilter;
/** /**
* Dubbo 路由 Tag 的上下文 * Dubbo 路由 Tag 的上下文
* *
* @see cn.iocoder.mall.dubbo.core.filter.DubboRouterTagFilter * @see DubboProviderRouterTagFilter
* @see cn.iocoder.mall.dubbo.core.web.DubboRouterTagWebInterceptor * @see cn.iocoder.mall.dubbo.core.web.DubboRouterTagWebInterceptor
*/ */
public class DubboRouterTagContextHolder { public class DubboRouterTagContextHolder {

View File

@ -1,7 +1,11 @@
package cn.iocoder.mall.dubbo.core.web; package cn.iocoder.mall.dubbo.core.web;
import cn.iocoder.common.framework.util.StringUtils; 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 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.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.ModelAndView;
@ -11,7 +15,8 @@ import javax.servlet.http.HttpServletResponse;
/** /**
* Dubbo 路由标签的 Web 拦截器将请求 Header 中的 {@link #HEADER_DUBBO_TAG} 设置到 {@link DubboRouterTagContextHolder} * 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 { public class DubboRouterTagWebInterceptor implements HandlerInterceptor {
@ -22,6 +27,7 @@ public class DubboRouterTagWebInterceptor implements HandlerInterceptor {
String tag = request.getHeader(HEADER_DUBBO_TAG); String tag = request.getHeader(HEADER_DUBBO_TAG);
if (StringUtils.hasText(tag)) { if (StringUtils.hasText(tag)) {
DubboRouterTagContextHolder.setTag(tag); DubboRouterTagContextHolder.setTag(tag);
RpcContext.getContext().setAttachment(CommonConstants.TAG_KEY, tag);
} }
return true; return true;
} }

View File

@ -1,2 +1,2 @@
dubboExceptionFilter=cn.iocoder.mall.dubbo.core.filter.DubboProviderExceptionFilter dubboExceptionFilter=cn.iocoder.mall.dubbo.core.filter.DubboProviderExceptionFilter
dubboRouterTagFilter=cn.iocoder.mall.dubbo.core.filter.DubboRouterTagFilter dubboProviderRouterTagFilter=cn.iocoder.mall.dubbo.core.filter.DubboProviderRouterTagFilter

View File

@ -0,0 +1 @@
dubboConsumerRouterTagClusterInterceptor=cn.iocoder.mall.dubbo.core.cluster.interceptor.DubboConsumerRouterTagClusterInterceptor

View File

@ -41,7 +41,7 @@
<mybatis-plus.version>3.1.1</mybatis-plus.version> <mybatis-plus.version>3.1.1</mybatis-plus.version>
<spring-boot-starter-data-jest.version>3.2.5.RELEASE</spring-boot-starter-data-jest.version> <spring-boot-starter-data-jest.version>3.2.5.RELEASE</spring-boot-starter-data-jest.version>
<!-- RPC 相关 --> <!-- RPC 相关 -->
<dubbo.version>2.7.6</dubbo.version> <dubbo.version>2.7.7</dubbo.version>
<!-- Job 相关 --> <!-- Job 相关 -->
<xxl-job.version>2.0.1</xxl-job.version> <xxl-job.version>2.0.1</xxl-job.version>
<!-- Transaction 相关 --> <!-- Transaction 相关 -->

View File

@ -11,4 +11,10 @@ spring:
dubbo: dubbo:
# Dubbo 注册中心 # Dubbo 注册中心
registry: 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

View File

@ -2,12 +2,14 @@ package cn.iocoder.mall.systemservice;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication @SpringBootApplication
public class SystemServiceApplication { public class SystemServiceApplication {
public static void main(String[] args) { public static void main(String[] args) {
SpringApplication.run(SystemServiceApplication.class, args); ConfigurableApplicationContext context = SpringApplication.run(SystemServiceApplication.class, args);
System.out.println(context);
} }
} }

View File

@ -17,4 +17,5 @@ spring:
dubbo: dubbo:
# Dubbo 注册中心 # Dubbo 注册中心
registry: 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 服务注册中心的地址