Dubbo学习记录(十三)--服务引入原理分析构造路由链

Dubbo服务引入源码解析

Dubbo解析@Reference注解
上面的内容中, Spring整合Dubbo的最终会生成一个ReferenceBean, 并调用ReferenceBean#get()方法生成一个动态代理实例;

ReferenceConfig#get()

    public synchronized T get() {
        checkAndUpdateSubConfigs();

        if (destroyed) {
            throw new IllegalStateException("The invoker of ReferenceConfig(" + url + ") has already destroyed!");
        }
        if (ref == null) {
            // 入口
            init();
        }
        return ref;  // Invoke代理
    }

工作:

  1. 通过checkAndUpdateSubConfigs() : 更新ReferenceBean的配置, 拿到优先级最高的配置属性, 像注册中心, 可在JVM系统关键变量, 配置中心的全局配置和应用配置, @Reference注解中配置, properties配置文件中配置, 同一个参数,可能会在多个配置中出现, 所以需要先确定配置的优先级。
  2. 这个过程和@ServiceConfig差不多, 配置优先级从高到低如下:
    JVM系统环境变量 > 应用配置 > 全局配置 > @Reference配置 > 配置文件配置;
    对应的配置类:
    SystemConfiguration > App的InmemoryConfiguration > 全局的InmemoryConfiguration > ReferenceConfig > PropertiesConfiguration;
  3. 调用init()方法,创建动态代理实例;

ReferenceConfig#get()

目的: 创建一个map用来存储创建代理实例的参数;
工作:

  1. 判断是否已经初始化, 已经初始化就返回;
  2. checkStubAndLocal已经过时;
  3. checkMock解析mock配置参数;
  4. 创建参数Map;
  5. 设置为消费者身份;
  6. 获取Dubbo版本信息, 放入Map;
  7. 设置接口关键字给Map;
  8. 获取监控中心的配置信息, 放入Map;
  9. 获取应用ApplicationConfig的配置信息, 放入map
  10. 获取模块ModuleConfig的配置信息,放入map;
  11. 获取消费ConsumerConfig的配置信息,放入map;
  12. 获取本机的注册中心信息, 放入map
  13. 调用createProxy()方法, ;
    private void init() {
        if (initialized) {
            return;
        }

        checkStubAndLocal(interfaceClass);
        checkMock(interfaceClass);
        Map<String, String> map = new HashMap<String, String>();

        map.put(SIDE_KEY, CONSUMER_SIDE);

        appendRuntimeParameters(map);
        if (!ProtocolUtils.isGeneric(getGeneric())) {
            String revision = Version.getVersion(interfaceClass, version);
            if (revision != null && revision.length() > 0) {
                map.put(REVISION_KEY, revision);
            }

            String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
            if (methods.length == 0) {
                logger.warn("No method found in service interface " + interfaceClass.getName());
                map.put(METHODS_KEY, ANY_VALUE);
            } else {
                map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), COMMA_SEPARATOR));
            }
        }
        map.put(INTERFACE_KEY, interfaceName);
        appendParameters(map, metrics);
        appendParameters(map, application);
        appendParameters(map, module);
        // remove 'default.' prefix for configs from ConsumerConfig
        // appendParameters(map, consumer, Constants.DEFAULT_KEY);
        appendParameters(map, consumer);
        appendParameters(map, this);

        Map<String, Object> attributes = null;
        if (CollectionUtils.isNotEmpty(methods)) {
            attributes = new HashMap<String, Object>();
            for (MethodConfig methodConfig : methods) {
                appendParameters(map, methodConfig, methodConfig.getName());
                String retryKey = methodConfig.getName() + ".retry";
                if (map.containsKey(retryKey)) {
                    String retryValue = map.remove(retryKey);
                    if ("false".equals(retryValue)) {
                        map.put(methodConfig.getName() + ".retries", "0");
                    }
                }

                attributes.put(methodConfig.getName(), convertMethodConfig2AsyncInfo(methodConfig));
            }
        }

        String hostToRegistry = ConfigUtils.getSystemProperty(DUBBO_IP_TO_REGISTRY);
        if (StringUtils.isEmpty(hostToRegistry)) {
            hostToRegistry = NetUtils.getLocalHost();
        } else if (isInvalidLocalHost(hostToRegistry)) {
            throw new IllegalArgumentException("Specified invalid registry ip from property:" + DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);
        }
        map.put(REGISTER_IP_KEY, hostToRegistry);

        // 得到一个代理对象
        ref = createProxy(map);

        String serviceKey = URL.buildKey(interfaceName, group, version);
        ApplicationModel.initConsumerModel(serviceKey, buildConsumerModel(serviceKey, attributes));
        initialized = true;
    }

