dubbo源码学习(三)-Router

dubbo源码学习(三)-Router
在学习router之前先了解下灰色发布的概念
灰度发布
概念:灰度发布(又名金丝雀发布)是指在黑与白之间,能够平滑过渡的一种发布方式。在其上可以进行A/B testing,即让一部分用户继续用产品特性A,一部分用户开始用产品特性B,如果用户对B没有什么反对意见,那么逐步扩大范围,把所有用户都迁移到B上面来。灰度发布可以保证整体系统的稳定,在初始灰度的时候就可以发现、调整问题,以保证其影响度。
灰度期:灰度发布开始到结束期间的这一段时间,称为灰度期。
过程
1.首先在192.168.22.58和192.168.22.59两台机器上启动Provider,然后启动Consumer
2.假设我们要升级192.168.22.58服务器上的服务,接着我们去dubbo的控制台配置路由,切断192.168.22.58的流量,配置完成并且启动之后,就看到此时只调用192.168.22.59的服务
3.假设此时你在192.168.22.58服务器升级服务,升级完成后再次将启动服务
dubbo源码学习(三)-Router_第1张图片

router主要有三个实现类,分别是ConditionRouter,MockInvokersSelector,ScriptRouter
在这里插入图片描述
主要学习ConditionRouter(条件路由):
条件路由主要就是根据dubbo管理控制台配置的路由规则来过滤相关的invoker,当我们对路由规则点击启用的时候,就会触发RegistryDirectory类的notify方法(在directory也讲到在服务订阅的时候会触发notify方法,另外还有修改路由规则的时候会触发);
其实对于Router来说,重要的就是他是怎么过滤的。具体的源码可以跟着http://dubbo.apache.org/zh-cn/docs/source_code_guide/router.html文档走一边源码,借用大佬画的一个图可以看清楚大致的源码执行过程
dubbo源码学习(三)-Router_第2张图片
提高URL,随便也学习下dubbo的url规则:

protocol:一般是 dubbo 中的各种协议 如:dubbo thrift http zk
username/password:用户名/密码
host/port:主机/端口
path:接口名称
parameters:参数键值对
dubbo://192.168.1.6:20880/moe.cnkirito.sample.HelloService?timeout=3000
描述一个 dubbo 协议的服务
zookeeper://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=democonsumer&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&pid=1214&qos.port=33333×tamp=15457219946
描述一个zookeeper 注册中心
consumer://30.5.120.217/org.apache.dubbo.demo.DemoService?application=democonsumer&category=consumers&check=false&dubbo=2.0.2&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=1209&qos.port=33333&side=consumer×tamp=1545721827784
描述一个消费者
public final class URL implements Serializable {
    private static final long serialVersionUID = -6256557181054799417L;
    /** 协议 (例如: dubbo:// zookeeper://) */
    private final String protocol;
    /** 用户名 (例如: dubbo://hadluo) */
    private final String username;
    /** 密码 */
    private final String password;
    /** 地址 */
    private final String host;
    /** 端口 */
    private final int port;
    /** 上下文路径 */
    private final String path;
    /** 附加参数 */
    private final Map parameters;

    /** 缓存 优化 当前URL的 toString() */
    private volatile transient String string;

    public URL(String protocol, String username, String password, String host, int port, String path,
            Map parameters) {
        if ((username == null || username.length() == 0) && password != null && password.length() > 0) {
            throw new IllegalArgumentException("Invalid url, password without username!");
        }
        this.protocol = protocol;
        this.username = username;
        this.password = password;
        this.host = host;
        this.port = (port < 0 ? 0 : port);
        this.path = path;
        // trim the beginning "/"
        while (path != null && path.startsWith("/")) {
            path = path.substring(1);
        }
        if (parameters == null) {
            parameters = new HashMap();
        } else {
            parameters = new HashMap(parameters);
        }
        // 不可修改的 map
        this.parameters = Collections.unmodifiableMap(parameters);
    }


