dubbo--服务本地暴露

启动dubbo的demo服务,我们会看到一行日志

The service ready on spring started. service: org.apache.dubbo.demo.DemoService
复制代码

我们利用idea的搜索功能,找到它的出处。

 public void onApplicationEvent(ContextRefreshedEvent event) {
        if (isDelay() && !isExported() && !isUnexported()) {
            if (logger.isInfoEnabled()) {
                logger.info("The service ready on spring started. service: " + getInterface());
            }
            export();
        }
    }
复制代码

看到onApplicationEvent方法,我们要非常敏感的想到Spring。那好,我们看下该类的继承关系。


可以看到 ServiceBean扩展来一系列 Spring的扩展点。这里在容器刷新的时候就会开始暴露服务,这是我们的入口。

经过一些的检查,判断到了

首先拿到 registryURLs,然后分不同的协议暴露服务。
loadRegistries方法根据xml的配置拼装成一个URL。过程如下

 protected List<URL> loadRegistries(boolean provider) {
        List<URL> registryList = new ArrayList<URL>();
        if (registries != null && !registries.isEmpty()) {
            for (RegistryConfig config : registries) {
                String address = config.getAddress();
                if (address == null || address.length() == 0) {
                    address = Constants.ANYHOST_VALUE;
                }
                String sysaddress = System.getProperty("dubbo.registry.address");
                if (sysaddress != null && sysaddress.length() > 0) {
                    address = sysaddress;
                }
                if (address.length() > 0 && !RegistryConfig.NO_AVAILABLE.equalsIgnoreCase(address)) {
                    Map<String, String> map = new HashMap<String, String>();
                    appendParameters(map, application);
                    appendParameters(map, config);
                    map.put("path", RegistryService.class.getName());
                    map.put("dubbo", Version.getProtocolVersion());
                    map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));
                    if (ConfigUtils.getPid() > 0) {
                        map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));
                    }
                    if (!map.containsKey("protocol")) {
                        if (ExtensionLoader.getExtensionLoader(RegistryFactory.class).hasExtension("remote")) {
                            map.put("protocol", "remote");
                        } else {
                            map.put("protocol", "dubbo");
                        }
                    }
                    List<URL> urls = UrlUtils.parseURLs(address, map);
                    for (URL url : urls) {
                        url = url.addParameter(Constants.REGISTRY_KEY, url.getProtocol());
                        url = url.setProtocol(Constants.REGISTRY_PROTOCOL);
                        if ((provider && url.getParameter(Constants.REGISTER_KEY, true))
                                || (!provider && url.getParameter(Constants.SUBSCRIBE_KEY, true))) {
                            registryList.add(url);
                        }
                    }
                }
            }
        }
        return registryList;
    }
复制代码
 private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
      //..根据protocolConfig,registryURLs.组装url对象
    protocolConfig:    
    <dubbo:protocol name="dubbo" port="20880" id="dubbo" />  
    
    registryURLs:  
    registry://127.0.0.1:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=72954&qos.port=22222&registry=zookeeper&timestamp=1537430745031
    
    url对象:  
    dubbo://0.0.0.1:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=0.0.0.1&bind.port=20880&dubbo=2.0.2&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=72954&qos.port=22222&side=provider&timestamp=1537432276167  
    
        if (!Constants.SCOPE_NONE.equalsIgnoreCase(scope)) {
            if (!Constants.SCOPE_REMOTE.equalsIgnoreCase(scope)) {
                exportLocal(url);
            }
         
            if (!Constants.SCOPE_LOCAL.equalsIgnoreCase(scope)) {
            //...远程暴露
            }
        }
        this.urls.add(url);
    }
复制代码