ReferenceConfig#createProxy(map)

目的: 创建代理实例, 创建Invoker;
工作:

  1. 判断是否为injvm引用, 是则使用injvm的服务;
  2. 清空urls ; @Reference可以配置url属性中配置多个url, 可以是点对点地址(用于调试), 也可以是注册中心地址;
  3. url不为空,则以","分割, 获取多个url, 遍历;
    3.1 如果是注册中心协议, 给注册中URLl添加key为“refer”,值为map的参数, 加入urls
    3.2 非注册中心协议, 则点对点协议,url中可能带有参数, 所以和map进行合并成新的URL, 加入urls
  4. url为空, 加载注册中心URL, 可能存在多个; 遍历每个URL添加key为"refer",值为map的参数, 加入urls;
  5. 判断是否只有一个注册中心URL
    5.1 是则调用REF_PROTOCOL.refer(interfaceClass, urls.get(0))返回一个invoker;
    5.2 否则遍历每个URL, 生成对应的Invoker实例, 并标记是否存在注册中心URL;
    5.3 判断是否存在注册中心URL配置
    5.3.1 是 则 把Invoker实例集合整个为一个RegistryAwareInvoker;
    5.3.2 否 则 把invoker实例集合整合为一个FalloverClusterInvoker;
    5.4 调用PROXY_FACTORY.getProxy(invoker),传入invoker, 生成一个代理实例;不指定代理方式,默认使用的是Javaassist代理;
    private T createProxy(Map<String, String> map) {
        if (shouldJvmRefer(map)) {
      	//...省略部分无关代码
        } else {
        	//清空urls
            urls.clear(); // reference retry init will add url to urls, lead to OOM
            // @Reference中指定了url属性
            if (url != null && url.length() > 0) { // user specified URL, could be peer-to-peer address, or register center's address.
                String[] us = SEMICOLON_SPLIT_PATTERN.split(url); // 用;号切分
                if (us != null && us.length > 0) {
                    for (String u : us) {
                        URL url = URL.valueOf(u);
                        if (StringUtils.isEmpty(url.getPath())) {
                            url = url.setPath(interfaceName);
                        }

                        // 如果是注册中心地址,则在url中添加一个refer参数
                        if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                            // map表示消费者端配置的参数
                            urls.add(url.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
                        } else {
                            // 如果是服务地址
                            // 有可能url中配置了参数,map中表示的服务消费者消费服务时的参数,所以需要合并
                            urls.add(ClusterUtils.mergeUrl(url, map));
                        }
                    }
                }
            } else { // assemble URL from register center's configuration
                // @Reference中的protocol属性表示使用哪个协议调用服务,如果不是本地调用协议injvm://, 则把注册中心地址找出来
                // 对于injvm://协议已经在之前的逻辑中就已经生成invoke了
                // if protocols not injvm checkRegistry
                if (!LOCAL_PROTOCOL.equalsIgnoreCase(getProtocol())){
                    checkRegistry();
                    // 加载注册中心地址
                    List<URL> us = loadRegistries(false);
                    if (CollectionUtils.isNotEmpty(us)) {
                        for (URL u : us) {
                            URL monitorUrl = loadMonitor(u);
                            if (monitorUrl != null) {
                                map.put(MONITOR_KEY, URL.encode(monitorUrl.toFullString()));
                            }
                            // 对于注册中心地址都添加REFER_KEY
                            urls.add(u.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map)));
                        }
                    }
                    if (urls.isEmpty()) {
					//抛异常
                }
            }

            // 如果只有一个url则直接refer得到一个invoker
            if (urls.size() == 1) {
                // RegistryProtocol.refer() 或者 DubboProtocol.refer()
                invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
            } else {

                List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
                URL registryURL = null; // 用来记录urls中最后一个注册中心url
                for (URL url : urls) {
                    invokers.add(REF_PROTOCOL.refer(interfaceClass, url));

                    if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
                        registryURL = url; // use last registry url
                    }
                }

                // 如果存在注册中心地址
                if (registryURL != null) { // registry url is available
                    // use RegistryAwareCluster only when register's CLUSTER is available
                    URL u = registryURL.addParameter(CLUSTER_KEY, RegistryAwareCluster.NAME);
                    // StaticDirectory表示静态服务目录,里面的invokers是不会变的, 生成一个RegistryAwareCluster
                    // The invoker wrap relation would be: RegistryAwareClusterInvoker(StaticDirectory) -> FailoverClusterInvoker(RegistryDirectory, will execute route) -> Invoker
                    invoker = CLUSTER.join(new StaticDirectory(u, invokers));
                } else { // not a registry url, must be direct invoke.
                    // 如果不存在注册中心地址, 生成一个FailoverClusterInvoker
                    invoker = CLUSTER.join(new StaticDirectory(invokers));
                }
            }
        }

		//...省略部分内容
        // create service proxy
        return (T) PROXY_FACTORY.getProxy(invoker);
    }