    public static URL valueOf(String url) {
        // dubbo://hadluo:[email protected]:20881/context/path?version=1.0.0&application=morgan&noValue
        if (url == null || (url = url.trim()).length() == 0) {
            throw new IllegalArgumentException("url == null");
        }
        String protocol = null;
        String username = null;
        String password = null;
        String host = null;
        int port = 0;
        String path = null;
        Map parameters = null;
        int i = url.indexOf("?"); // seperator between body and parameters
        if (i >= 0) {
            // 以 & 分隔 version=1.0.0&application=morgan&noValue
            String[] parts = url.substring(i + 1).split("\\&");
            parameters = new HashMap();
            for (String part : parts) {
                part = part.trim();
                if (part.length() > 0) {
                    int j = part.indexOf('=');
                    if (j >= 0) {
                        // 是 key=val 形式的
                        parameters.put(part.substring(0, j), part.substring(j + 1));
                    } else {
                        // 只有 val形式的
                        parameters.put(part, part);
                    }
                }
            }
            // url 变成 : dubbo://hadluo:[email protected]:20881/context/path?
            url = url.substring(0, i);
        }
        i = url.indexOf("://");
        if (i >= 0) {
            if (i == 0)
                throw new IllegalStateException("url missing protocol: \"" + url + "\"");
            // 取 dubbo
            protocol = url.substring(0, i);
            // url变成 hadluo:[email protected]:20881/context/path
            url = url.substring(i + 3);
        } else {
            // case: file:/path/to/file.txt
            i = url.indexOf(":/");
            if (i >= 0) {
                if (i == 0)
                    throw new IllegalStateException("url missing protocol: \"" + url + "\"");
                protocol = url.substring(0, i);
                url = url.substring(i + 1);
            }
        }

        i = url.indexOf("/");
        if (i >= 0) {
            // context/path
            path = url.substring(i + 1);
            // url变成 hadluo:[email protected]:20881
            url = url.substring(0, i);
        }
        i = url.indexOf("@");
        if (i >= 0) {
            // hadluo:1234
            username = url.substring(0, i);
            int j = username.indexOf(":");
            if (j >= 0) {
                // 1234
                password = username.substring(j + 1);
                // hadluo
                username = username.substring(0, j);
            }
            // 127.0.0.1:20881
            url = url.substring(i + 1);
        }
        i = url.indexOf(":");
        if (i >= 0 && i < url.length() - 1) {
            // 20881
            port = Integer.parseInt(url.substring(i + 1));
            // 127.0.0.1
            url = url.substring(0, i);
        }
        if (url.length() > 0)
            host = url;
        return new URL(protocol, username, password, host, port, path, parameters);
    }

    public String toString() {
        if (string != null) {
            return string;
        }
        // 打印成 string ,并缓存起来
        return string = buildString();
    }

    private String buildString() {
        StringBuilder buf = new StringBuilder();
        if (protocol != null && protocol.length() > 0) {
            buf.append(protocol);
            buf.append("://");
        }
        if (username != null && username.length() > 0) {
            buf.append(username);
            if (password != null && password.length() > 0) {
                buf.append(":");
                buf.append(password);
            }
            buf.append("@");
        }
        if (host != null && host.length() > 0) {
            buf.append(host);
            if (port > 0) {
                buf.append(":");
                buf.append(port);
            }
        }
        if (path != null && path.length() > 0) {
            buf.append("/");
            buf.append(path);
        }
        buildParameters(buf);
        return buf.toString();
    }

    private void buildParameters(StringBuilder buf) {
        if (this.parameters != null && this.parameters.size() > 0) {
            boolean first = true;
            for (Map.Entry entry : new TreeMap(this.parameters).entrySet()) {
                if (entry.getKey() != null && entry.getKey().length() > 0) {
                    if (first) {
                        // 第一个 参数 是添加 ?
                        buf.append("?");
                        first = false;
                    } else {
                        // 后面参数 是 &连接
                        buf.append("&");
                    }
                    buf.append(entry.getKey());
                    buf.append("=");
                    buf.append(entry.getValue() == null ? "" : entry.getValue().trim());
                }
            }
        }
    }
}

你可能感兴趣的:(dubbo源码)