dubbo服务的引用过程

dubbo服务的启动过程中,看到,所有的dubbo自定义标签都会由DubboNamespaceHandler处理, 遇到reference标签,如何发现服务的?

registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));

在Spring容器启动过程中,遇到reference标签会初始化一个ReferenceBean,在初始化这个bean的过程中,获取到服务提供方提供的proxy服务

官方文档:
reference

按照官方文档的说法,应该是由ReferenceConfig,调用DubboProtol获取DubboInvoker,然后通过ProxyFactory获取到下游ref对象的代理。

public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean {

    public Object getObject() throws Exception {
        //返回的真实对象
        return get();
    }
    public void afterPropertiesSet() throws Exception {
       //省略一万行代码
        Boolean b = isInit();
        if (b != null && b.booleanValue()) {
            getObject();
        }
    }
}

ReferenceBean对象定义实现了FactoryBean接口能够看到,该对象初始化完成之后返回的是getObject()方法的返回对象,实现了InitializingBean接口,在该对象初始化完成之后调用afterPropertiesSet(),最终都会调用ReferenceConfigget()方法获取ref对象。

// interface proxy reference
private transient volatile T ref;

public synchronized T get() {
        if (destroyed) {
            throw new IllegalStateException("Already destroyed!");
        }
        if (ref == null) {
            //初始化
            init();
        }
        return ref;
    }
 private void init() {
        if (initialized) {
            return;
        }
        initialized = true;
        //拼接URLkey=value参数,保存在Map中
        Map<String, String> map = new HashMap<String, String>();
        Map<Object, Object> attributes = new HashMap<Object, Object>();
        map.put(Constants.SIDE_KEY, Constants.CONSUMER_SIDE);
        map.put(Constants.DUBBO_VERSION_KEY, Version.getVersion());
        map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
        if (ConfigUtils.getPid() > 0) {
            map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
        }
        map.put(Constants.INTERFACE_KEY, interfaceName);
        appendParameters(map, application);
        appendParameters(map, module);
        appendParameters(map, consumer, Constants.DEFAULT_KEY);
        appendParameters(map, this);
        String prefix = StringUtils.getServiceKey(map);
        //忽略很多代码
        String hostToRegistry = ConfigUtils.getSystemProperty(Constants.DUBBO_IP_TO_REGISTRY);
        if (hostToRegistry == null || hostToRegistry.length() == 0) {
            hostToRegistry = NetUtils.getLocalHost();
        } else if (isInvalidLocalHost(hostToRegistry)) {
            throw new IllegalArgumentException("Specified invalid registry ip from property:" + Constants.DUBBO_IP_TO_REGISTRY + ", value:" + hostToRegistry);
        }
        map.put(Constants.REGISTER_IP_KEY, hostToRegistry);

        //attributes are stored by system context.
        StaticContext.getSystemContext().putAll(attributes);
        //重点在这里,创建一个Proxy,拿到ref对象。
        ref = createProxy(map);
        ConsumerModel consumerModel = new ConsumerModel(getUniqueServiceName(), this, ref, interfaceClass.getMethods());
        ApplicationModel.initConsumerModel(getUniqueServiceName(), consumerModel);
    }

在创建ref对象时,是通过注册中心配置,拼接URL,当注册中心只有一个服务时,直接去引用服务,当注册中心有多个服务时,则先将多个服务分别get到,然后对外统一组装成一个服务,然后通过代理工厂产生了一个代理对象。

//这里忽略很多代码 点对点,只分析dubbo协议,且只从注册中心订阅服务的场景
//这里加载配置中心
List<URL> us = loadRegistries(false);
if (us != null && us.size() > 0) {
    for (URL u : us) {
        //registry://224.5.6.7:1234/com.alibaba.dubbo.registry.RegistryService?application=test-protocol-random-port&dubbo=2.0.0&pid=2264&refer=application%3Dtest-protocol-random-port%26dubbo%3D2.0.0%26injvm%3Dfalse%26interface%3Dcom.alibaba.dubbo.config.api.DemoService%26methods%3DsayName%2CgetUsers%2Cecho%2CthrowDemoException%2CgetBox%26pid%3D2264%26register.ip%3D192.168.5.5%26side%3Dconsumer%26timestamp%3D1520518336336&registry=multicast&timestamp=1520518337358
        //组装URL
        urls.add(u.addParameterAndEncoded(Constants.REFER_KEY, StringUtils.toQueryString(map)));
    }
}
//忽略若干行代码
if (urls.size() == 1) {
    invoker = refprotocol.refer(interfaceClass, urls.get(0));
} else {
    List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();
    URL registryURL = null;
    for (URL url : urls) {
        invokers.add(refprotocol.refer(interfaceClass, url));
        if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
            registryURL = url; // use last registry url
        }
    }
    if (registryURL != null) { // registry url is available
        // use AvailableCluster only when register's cluster is available
        URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);
        invoker = cluster.join(new StaticDirectory(u, invokers));
    } else { // not a registry url
        invoker = cluster.join(new StaticDirectory(invokers));
    }
}
return (T) proxyFactory.getProxy(invoker);

引用服务,实质是组装一个DubboInvoker

//refprotocol.refer(interfaceClass, url)源码
 public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
    optimizeSerialization(url);
    // create rpc invoker.
    DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
    invokers.add(invoker);
    return invoker;
}

而将多个服务如何包装成一个服务?dubbo中有一层目录结构,directory,将多个invoker组装成一个FailoverClusterInvoker服务,实质就是一个invoker服务。

cluster.join(new StaticDirectory(invokers));
//cluster定义,用到spi
private static final Cluster cluster = ExtensionLoader.getExtensionLoader(Cluster.class).getAdaptiveExtension();

public class FailoverCluster implements Cluster {
    public final static String NAME = "failover";
    public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
        return new FailoverClusterInvoker<T>(directory);
    }
}
//FailoverClusterInvoker定义
public class FailoverClusterInvoker<T> extends AbstractClusterInvoker<T> 
//AbstractClusterInvoker定义
public abstract class AbstractClusterInvoker<T> implements Invoker<T> 

代理对象如何产生?这里和服务发布一样,使用proxyFactory获取一个代理对象,实质返回的是 InvokerInvocationHandler对象。

return (T) proxyFactory.getProxy(invoker);
private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();

public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
        return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}

这里有一个问题,当我们在调用的过程中,发现实际上调用的InvokerInvocationHandler对象,并不是FailoverClusterInvoker,而是MockClusterInvoker,这是为什么呢?是wrapper扩展点加载机制导致的。后面再介绍。

fyi

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352