REF_PROTOCOL.refer(interfaceClass, urls.get(0))

  • Invoker执行器实例的生成是通过调用refer生成;
  • 一般url传的都是注册中心的URL, 可以有多个,对应的是urls;
	@SPI("dubbo")
	public interface Protocol {
	//...
    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
	}
  • 根据SPI机制, 调用refer之前, 会先调用一些ProtocolWrapper类, 然后根据URL决定使用哪个Protocol的实现类;
  • 如果url的Protocol属性为dubbo, 则调用DubboProtocol, 如果url的Protocol属性为registry, 则调用RegistryProtocol协议类;
  • 上面传的是registry注册中心的URL, 因此先调用Wrapper类refer处理,再调用RegistryProtocol处理;

1. ProtocolListenerWrapper#refer(Class type, URL url)

  1. 判断协议是否为Registry, 是则进行调用下一个Protocol处理;
  2. 否 则 先调用下一个Protocol处理, 再创建一个监听器, 监听这个Invoker;
    传递的url为注册中心的URL, 因此执行1步骤, 下一个是Protocol是ProtocolFilterWrapper;
    @Override
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
            return protocol.refer(type, url);
        }
        return new ListenerInvokerWrapper<T>(protocol.refer(type, url),
                Collections.unmodifiableList(
                        ExtensionLoader.getExtensionLoader(InvokerListener.class)
                                .getActivateExtension(url, INVOKER_LISTENER_KEY)));
    }

2. ProtocolFilterWrapper#refer(Class type, URL url)

  1. 判断协议是否为Registry, 是则进行调用下一个Protocol处理;
  2. 否 则 先调用下一个Protocol处理,创建一个执行器链chain(暂时不知道用来干啥)
    传递的url为注册中心的URL, 因此执行1步骤, 下一个Protocol是RegistryProtocol;
    @Override
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
        if (REGISTRY_PROTOCOL.equals(url.getProtocol())) {
            return protocol.refer(type, url);
        }
        return buildInvokerChain(protocol.refer(type, url), REFERENCE_FILTER_KEY, CommonConstants.CONSUMER);
    }

3. RegistryProtocol#refer(Class type, URL url)

工作:

  1. 获取url中registry的值, 设置为url的Protocol,再移除registry参数, registry的值默认为dubbo;
  2. 获取注册中心的实现(配置为Zookeeper, 则实现类为ZookeeperRegistry)
  3. 获取url的refer的参数值;
  4. 调用doRefer方法,创建Invoker;
    @Override
    @SuppressWarnings("unchecked")
    public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {

        // 从registry://的url中获取对应的注册中心,比如zookeeper, 默认为dubbo,dubbo提供了自带的注册中心实现
        // url由 registry:// 改变为---> zookeeper://
        url = URLBuilder.from(url)
                .setProtocol(url.getParameter(REGISTRY_KEY, DEFAULT_REGISTRY))
                .removeParameter(REGISTRY_KEY)
                .build();

        // 拿到注册中心实现,ZookeeperRegistry
        Registry registry = registryFactory.getRegistry(url);

        // 下面这个代码,通过过git历史提交记录是用来解决SimpleRegistry不可用的问题
        if (RegistryService.class.equals(type)) {
            return proxyFactory.getInvoker((T) registry, type, url);
        }

        // qs表示 queryString, 表示url中的参数,表示消费者引入服务时所配置的参数
        Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(REFER_KEY));

        // group="a,b" or group="*"
        String group = qs.get(GROUP_KEY);
        if (group != null && group.length() > 0) {
            if ((COMMA_SPLIT_PATTERN.split(group)).length > 1 || "*".equals(group)) {
                // group有多个值,这里的cluster为MergeableCluster
                return doRefer(getMergeableCluster(), registry, type, url);
            }
        }

        // 这里的cluster是cluster的Adaptive对象
        return doRefer(cluster, registry, type, url);
    }

