1. 重新实现 Dubbo 路由的过滤器,之前的版本有问题
2. Spring Cloud Alibaba Dubbo 的 URL 处理有问题,切回到 Dubbo 原生注册中心,不使用 Spring Cloud 注册中心pull/3/head
							parent
							
								
									1dadf93449
								
							
						
					
					
						commit
						92c2d79dc1
					
				| 
						 | 
				
			
			@ -26,9 +26,15 @@
 | 
			
		|||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <!-- RPC 相关 -->
 | 
			
		||||
        <!-- TODO 优化点:Spring Cloud Alibaba Dubbo 的示例 -->
 | 
			
		||||
<!--        <dependency>-->
 | 
			
		||||
<!--            <groupId>com.alibaba.cloud</groupId>-->
 | 
			
		||||
<!--            <artifactId>spring-cloud-starter-dubbo</artifactId>-->
 | 
			
		||||
<!--        </dependency>-->
 | 
			
		||||
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.alibaba.cloud</groupId>
 | 
			
		||||
            <artifactId>spring-cloud-starter-dubbo</artifactId>
 | 
			
		||||
            <groupId>org.apache.dubbo</groupId>
 | 
			
		||||
            <artifactId>dubbo-spring-boot-starter</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
        <!-- 日志相关 -->
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 标签路由]");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -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 {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
dubboConsumerRouterTagClusterInterceptor=cn.iocoder.mall.dubbo.core.cluster.interceptor.DubboConsumerRouterTagClusterInterceptor
 | 
			
		||||
| 
						 | 
				
			
			@ -41,7 +41,7 @@
 | 
			
		|||
        <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>
 | 
			
		||||
        <!-- RPC 相关 -->
 | 
			
		||||
        <dubbo.version>2.7.6</dubbo.version>
 | 
			
		||||
        <dubbo.version>2.7.7</dubbo.version>
 | 
			
		||||
        <!-- Job 相关 -->
 | 
			
		||||
        <xxl-job.version>2.0.1</xxl-job.version>
 | 
			
		||||
        <!-- Transaction 相关 -->
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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 服务注册中心的地址
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue