Reglas de enrutamiento para el análisis del código fuente de dubbo

1. Actualización de las reglas de enrutamiento

Si hay un cambio en la información de configuración de enrutamiento, notifique a RegistryDirectory # notificar (Listar URL). Lo siguiente toma a Zookeeper como un ejemplo para explicar.

public synchronized void notify(List<URL> urls) {
    
    
    ...
    List<URL> routerUrls = new ArrayList<URL>();
    ...
    for (URL url : urls) {
    
    
        String protocol = url.getProtocol();
        String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
        if (Constants.ROUTERS_CATEGORY.equals(category)
                || Constants.ROUTE_PROTOCOL.equals(protocol)) {
    
    
            routerUrls.add(url);
        }
        ...
    }
    ...
    // routers
    if (routerUrls != null && !routerUrls.isEmpty()) {
    
    
        // 获取路由
        // 下面详细讲解
        List<Router> routers = toRouters(routerUrls);
        if (routers != null) {
    
     // null - do nothing
            // 设置路由
            setRouters(routers);
        }
    }
    ...
}

Saque la URL modificada -> obtener ruta -> establecer ruta. El proceso de "obtención de ruta" se explica en detalle a continuación.

// RegistryDirectory#toRouters(List<URL>)
private List<Router> toRouters(List<URL> urls) {
    
    
    List<Router> routers = new ArrayList<Router>();
    if (urls == null || urls.isEmpty()) {
    
    
        return routers;
    }
    if (urls != null && !urls.isEmpty()) {
    
    
        for (URL url : urls) {
    
    
            if (Constants.EMPTY_PROTOCOL.equals(url.getProtocol())) {
    
    
                continue;
            }
            String routerType = url.getParameter(Constants.ROUTER_KEY);
            if (routerType != null && routerType.length() > 0) {
    
    
                url = url.setProtocol(routerType);
            }
            try {
    
    
                // RouterFactory是一个@SPI类,getRouter(URL)方法是@Adaptive("protocol")修饰的,所以通过url.protocol决定实现类
                Router router = routerFactory.getRouter(url);
                if (!routers.contains(router))
                    routers.add(router);
            } catch (Throwable t) {
    
    
                logger.error("convert router url to router error, url: " + url, t);
            }
        }
    }
    return routers;
}
2. Uso de reglas de enrutamiento

Al obtener la lista de Invocadores, primero seleccione la lista de Invocadores disponibles actualmente a través del Directorio y luego filtre los Invocadores que no cumplen con las reglas de enrutamiento a través de la ruta (Lista <Invocador>, URL, Invocación) de la interfaz del Enrutador. Echemos un vistazo a la implementación específica.

2.1 Usar reglas de enrutamiento para filtrar Invocadores
// AbstractDirectory#list(Invocation invocation)
public List<Invoker<T>> list(Invocation invocation) throws RpcException {
    
    
    ...
    // 通过Directory选出当前可用的Invokers列表
    List<Invoker<T>> invokers = doList(invocation);
    // 路由过滤
    List<Router> localRouters = this.routers; // local reference
    if (localRouters != null && !localRouters.isEmpty()) {
    
    
        for (Router router : localRouters) {
    
    
            try {
    
    
                if (router.getUrl() == null || router.getUrl().getParameter(Constants.RUNTIME_KEY, false)) {
    
    
                    invokers = router.route(invokers, getConsumerUrl(), invocation);
                }
            } catch (Throwable t) {
    
    
                logger.error("Failed to execute router: " + getUrl() + ", cause: " + t.getMessage(), t);
            }
        }
    }
    return invokers;
}
2.2 La primera forma de implementar el filtrado de enrutamiento: ConditionRouter # route (List <Invoker>, URL, Invocation)
// ConditionRouter#route(List<Invoker<T>>, URL, Invocation)
public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation)
        throws RpcException {
    
    
    if (invokers == null || invokers.isEmpty()) {
    
    
        return invokers;
    }
    try {
    
    
        // 匹配规则:host = 10.99.1.109 =>  host = 10.20.3.3,就是消费者ip为10.99.1.109的,转向提供者ip为10.20.3.3的服务器
        // when: => 的前半部分,也就是 host = 10.99.1.109
        if (!matchWhen(url, invocation)) {
    
    
            return invokers;
        }
        List<Invoker<T>> result = new ArrayList<Invoker<T>>();
        if (thenCondition == null) {
    
    
            logger.warn("The current consumer in the service blacklist. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey());
            return result;
        }
        for (Invoker<T> invoker : invokers) {
    
    
            // 匹配then条件
            // then: => 的后半部分,也就是 host = 10.20.3.3
            if (matchThen(invoker.getUrl(), url)) {
    
    
                result.add(invoker);
            }
        }
        if (!result.isEmpty()) {
    
    
            return result;
        } else if (force) {
    
    
            logger.warn("The route result is empty and force execute. consumer: " + NetUtils.getLocalHost() + ", service: " + url.getServiceKey() + ", router: " + url.getParameterAndDecoded(Constants.RULE_KEY));
            return result;
        }
    } catch (Throwable t) {
    
    
        logger.error("Failed to execute condition router rule: " + getUrl() + ", invokers: " + invokers + ", cause: " + t.getMessage(), t);
    }
    return invokers;
}

El ConditionRouter debe filtrar según las condiciones configuradas A continuación se muestra cómo el ScriptRouter filtra según el script.

2.2 La segunda forma de lograr el filtrado de enrutamiento: ScriptRouter # ruta (Lista <Invoker>, URL, Invocación)
// ScriptRouter#route(List<Invoker<T>>, URL, Invocation)
public <T> List<Invoker<T>> route(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException {
    
    
    try {
    
    
        List<Invoker<T>> invokersCopy = new ArrayList<Invoker<T>>(invokers);
        // 脚本引擎engine = new ScriptEngineManager()
        Compilable compilable = (Compilable) engine;
        Bindings bindings = engine.createBindings();
        bindings.put("invokers", invokersCopy);
        bindings.put("invocation", invocation);
        bindings.put("context", RpcContext.getContext());
        // 编译规则
        // rule是在FileRouterFactory#getRouter()中赋值的
        CompiledScript function = compilable.compile(rule);
        Object obj = function.eval(bindings);
        if (obj instanceof Invoker[]) {
    
    
            invokersCopy = Arrays.asList((Invoker<T>[]) obj);
        } else if (obj instanceof Object[]) {
    
    
            invokersCopy = new ArrayList<Invoker<T>>();
            for (Object inv : (Object[]) obj) {
    
    
                invokersCopy.add((Invoker<T>) inv);
            }
        } else {
    
    
            invokersCopy = (List<Invoker<T>>) obj;
        }
        return invokersCopy;
    } catch (ScriptException e) {
    
    
        //fail then ignore rule .invokers.
        logger.error("route error , rule has been ignored. rule: " + rule + ", method:" + invocation.getMethodName() + ", url: " + RpcContext.getContext().getUrl(), e);
        return invokers;
    }
}

FileRouterFactory lee el script de la regla a través de File, crea un nuevo motor de script ScriptEngineManager para leer el script anterior y obtiene la lista de Invocadores. A continuación se explica en detalle el proceso de lectura del script de FileRouterFactory.

// FileRouterFactory#getRouter(URL url)
public Router getRouter(URL url) {
    
    
    try {
    
    
        // Transform File URL into Script Route URL, and Load
        // file:///d:/path/to/route.js?router=script ==> script:///d:/path/to/route.js?type=js&rule=<file-content>
        String protocol = url.getParameter(Constants.ROUTER_KEY, ScriptRouterFactory.NAME); // Replace original protocol (maybe 'file') with 'script'
        String type = null; // Use file suffix to config script type, e.g., js, groovy ...
        // 获取url的path属性
        // TODO 这个path是怎么赋值的
        String path = url.getPath();
        if (path != null) {
    
    
            int i = path.lastIndexOf('.');
            if (i > 0) {
    
    
                type = path.substring(i + 1);
            }
        }
        // 获取过滤规则
        String rule = IOUtils.read(new FileReader(new File(url.getAbsolutePath())));

        boolean runtime = url.getParameter(Constants.RUNTIME_KEY, false);
        // 设置rule属性到url对象中
        URL script = url.setProtocol(protocol).addParameter(Constants.TYPE_KEY, type).addParameter(Constants.RUNTIME_KEY, runtime).addParameterAndEncoded(Constants.RULE_KEY, rule);
        // 获取ScriptRouter的实例
        return routerFactory.getRouter(script);
    } catch (IOException e) {
    
    
        throw new IllegalStateException(e.getMessage(), e);
    }
}

Supongo que te gusta

Origin blog.csdn.net/fomeiherz/article/details/101031435
Recomendado
Clasificación