4. RegistryProtcol#doRefer(Cluster cluster, Registry registry, Class type, URL url)

目的:创建Invoker
工作:

  1. 创建服务目录,参数为接口类class与url 并设置Registry为ZookeeperRegistry, Protocol为RegistryProtocol;
  2. 获取服务目录的url的参数;
  3. 创建消费者的URL, Protocol为consumers, 从参数中获取IP信息, 端口port为0, path路径为类路径名, 参数为parameters
  4. 简化URL, 去除一些没用的参数;
  5. 消费者URL注册到注册中心中;
  6. 构造路由链;路由链是动态服务目录中的一个属性
  7. 服务目录订阅几个路径;
  8. 调用FalloverCluster#join方法生成一个FalloverClusterInvoker执行器实例;
    private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
        // RegistryDirectory表示动态服务目录,会和注册中心的数据保持同步
        // type表示一个服务对应一个RegistryDirectory,url表示注册中心地址
        // 在消费端,最核心的就是RegistryDirectory
        RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
        directory.setRegistry(registry);
        directory.setProtocol(protocol);


        // all attributes of REFER_KEY
        // 引入服务所配置的参数
        Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());

        // 消费者url
        URL subscribeUrl = new URL(CONSUMER_PROTOCOL, parameters.remove(REGISTER_IP_KEY), 0, type.getName(), parameters);
        if (!ANY_VALUE.equals(url.getServiceInterface()) && url.getParameter(REGISTER_KEY, true)) {
            directory.setRegisteredConsumerUrl(getRegisteredConsumerUrl(subscribeUrl, url));

            // 注册简化后的消费url
            registry.register(directory.getRegisteredConsumerUrl());
        }
        //构造执行器链;
        directory.buildRouterChain(subscribeUrl);

        // 服务目录需要订阅的几个路径
        // 当前所引入的服务的消费应用目录:/dubbo/config/dubbo/dubbo-demo-consumer-application.configurators
        // 当前所引入的服务的动态配置目录:/dubbo/config/dubbo/org.apache.dubbo.demo.DemoService:1.1.1:g1.configurators
        // 当前所引入的服务的提供者目录:/dubbo/org.apache.dubbo.demo.DemoService/providers
        // 当前所引入的服务的老版本动态配置目录:/dubbo/org.apache.dubbo.demo.DemoService/configurators
        // 当前所引入的服务的老版本路由器目录:/dubbo/org.apache.dubbo.demo.DemoService/routers
        directory.subscribe(subscribeUrl.addParameter(CATEGORY_KEY,
                PROVIDERS_CATEGORY + "," + CONFIGURATORS_CATEGORY + "," + ROUTERS_CATEGORY));

        // 利用传进来的cluster,join得到invoker,
        Invoker invoker = cluster.join(directory);
        ProviderConsumerRegTable.registerConsumer(invoker, url, subscribeUrl, directory);
        return invoker;
    }
4.1 RegistryDirectory#buildRouterChain(URL url)

构造路由链

    public void buildRouterChain(URL url) {
        this.setRouterChain(RouterChain.buildChain(url));
    }

RouterChain#buildChain(URL url)
工作中,像gateway, 就会有一些路由规则, 某些请求只能访问端口号为 8080的服务, 这就是路由; 由于这些路由规则有很多是人为可以配置的, 因此服务运行过程中,也会发生改变。 因此, 一般都是要监听这些路由配置的。

