Dubbo Learning Record (13)--Analysis of service introduction principle and construction of routing chain

Dubbo service introduces source code analysis

Dubbo parses the @Reference annotation
In the above content, Spring will eventually generate a ReferenceBean when integrating Dubbo, and call the ReferenceBean#get() method to generate a dynamic proxy instance;

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代理
    }

Work:

  1. Through checkAndUpdateSubConfigs(): update the configuration of ReferenceBean, get the configuration attribute with the highest priority, like the registration center, which can be configured in the key variables of the JVM system, the global configuration and application configuration of the configuration center, the configuration in the @Reference annotation, and the configuration in the properties configuration file , the same parameter may appear in multiple configurations, so it is necessary to determine the priority of the configuration first.
  2. This process is similar to @ServiceConfig, and the configuration priority from high to low is as follows:
    JVM system environment variables > application configuration > global configuration > @Reference configuration > configuration file configuration;
    corresponding configuration class:
    SystemConfiguration > App's InmemoryConfiguration > Global InmemoryConfiguration > ReferenceConfig > PropertiesConfiguration;
  3. Call the init() method to create a dynamic proxy instance;

ReferenceConfig#get()

Purpose: Create a map to store parameters for creating proxy instances;
work:

  1. Determine whether it has been initialized, and return if it has been initialized;
  2. checkStubAndLocal is obsolete;
  3. checkMock parses mock configuration parameters;
  4. Create parameter Map;
  5. set as a consumer;
  6. Obtain Dubbo version information and put it into the Map;
  7. Set the interface keyword to Map;
  8. Obtain the configuration information of the monitoring center and put it into the Map;
  9. Obtain the configuration information of ApplicationConfig and put it into the map
  10. Obtain the configuration information of the module ModuleConfig and put it into the map;
  11. Obtain the configuration information of ConsumerConfig and put it into the map;
  12. Obtain the registration center information of the machine and put it into the map
  13. Call the createProxy() method, ;
    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)

Purpose: create proxy instance, create Invoker;
work:

  1. Determine whether it is an injvm reference, and if so, use the injvm service;
  2. Clear urls; @Reference can configure multiple urls in the url attribute, which can be a point-to-point address (for debugging), or a registration center address;
  3. If the url is not empty, separate it with "," to get multiple urls and traverse them;
    3.1 If it is a registration center protocol, add the key as "refer" to the URLl in the registration, and the value is a parameter of map, and add urls
    3.2 Non-registration center protocol , it is a point-to-point protocol, and the url may contain parameters, so it is merged with the map to form a new URL, and added to urls
  4. url is empty, load the URL of the registration center, there may be more than one; traverse each URL and add the key as "refer", the value is the parameter of map, and add urls;
  5. Judging whether there is only one registry URL
    5.1 If yes, call REF_PROTOCOL.refer(interfaceClass, urls.get(0)) to return an invoker;
    5.2 Otherwise, traverse each URL, generate a corresponding Invoker instance, and mark whether there is a registry URL;
    5.3 Judging whether there is a registry center URL configuration
    5.3.1 If yes, integrate the Invoker instance set into a RegistryAwareInvoker;
    5.3.2 Otherwise, integrate the invoker instance set into a FalloverClusterInvoker;
    5.4 Call PROXY_FACTORY.getProxy(invoker), pass in the invoker, and generate a proxy Example; do not specify the proxy mode, the default is the Javaassist proxy;
    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))

  • The Invoker executor instance is generated by calling refer;
  • Generally, the url is the URL of the registration center, there can be more than one, corresponding to urls;
	@SPI("dubbo")
	public interface Protocol {
    
    
	//...
    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
	}
  • According to the SPI mechanism, before calling refer, it will call some ProtocolWrapper classes first, and then decide which Protocol implementation class to use according to the URL;
  • If the Protocol attribute of url is dubbo, call DubboProtocol, if the Protocol attribute of url is registry, call RegistryProtocol protocol class;
  • The URL of the registry registration center is uploaded above, so first call the Wrapper class refer processing, and then call the RegistryProtocol processing;

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

  1. Determine whether the protocol is Registry, if so, call the next Protocol for processing;
  2. Otherwise, first call the next Protocol to process, and then create a listener to listen to the Invoker; the
    passed url is the URL of the registration center, so step 1 is executed, and the next protocol is 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. Determine whether the protocol is Registry, if so, call the next Protocol for processing;
  2. Otherwise, first call the next Protocol to create an executor chain (I don’t know what it is used for now). The
    url passed is the URL of the registry, so step 1 is executed, and the next Protocol is 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)

Work:

  1. Get the value of registry in url, set it as the protocol of url, and remove the registry parameter, the value of registry is dubbo by default;
  2. Obtain the implementation of the registry (configure as Zookeeper, then the implementation class is ZookeeperRegistry)
  3. Get the parameter value of the refer of the url;
  4. Call the doRefer method to create an 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)

Purpose: To create an Invoker
job:

  1. Create a service directory, the parameters are interface class class and url and set Registry to ZookeeperRegistry, Protocol to RegistryProtocol;
  2. Obtain the parameters of the url of the service directory;
  3. Create the URL of the consumer, the Protocol is consumers, the IP information is obtained from the parameters, the port port is 0, the path path is the class path name, and the parameter is parameters
  4. Simplify the URL and remove some useless parameters;
  5. The consumer URL is registered in the registration center;
  6. Constructs a routing chain; a routing chain is an attribute in the dynamic service catalog
  7. The service catalog subscribes to several paths;
  8. Call the FalloverCluster#join method to generate a FalloverClusterInvoker executor instance;
    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)

Construct routing chain

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


In the work of RouterChain#buildChain(URL url), like gateway, there will be some routing rules, some requests can only access the service with port number 8080, this is routing; because many of these routing rules can be configured manually, so Changes can also occur while the service is running. Therefore, it is generally necessary to monitor these routing configurations.

Purpose: Create routing instance routers,
work:

  1. Obtain the implementation class of RouterFactory through the SPI mechanism, there are mainly four: MockRouterFactory, TagRouterFactory, AppRouterFactory, ServiceRouterFactory
  2. Traversing the RouterFactory, calling getRouter to create a Router instance;
	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)

Look at the process of creating three routes: 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. When calling AppRouterFactory to create AppRouter, the RouterKey corresponding to AppRouter will bind a listener.
  2. Then get the routing configuration of the current service or application,
  3. Then call the listening event to process the routing configuration, generate a ConditionRouter collection, and assign it to the conditionRouter property of AppRouter;
  • ServiceRouterFactory#getRouter()
    creates a ServiceRouter instance, passing in an instance representing dynamic configuration and a service 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): Create tag routing;
  1. Call the getRouter method of the parent class CacheableRouterFactory, there is an abstract method createRouter method, and the subclass implements the specific Router, such as ServiceRouter;
  2. Create a TagRouter instance, pass in the instance that represents the dynamic configuration, and the service URL;
  3. Create and set some attribute values ​​for the TagRouter instance and end (the monitoring process is not implemented here)
@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;
}

Using the simple factory pattern, the common logic is implemented by the abstract class, and some different details are handed over to the subclass.

Guess you like

Origin blog.csdn.net/yaoyaochengxian/article/details/123755139