我们进入exportLocal方法。

    private void exportLocal(URL url) {
    //url
    dubbo://0.0.0.1:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=0.0.0.1&bind.port=20880&dubbo=2.0.2&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=72954&qos.port=22222&side=provider&timestamp=1537432276167
    
        if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
            URL local = URL.valueOf(url.toFullString())
                    .setProtocol(Constants.LOCAL_PROTOCOL)
                    .setHost(LOCALHOST)
                    .setPort(0);
   // local:
   injvm://127.0.0.1/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=192.168.21.178&bind.port=20880&dubbo=2.0.2&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=72954&qos.port=22222&side=provider&timestamp=1537432276167
    
            ServiceClassHolder.getInstance().pushServiceClass(getServiceClass(ref));
     //proxyFactory是动态生成的. ProxyFactory$Adaptive     
            Exporter<?> exporter = protocol.export(
                    proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
            exporters.add(exporter);
            logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");
        }
    }
复制代码

我们进入getInvoker(ProxyFactory$Adaptive)方法


这里使用的是javassist的实现方式。

这里重点来了。

首先对实现类进行包装,返回一个wrapper类。(思想类似Spring的BeanWrapper)
getWrapper中调用makeWrapper。利用javasssit重新生成一个.class文件。 我们看下它的生成过程。

private static Wrapper makeWrapper(Class<?> c) {
        StringBuilder c1 = new StringBuilder("public void setPropertyValue(Object o, String n, Object v){ ");
        StringBuilder c2 = new StringBuilder("public Object getPropertyValue(Object o, String n){ ");
        StringBuilder c3 = new StringBuilder("public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws " + InvocationTargetException.class.getName() + "{ ");

        c1.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
        c2.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
        c3.append(name).append(" w; try{ w = ((").append(name).append(")$1); }catch(Throwable e){ throw new IllegalArgumentException(e); }");
        c1.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" filed or setter method in class " + c.getName() + ".\"); }");
        c2.append(" throw new " + NoSuchPropertyException.class.getName() + "(\"Not found property \\\"\"+$2+\"\\\" filed or setter method in class " + c.getName() + ".\"); }");

        // make class
        long id = WRAPPER_CLASS_COUNTER.getAndIncrement();
        ClassGenerator cc = ClassGenerator.newInstance(cl);
        cc.setClassName((Modifier.isPublic(c.getModifiers()) ? Wrapper.class.getName() : c.getName() + "$sw") + id);
        cc.setSuperClass(Wrapper.class);

        cc.addDefaultConstructor();
        cc.addField("public static String[] pns;"); // property name array.
        cc.addField("public static " + Map.class.getName() + " pts;"); // property type map.
        cc.addField("public static String[] mns;"); // all method name array.
        cc.addField("public static String[] dmns;"); // declared method name array.
        for (int i = 0, len = ms.size(); i < len; i++)
            cc.addField("public static Class[] mts" + i + ";");

        cc.addMethod("public String[] getPropertyNames(){ return pns; }");
        cc.addMethod("public boolean hasProperty(String n){ return pts.containsKey($1); }");
        cc.addMethod("public Class getPropertyType(String n){ return (Class)pts.get($1); }");
        cc.addMethod("public String[] getMethodNames(){ return mns; }");
        cc.addMethod("public String[] getDeclaredMethodNames(){ return dmns; }");
        //省略代码  
        }  
复制代码

我们看下最终生成的类。

public class Wrapper1 extends Wrapper {

    //省略代码

    public Object invokeMethod(Object o, String n, Class[] p, Object[] v) {
        com.alibaba.dubbo.demo.provider.DemoServiceImpl w;
        try{
           w = ((com.alibaba.dubbo.demo.provider.DemoServiceImpl)o);
        }catch(Throwable e){
        }
        try{
            if( "sayHello".equals( n )  &&  p.length == 1 ) {
                return w.sayHello((java.lang.String)v[0]);
            }
        } catch(Throwable e) {
        }
    }
}
复制代码

可以看到这里是通过直接调用的方式来调用目标方法,相比于JdkProxyFactory的反射调用,直接调用能带来更大的性能提升,因此JavassistProxyFactory也是dubbo的默认配置。

我们看下

 public AbstractProxyInvoker(T proxy, Class<T> type, URL url) {
       
        this.proxy = proxy;//DemoServiceImpl
        this.type = type;//DemoService
        this.url = url;
    }
复制代码

这里会返回一个invoke对象。

转载于:https://juejin.im/post/5ba34c16e51d450e7210a5c7

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值