目的:创建路由实例routers,
工作:

  1. 通过SPI机制获取RouterFactory的实现类, 主要有四个:MockRouterFactory, TagRouterFactory, AppRouterFactory, ServiceRouterFactory
  2. 遍历RouterFactory,调用getRouter创建Router实例;
	public static <T> RouterChain<T> buildChain(URL url) {
        return new RouterChain<>(url);
    }
    private RouterChain(URL url) {
		//获取RouterFactory的实现类;
        List<RouterFactory> extensionFactories = ExtensionLoader.getExtensionLoader(RouterFactory.class)
                .getActivateExtension(url, (String[]) null);

        // 然后利用RouterFactory根据url生成各个类型的Router
        // 这里生产的routers已经是真实可用的了,但是有个比较特殊的:
        // 对于应用条件路由和服务条件路由对于的Router对象,对象内部已经有真实可用的数据了(数据已经从配置中心得到了)
        // 但是对于标签路由则没有,它暂时还相当于一个没有内容的对象(还没有从配置中心获取标签路由的数据)
        List<Router> routers = extensionFactories.stream()
                .map(factory -> factory.getRouter(url))
                .collect(Collectors.toList());

        // 把routers按priority进行排序
        initWithRouters(routers);
    }
4.2 RouterFactory #getRouter(URL url)

看看三个路由创建的过程: AppRouter , ServiceRouter , TagRouter;

@SPI
public interface RouterFactory {
    @Adaptive("protocol")
    Router getRouter(URL url);
}

  • AppRouterFactory # getRouter(URL url):创建AppRouter;
@Activate(order = 200)
public class AppRouterFactory implements RouterFactory {
    public static final String NAME = "app";
    private volatile Router router;
    @Override
    public Router getRouter(URL url) {
        synchronized (this) {
            if (router == null) {
                router = createRouter(url);
            }
        }
        return router;
    }
    private Router createRouter(URL url) {
        // 内部会进行初始化
        return new AppRouter(DynamicConfiguration.getDynamicConfiguration(), url);
    }
//AppRouter 
public class AppRouter extends ListenableRouter {
    public static final String NAME = "APP_ROUTER";
    private static final int APP_ROUTER_DEFAULT_PRIORITY = 150;

    public AppRouter(DynamicConfiguration configuration, URL url) {
        // 拿到应用名作为ruleKey调用父类的构造方法;
        super(configuration, url, url.getParameter(CommonConstants.APPLICATION_KEY));
        this.priority = APP_ROUTER_DEFAULT_PRIORITY;
    }
}
//ListenableRouter 构造方法   
//初始化,会绑定一个监听器,负责监听配置中心条件路由的修改,并且会主动从配置中心获取一下当前条件路由的数据并做解析
public ListenableRouter(DynamicConfiguration configuration, URL url, String ruleKey) {
        super(configuration, url);
        this.force = false;
        this.init(ruleKey);
}
//ListenableRouter#init   
//工作:
//1. 创建路由的key, ruleKey为应用名称或者服务名, 格式为:  服务名+".condition-router",或 应用名+".condition-router"
//2. 对routerKey绑定一个监听器,监听routerKey对应的路径,当前类ListenableRouter就自带了一个监听器
//3.  绑定完监听器后,主动的从配置中心获取一下当前服务或消费者应用的对应的路由配置
//4. 手动调用处理事件方法process();
private synchronized void init(String ruleKey) {
        if (StringUtils.isEmpty(ruleKey)) {
            return;
        }
        String routerKey = ruleKey + RULE_SUFFIX;
        configuration.addListener(routerKey, this);
        String rule = configuration.getRule(routerKey, DynamicConfiguration.DEFAULT_GROUP);
        if (StringUtils.isNotEmpty(rule)) {
            // 手动调用监听器处理事件的方法process()
            this.process(new ConfigChangeEvent(routerKey, rule));
        }
    }
    @Override
    public synchronized void process(ConfigChangeEvent event) {
        if (event.getChangeType().equals(ConfigChangeType.DELETED)) {
            // 如果是一个删除时间,则清空当前Router中的conditionRouters属性,表示当前Router对象中没有路由规则
            routerRule = null;
            conditionRouters = Collections.emptyList();
        } else {
            try {
                // 解析路由规则
                routerRule = ConditionRuleParser.parse(event.getValue());
                // 根据路由规则,生成ConditionRouter-条件路由对象,并赋值给当前Router对象的conditionRouters属性
                generateConditions(routerRule);
            } catch (Exception e) {
            }
        }
    }
  1. 调用AppRouterFactory创建AppRouter的时候,AppRouter对应的RouterKey会绑定一个监听器,
  2. 再拿到当前服务或者应用的路由配置,
  3. 然后调用监听事件处理路由配置,生成ConditionRouter集合,赋值给AppRouter的conditionRouter属性;
  • ServiceRouterFactory#getRouter()
    创建ServiceRouter实例, 传入代表动态配置的实例, 以及服务URL;
public abstract class CacheableRouterFactory implements RouterFactory {
    private ConcurrentMap<String, Router> routerMap = new ConcurrentHashMap<>();
    @Override
    public Router getRouter(URL url) {
        // 创建Router并放入routerMap中,一个服务对应一个标签路由
        routerMap.computeIfAbsent(url.getServiceKey(), k -> createRouter(url));
        return routerMap.get(url.getServiceKey());
    }
    protected abstract Router createRouter(URL url);
}
@Activate(order = 300)
public class ServiceRouterFactory extends CacheableRouterFactory {
    public static final String NAME = "service";
    @Override
    protected Router createRouter(URL url) {
        return new ServiceRouter(DynamicConfiguration.getDynamicConfiguration(), url);
    }
}
//调用父类ListenableRouter的构造方法, 传入动态配置,服务URL, 以及路由key, 格式为:{interfaceName}:[version]:[group] 
public class ServiceRouter extends ListenableRouter {
    public static final String NAME = "SERVICE_ROUTER";
    private static final int SERVICE_ROUTER_DEFAULT_PRIORITY = 140;

    public ServiceRouter(DynamicConfiguration configuration, URL url) {
        // 得到服务名
        super(configuration, url, DynamicConfiguration.getRuleKey(url));
        this.priority = SERVICE_ROUTER_DEFAULT_PRIORITY;
    }
}
//ListnableRouter: 过程和AppRouter一样。
public ListenableRouter(DynamicConfiguration configuration, URL url, String ruleKey) {
        super(configuration, url);
        this.force = false;
        // ruleKey为服务名或应用名
        // 初始化,会绑定一个监听器,负责监听配置中心条件路由的修改,并且会主动从配置中心获取一下当前条件路由的数据并做解析
        this.init(ruleKey);
    }
  • TagRouterFactory#getRouter(URL url): 创建标签路由;
  1. 调用父类CacheableRouterFactory 的getRouter方法, 有一个抽象方法createRouter方法, 由子类实现具体的Router, 如ServiceRouter类似;
  2. 创建TagRouter实例, 传入代表动态配置的实例, 服务URL;
  3. 创建给TagRouter实例设置一些属性值就结束 (监听的过程没有这里实现)
@Activate(order = 100)
public class TagRouterFactory extends CacheableRouterFactory {
    public static final String NAME = "tag";
    @Override
    protected Router createRouter(URL url) {
        return new TagRouter(DynamicConfiguration.getDynamicConfiguration(), url);
    }
}
public abstract class CacheableRouterFactory implements RouterFactory {
    private ConcurrentMap<String, Router> routerMap = new ConcurrentHashMap<>();
    @Override
    public Router getRouter(URL url) {
        // 创建Router并放入routerMap中,一个服务对应一个标签路由
        routerMap.computeIfAbsent(url.getServiceKey(), k -> createRouter(url));
        return routerMap.get(url.getServiceKey());
    }
    protected abstract Router createRouter(URL url);
}

public class TagRouter extends AbstractRouter implements ConfigurationListener {
    public static final String NAME = "TAG_ROUTER";
    private static final int TAG_ROUTER_DEFAULT_PRIORITY = 100;
    private static final String RULE_SUFFIX = ".tag-router";
    private TagRouterRule tagRouterRule;
    private String application;
    public TagRouter(DynamicConfiguration configuration, URL url) {
        super(configuration, url);
        this.priority = TAG_ROUTER_DEFAULT_PRIORITY;
    }
}
public AbstractRouter(DynamicConfiguration configuration, URL url) {
        this.configuration = configuration;
        this.url = url;
}

使用了简单工厂模式, 公共逻辑让抽象类实现, 而一些不同的细节交给子类去实现。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值