继续浏览精彩内容
慕课网APP
程序员的梦工厂
打开
继续
感谢您的支持,我会继续努力的
赞赏金额会直接到老师账户
将二维码发送给自己后长按识别
微信支付
支付宝支付

Dubbo 服务暴露详解

慕姐8265434
关注TA
已关注
手记 1295
粉丝 222
获赞 1065

作为分布式框架,最核心的功能无非是服务的暴露和服务的引用,今天我们先说服务的暴露。

我们先从暴露服务配置说起

  @Bean
    public ServiceBean logServiceServiceBean(LogService logService){
        ServiceBean<LogService> serviceBean=new ServiceBean<>();
        serviceBean.setInterface(LogService.class);
        serviceBean.setRef(logService);        return serviceBean;
    }

我们声明了一个ServiceBean,他的接口类型是LogService.class,他的实现是注入进来的logService。既然他被封装成了ServiceBean,我们就有必要看看他是怎么实现的。ServiceBean实现了InitializingBean,我们就需要看看他是如何覆写其中的afterPropertiesSet方法的:

public void afterPropertiesSet() throws Exception {        if (getProvider() == null) {            Map<String, ProviderConfig> providerConfigMap = applicationContext == null ? null  : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProviderConfig.class, false, false);            if (providerConfigMap != null && providerConfigMap.size() > 0) {                Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null  : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class, false, false);                if ((protocolConfigMap == null || protocolConfigMap.size() == 0)
                        && providerConfigMap.size() > 1) { // 兼容旧版本
                    List<ProviderConfig> providerConfigs = new ArrayList<ProviderConfig>();                    for (ProviderConfig config : providerConfigMap.values()) {                        if (config.isDefault() != null && config.isDefault().booleanValue()) {
                            providerConfigs.add(config);
                        }
                    }                    if (providerConfigs.size() > 0) {
                        setProviders(providerConfigs);
                    }
                } else {
                    ProviderConfig providerConfig = null;                    for (ProviderConfig config : providerConfigMap.values()) {                        if (config.isDefault() == null || config.isDefault().booleanValue()) {                            if (providerConfig != null) {                                throw new IllegalStateException("Duplicate provider configs: " + providerConfig + " and " + config);
                            }
                            providerConfig = config;
                        }
                    }                    if (providerConfig != null) {
                        setProvider(providerConfig);
                    }
                }
            }
        }        if (getApplication() == null
                && (getProvider() == null || getProvider().getApplication() == null)) {            Map<String, ApplicationConfig> applicationConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ApplicationConfig.class, false, false);            if (applicationConfigMap != null && applicationConfigMap.size() > 0) {
                ApplicationConfig applicationConfig = null;                for (ApplicationConfig config : applicationConfigMap.values()) {                    if (config.isDefault() == null || config.isDefault().booleanValue()) {                        if (applicationConfig != null) {                            throw new IllegalStateException("Duplicate application configs: " + applicationConfig + " and " + config);
                        }
                        applicationConfig = config;
                    }
                }                if (applicationConfig != null) {
                    setApplication(applicationConfig);
                }
            }
        }        if (getModule() == null
                && (getProvider() == null || getProvider().getModule() == null)) {            Map<String, ModuleConfig> moduleConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ModuleConfig.class, false, false);            if (moduleConfigMap != null && moduleConfigMap.size() > 0) {
                ModuleConfig moduleConfig = null;                for (ModuleConfig config : moduleConfigMap.values()) {                    if (config.isDefault() == null || config.isDefault().booleanValue()) {                        if (moduleConfig != null) {                            throw new IllegalStateException("Duplicate module configs: " + moduleConfig + " and " + config);
                        }
                        moduleConfig = config;
                    }
                }                if (moduleConfig != null) {
                    setModule(moduleConfig);
                }
            }
        }        if ((getRegistries() == null || getRegistries().size() == 0)
                && (getProvider() == null || getProvider().getRegistries() == null || getProvider().getRegistries().size() == 0)
                && (getApplication() == null || getApplication().getRegistries() == null || getApplication().getRegistries().size() == 0)) {            Map<String, RegistryConfig> registryConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, RegistryConfig.class, false, false);            if (registryConfigMap != null && registryConfigMap.size() > 0) {
                List<RegistryConfig> registryConfigs = new ArrayList<RegistryConfig>();                for (RegistryConfig config : registryConfigMap.values()) {                    if (config.isDefault() == null || config.isDefault().booleanValue()) {
                        registryConfigs.add(config);
                    }
                }                if (registryConfigs != null && registryConfigs.size() > 0) {                    super.setRegistries(registryConfigs);
                }
            }
        }        if (getMonitor() == null
                && (getProvider() == null || getProvider().getMonitor() == null)
                && (getApplication() == null || getApplication().getMonitor() == null)) {            Map<String, MonitorConfig> monitorConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, MonitorConfig.class, false, false);            if (monitorConfigMap != null && monitorConfigMap.size() > 0) {
                MonitorConfig monitorConfig = null;                for (MonitorConfig config : monitorConfigMap.values()) {                    if (config.isDefault() == null || config.isDefault().booleanValue()) {                        if (monitorConfig != null) {                            throw new IllegalStateException("Duplicate monitor configs: " + monitorConfig + " and " + config);
                        }
                        monitorConfig = config;
                    }
                }                if (monitorConfig != null) {
                    setMonitor(monitorConfig);
                }
            }
        }        if ((getProtocols() == null || getProtocols().size() == 0)
                && (getProvider() == null || getProvider().getProtocols() == null || getProvider().getProtocols().size() == 0)) {            Map<String, ProtocolConfig> protocolConfigMap = applicationContext == null ? null  : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProtocolConfig.class, false, false);            if (protocolConfigMap != null && protocolConfigMap.size() > 0) {
                List<ProtocolConfig> protocolConfigs = new ArrayList<ProtocolConfig>();                for (ProtocolConfig config : protocolConfigMap.values()) {                    if (config.isDefault() == null || config.isDefault().booleanValue()) {
                        protocolConfigs.add(config);
                    }
                }                if (protocolConfigs != null && protocolConfigs.size() > 0) {                    super.setProtocols(protocolConfigs);
                }
            }
        }        if (getPath() == null || getPath().length() == 0) {            if (beanName != null && beanName.length() > 0 
                    && getInterface() != null && getInterface().length() > 0
                    && beanName.startsWith(getInterface())) {
                setPath(beanName);
            }
        }        if (! isDelay()) {            export();
        }
    }

从spring容器中获取bean,填充dubbo正常使用的属性,例如
Provider,Application,Moudle等。填充完后,有一个重要的方法就是export()。dubbo允许通过配置来进行延迟暴露,当然一般情况下是同步的,进入了doExport(),里面又是一通检查,检查完后,进入了我们今天要着重开始说明的doExportUrls()

   private void doExportUrls() {
        List<URL> registryURLs = loadRegistries(true);        for (ProtocolConfig protocolConfig : protocols) {
            doExportUrlsFor1Protocol(protocolConfig, registryURLs);
        }
    }

获取到注册中心的地址,是个列表,也就是说可以有多个注册中心,那么我们的注册中心是在我们的配置文件中这个Bean指定的

   @Bean
    public RegistryConfig registry() {
        RegistryConfig registryConfig = new RegistryConfig();
        registryConfig.setAddress("127.0.0.1:2181");
        registryConfig.setProtocol("zookeeper");        return registryConfig;
    }

上面我们已经找到了注册中心的地址,下面,我们就是要把我们需要被暴露的服务的信息放到注册中心上。

 private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {        String name = protocolConfig.getName();        if (name == null || name.length() == 0) {
            name = "dubbo";
        }        String host = protocolConfig.getHost();        if (provider != null && (host == null || host.length() == 0)) {
            host = provider.getHost();
        }
        boolean anyhost = false;        if (NetUtils.isInvalidLocalHost(host)) {
            anyhost = true;            try {
                host = InetAddress.getLocalHost().getHostAddress();
            } catch (UnknownHostException e) {
                logger.warn(e.getMessage(), e);
            }            if (NetUtils.isInvalidLocalHost(host)) {                if (registryURLs != null && registryURLs.size() > 0) {                    for (URL registryURL : registryURLs) {                        try {
                            Socket socket = new Socket();                            try {
                                SocketAddress addr = new InetSocketAddress(registryURL.getHost(), registryURL.getPort());
                                socket.connect(addr, 1000);
                                host = socket.getLocalAddress().getHostAddress();                                break;
                            } finally {                                try {
                                    socket.close();
                                } catch (Throwable e) {}
                            }
                        } catch (Exception e) {
                            logger.warn(e.getMessage(), e);
                        }
                    }
                }                if (NetUtils.isInvalidLocalHost(host)) {
                    host = NetUtils.getLocalHost();
                }
            }
        }

        Integer port = protocolConfig.getPort();        if (provider != null && (port == null || port == 0)) {
            port = provider.getPort();
        }
        final int defaultPort = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name).getDefaultPort();        if (port == null || port == 0) {
            port = defaultPort;
        }        if (port == null || port <= 0) {
            port = getRandomPort(name);            if (port == null || port < 0) {
                port = NetUtils.getAvailablePort(defaultPort);
                putRandomPort(name, port);
            }
            logger.warn("Use random available port(" + port + ") for protocol " + name);
        }        Map<String, String> map = new HashMap<String, String>();        if (anyhost) {
            map.put(Constants.ANYHOST_KEY, "true");
        }
        map.put(Constants.SIDE_KEY, Constants.PROVIDER_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()));
        }
        appendParameters(map, application);
        appendParameters(map, module);
        appendParameters(map, provider, Constants.DEFAULT_KEY);
        appendParameters(map, protocolConfig);
        appendParameters(map, this);        if (methods != null && methods.size() > 0) {            for (MethodConfig method : methods) {
                appendParameters(map, method, method.getName());                String retryKey = method.getName() + ".retry";                if (map.containsKey(retryKey)) {                    String retryValue = map.remove(retryKey);                    if ("false".equals(retryValue)) {
                        map.put(method.getName() + ".retries", "0");
                    }
                }
                List<ArgumentConfig> arguments = method.getArguments();                if (arguments != null && arguments.size() > 0) {                    for (ArgumentConfig argument : arguments) {                        //类型自动转换.
                        if(argument.getType() != null && argument.getType().length() >0){
                            Method[] methods = interfaceClass.getMethods();                            //遍历所有方法
                            if(methods != null && methods.length > 0){                                for (int i = 0; i < methods.length; i++) {                                    String methodName = methods[i].getName();                                    //匹配方法名称,获取方法签名.
                                    if(methodName.equals(method.getName())){
                                        Class<?>[] argtypes = methods[i].getParameterTypes();                                        //一个方法中单个callback
                                        if (argument.getIndex() != -1 ){                                            if (argtypes[argument.getIndex()].getName().equals(argument.getType())){
                                                appendParameters(map, argument, method.getName() + "." + argument.getIndex());
                                            }else {                                                throw new IllegalArgumentException("argument config error : the index attribute and type attirbute not match :index :"+argument.getIndex() + ", type:" + argument.getType());
                                            }
                                        } else {                                            //一个方法中多个callback
                                            for (int j = 0 ;j<argtypes.length ;j++) {
                                                Class<?> argclazz = argtypes[j];                                                if (argclazz.getName().equals(argument.getType())){
                                                    appendParameters(map, argument, method.getName() + "." + j);                                                    if (argument.getIndex() != -1 && argument.getIndex() != j){                                                        throw new IllegalArgumentException("argument config error : the index attribute and type attirbute not match :index :"+argument.getIndex() + ", type:" + argument.getType());
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }else if(argument.getIndex() != -1){
                            appendParameters(map, argument, method.getName() + "." + argument.getIndex());
                        }else {                            throw new IllegalArgumentException("argument config must set index or type attribute.eg: <dubbo:argument index='0' .../> or <dubbo:argument type=xxx .../>");
                        }

                    }
                }
            } // end of methods for
        }        if (generic) {
            map.put("generic", String.valueOf(true));
            map.put("methods", Constants.ANY_VALUE);
        } else {            String revision = Version.getVersion(interfaceClass, version);            if (revision != null && revision.length() > 0) {
                map.put("revision", revision);
            }            String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();            if(methods.length == 0) {
                logger.warn("NO method found in service interface " + interfaceClass.getName());
                map.put("methods", Constants.ANY_VALUE);
            }            else {
                map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
            }
        }        if (! ConfigUtils.isEmpty(token)) {            if (ConfigUtils.isDefault(token)) {
                map.put("token", UUID.randomUUID().toString());
            } else {
                map.put("token", token);
            }
        }        if ("injvm".equals(protocolConfig.getName())) {
            protocolConfig.setRegister(false);
            map.put("notify", "false");
        }        // 导出服务
        String contextPath = protocolConfig.getContextpath();        if ((contextPath == null || contextPath.length() == 0) && provider != null) {
            contextPath = provider.getContextpath();
        }
        URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);        if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                .hasExtension(url.getProtocol())) {
            url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                    .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
        }        String scope = url.getParameter(Constants.SCOPE_KEY);        //配置为none不暴露
        if (! Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {            //配置不是remote的情况下做本地暴露 (配置为remote,则表示只暴露远程服务)
            if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
                exportLocal(url);
            }            //如果配置不是local则暴露为远程服务.(配置为local,则表示只暴露远程服务)
            if (! Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope) ){                if (logger.isInfoEnabled()) {
                    logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                }                if (registryURLs != null && registryURLs.size() > 0
                        && url.getParameter("register", true)) {                    for (URL registryURL : registryURLs) {
                        url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic"));
                        URL monitorUrl = loadMonitor(registryURL);                        if (monitorUrl != null) {
                            url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString());
                        }                        if (logger.isInfoEnabled()) {
                            logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
                        }
                        Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));

                        Exporter<?> exporter = protocol.export(invoker);
                        exporters.add(exporter);
                    }
                } else {
                    Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);

                    Exporter<?> exporter = protocol.export(invoker);
                    exporters.add(exporter);
                }
            }
        }        this.urls.add(url);
    }

这个方法有点长,但是没关系,我们一点点来,首先思考一个问题:如果给你了注册中心的地址,和要暴露的服务的信息,你会怎么做这个暴露?如果是我,我会这么做:暴露服务,首先要让人知道的是你这个服务的ip和端口,这样引用者才知道怎么连接,连上服务器后,我们在考虑调用哪个服务模块的有哪些方法可以让引用者调用,如果确定要调用哪个服务的哪个方法,只有将这些信息都知道,引用才能知道怎么发起这个调用。所以我们再暴露服务的时候,IP,端口,服务名称,方法名称,是要放入到注册中心的。因为最终服务的引用者是和注册中心进行交互的,获取信息的。那么确实dubbo也是这么做的,它将所有的数据都放到自定义的URL的实例中,然后,将注册中心的信息和URL的信息整合得到一起,到注册中心进行暴露服务。也就是下面这个关键的步骤

                        Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));

我们看下registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString())
拼成的字符串经过decode后是什么样的
registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=log&dubbo=2.5.3&export=dubbo://192.168.1.102:20880/com.linyang.test.service.LogService?anyhost=true&application=log&default.proxy=javassist&default.retries=0&default.timeout=30000&default.version=LATEST&dubbo=2.5.3&interface=com.linyang.test.service.LogService&methods=modify,create&pid=4917&side=provider&threads=100&timestamp=1525576255082&pid=4917&registry=zookeeper&timestamp=1525575179509剩下的就看会怎么利用这个字符串了,利用ref这个接口的实现类和接口类型和这个URL创建了一个代理的invoker.根据上下文判断是javasist类型的ProxyFactory,他内部创建了一个AbstractProxyInvoker类型的类的实例,里面存储了传进来的三个值。我们得到了这个invoker后,就可以暴露他了,暴露的对象是invoker,这是一个关键点

Exporter<?> exporter = protocol.export(invoker);

我们看到是协议protocal进行暴露的,这个协议最终调用的是registryProtocol。说到这里大家有可能会有疑问,不是应该是DubboProtocol吗,其实不是这样,我们可以看下Protocol类

/*
 * Copyright 1999-2011 Alibaba Group.
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */package com.alibaba.dubbo.rpc;import com.alibaba.dubbo.common.URL;import com.alibaba.dubbo.common.extension.Adaptive;import com.alibaba.dubbo.common.extension.SPI;/**
 * Protocol. (API/SPI, Singleton, ThreadSafe)
 * 
 * @author william.liangf
 */@SPI("dubbo")public interface Protocol {    
    /**
     * 获取缺省端口,当用户没有配置端口时使用。
     * 
     * @return 缺省端口
     */
    int getDefaultPort();    /**
     * 暴露远程服务:<br>
     * 1. 协议在接收请求时,应记录请求来源方地址信息:RpcContext.getContext().setRemoteAddress();<br>
     * 2. export()必须是幂等的,也就是暴露同一个URL的Invoker两次,和暴露一次没有区别。<br>
     * 3. export()传入的Invoker由框架实现并传入,协议不需要关心。<br>
     * 
     * @param <T> 服务的类型
     * @param invoker 服务的执行体
     * @return exporter 暴露服务的引用,用于取消暴露
     * @throws RpcException 当暴露服务出错时抛出,比如端口已占用
     */
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;    /**
     * 引用远程服务:<br>
     * 1. 当用户调用refer()所返回的Invoker对象的invoke()方法时,协议需相应执行同URL远端export()传入的Invoker对象的invoke()方法。<br>
     * 2. refer()返回的Invoker由协议实现,协议通常需要在此Invoker中发送远程请求。<br>
     * 3. 当url中有设置check=false时,连接失败不能抛出异常,并内部自动恢复。<br>
     * 
     * @param <T> 服务的类型
     * @param type 服务的类型
     * @param url 远程服务的URL地址
     * @return invoker 服务的本地代理
     * @throws RpcException 当连接服务提供方失败时抛出
     */
    @Adaptive
    <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;    /**
     * 释放协议:<br>
     * 1. 取消该协议所有已经暴露和引用的服务。<br>
     * 2. 释放协议所占用的所有资源,比如连接和端口。<br>
     * 3. 协议在释放后,依然能暴露和引用新的服务。<br>
     */
    void destroy();

}

在export方法上有@Adaptive注解,这个注解写在方法上,有个作用,可以根据传入的URL来指定需要的协议,上面的URL中指名了是registry,所以他使用的应该是registryProtocol协议,那么我们想的那个DubboProtocol是在什么地方呢?不用急,是在RegistryProtocal里面做的。我们继续说。但是是不是只调了registryProtocol?当时不是,还记得在@adaptive那个注解的时候说扫描,有提到warpper类,就是将协议进行包裹,所以这个protocal是融合了ProtocolFilterWrapper和ProtocolListenerWrapper和DubboProtocal三者,先执行
ProtocolFilterWrapper和ProtocolListenerWrapper,他们针对registry协议没做啥,直接进行下一步,进入了RegistryProtocol的export

public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {        //export invoker
        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);        //获得注册中心
        final Registry registry = getRegistry(originInvoker);        //获得要注册的链接,也就是真正的要暴露的服务
        final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);        //想注册中心注册
        registry.register(registedProviderUrl);        // 订阅override数据
        // FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,因为subscribed以服务名为缓存的key,导致订阅信息覆盖。
        final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);        final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl);
        overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
        registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);        //保证每次export都返回一个新的exporter实例
        return new Exporter<T>() {            public Invoker<T> getInvoker() {                return exporter.getInvoker();
            }            public void unexport() {                try {
                    exporter.unexport();
                } catch (Throwable t) {
                    logger.warn(t.getMessage(), t);
                }                try {
                    registry.unregister(registedProviderUrl);
                } catch (Throwable t) {
                    logger.warn(t.getMessage(), t);
                }                try {
                    overrideListeners.remove(overrideSubscribeUrl);
                    registry.unsubscribe(overrideSubscribeUrl, overrideSubscribeListener);
                } catch (Throwable t) {
                    logger.warn(t.getMessage(), t);
                }
            }
        };
    }

注册协议做的事情很简单先进行本地暴露,获取响应的注册中心,想注册中心注册要暴露的服务,设置订阅,注册中心信息发生改变后会通知服务,进行数据的更新。说起来很容易,但是其实里面封装了大量的内容。首先我们来看下本地暴露的方法doLocalExport

 private <T> ExporterChangeableWrapper<T>  doLocalExport(final Invoker<T> originInvoker){
        String key = getCacheKey(originInvoker);
        ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);        if (exporter == null) {            synchronized (bounds) {
                exporter = (ExporterChangeableWrapper<T>) bounds.get(key);                if (exporter == null) {                    final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));
                    exporter = new ExporterChangeableWrapper<T>((Exporter<T>)protocol.export(invokerDelegete), originInvoker);
                    bounds.put(key, exporter);
                }
            }
        }        return (ExporterChangeableWrapper<T>) exporter;
    }

延续了以往的Dubbo风格,使用了大量的本地缓存。根据传入的原始的invoker,获取到cacheKey,从本地缓存bounds中获取,获取不到,就开始了创建的过程。首先创建了一个包赚对象invokerDelegete,包含了原始的invoker和providerUrl,我们可以简单看下这个providerUrl.getProviderUrl(originInvoker)结果是

dubbo://192.168.1.102:20880/com.linyang.test.service.LogService?anyhost=true&application=log&default.proxy=javassist&default.retries=0&default.timeout=30000&default.version=LATEST&dubbo=2.5.3&interface=com.linyang.test.service.LogService&methods=modify,create&pid=19026&side=provider&threads=100&timestamp=1525594853055

看一下这个provider的链接提供的dubbo协议,所以,当我们本地暴露的时候(Exporter<T>)protocol.export(invokerDelegete)会发生什么?会真正调用的是DubboProtocol,当然我们不能忘了ProtocolFilterWrapper和ProtocolListenerWrapper。OK,那我们再次看看他们到底做了什么,先来看看ProtocolFilterWrapper

  public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {            return protocol.export(invoker);
        }        return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
    }

这个filter处理的时候建立了一条调用链InvokerChain

private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
        Invoker<T> last = invoker;
        List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);        if (filters.size() > 0) {            for (int i = filters.size() - 1; i >= 0; i --) {                final Filter filter = filters.get(i);                final Invoker<T> next = last;
                last = new Invoker<T>() {                    public Class<T> getInterface() {                        return invoker.getInterface();
                    }                    public URL getUrl() {                        return invoker.getUrl();
                    }                    public boolean isAvailable() {                        return invoker.isAvailable();
                    }                    public Result invoke(Invocation invocation) throws RpcException {                        return filter.invoke(next, invocation);
                    }                    public void destroy() {
                        invoker.destroy();
                    }                    @Override
                    public String toString() {                        return invoker.toString();
                    }
                };
            }
        }        return last;
    }

从SPI中获取激活的Filter类的实例,在@activite的那一节也讲过,他的主要用法是在Filter上,其实就是说的这里,然后将他们变成链式结构,保证他们再调用的时候,一个接着一个,当然这是常用的filter的使用模式。看完了ProtocolFilterWrapper,我们再看下他的下一个ProtocolListenerWrapper。

   public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {            return protocol.export(invoker);
        }        return new ListenerExporterWrapper<T>(protocol.export(invoker), 
                Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
                        .getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
    }

他做的就是讲Exporter包装成ListenerExporterWrapper的实例,他是原来的exporter和从spi扩展点中获取的ExporterListener的实例组成。下面到了我们的有一个重头戏:DubboProtocal的export的地方

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        URL url = invoker.getUrl();        
        // export service.
        String key = serviceKey(url);
        DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
        exporterMap.put(key, exporter);        
        //export an stub service for dispaching event
        Boolean isStubSupportEvent = url.getParameter(Constants.STUB_EVENT_KEY,Constants.DEFAULT_STUB_EVENT);        Boolean isCallbackservice = url.getParameter(Constants.IS_CALLBACK_SERVICE, false);        if (isStubSupportEvent && !isCallbackservice){            String stubServiceMethods = url.getParameter(Constants.STUB_EVENT_METHODS_KEY);            if (stubServiceMethods == null || stubServiceMethods.length() == 0 ){                if (logger.isWarnEnabled()){
                    logger.warn(new IllegalStateException("consumer [" +url.getParameter(Constants.INTERFACE_KEY) +                            "], has set stubproxy support event ,but no stub methods founded."));
                }
            } else {
                stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
            }
        }

        openServer(url);        
        return exporter;
    }

每次创建的时候都创建一个新的DubboExporter,并将其返回上层使用。到目前为止,我们还没有提到本地起服务,因为你想让其他方调用到自己,肯定是要开一个socket,但是目前我们还没有看到,但是不用急,他的剩下的内容都包含在openServer(url)根据providerUrl进行开启一个server

    private void openServer(URL url) {        // find server.
        String key = url.getAddress();        //client 也可以暴露一个只有server可以调用的服务。
        boolean isServer = url.getParameter(Constants.IS_SERVER_KEY,true);        if (isServer) {
            ExchangeServer server = serverMap.get(key);            if (server == null) {
                serverMap.put(key, createServer(url));
            } else {                //server支持reset,配合override功能使用
                server.reset(url);
            }
        }
    }

直击重点createServer(url)

private ExchangeServer createServer(URL url) {        //默认开启server关闭时发送readonly事件
        url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());        //默认开启heartbeat
        url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));        String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);        if (str != null && str.length() > 0 && ! ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str))            throw new RpcException("Unsupported server type: " + str + ", url: " + url);

        url = url.addParameter(Constants.CODEC_KEY, Version.isCompatibleVersion() ? COMPATIBLE_CODEC_NAME : DubboCodec.NAME);
        ExchangeServer server;        try {
            server = Exchangers.bind(url, requestHandler);
        } catch (RemotingException e) {            throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
        }
        str = url.getParameter(Constants.CLIENT_KEY);        if (str != null && str.length() > 0) {            Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();            if (!supportedTypes.contains(str)) {                throw new RpcException("Unsupported client type: " + str);
            }
        }        return server;
    }

我们看到了Exchangers.bind(url, requestHandler)Dubbo的Exchanger层,他具体的处理逻辑

   public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {        if (url == null) {            throw new IllegalArgumentException("url == null");
        }        if (handler == null) {            throw new IllegalArgumentException("handler == null");
        }
        url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");        return getExchanger(url).bind(url, handler);
    }

Exchanger的具体实现类是HeaderExchanger,所以调用他的bind方法

  public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {        return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
    }

出现了Dubbo的Transporter层,他的bind方法和exchange差不多,找具体的Transporter进行bind

   public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {        if (url == null) {            throw new IllegalArgumentException("url == null");
        }        if (handlers == null || handlers.length == 0) {            throw new IllegalArgumentException("handlers == null");
        }
        ChannelHandler handler;        if (handlers.length == 1) {
            handler = handlers[0];
        } else {
            handler = new ChannelHandlerDispatcher(handlers);
        }        return getTransporter().bind(url, handler);
    }

最后发现使用的是NettyTransporter,Netty大家灭有用过也应该听说过,他是现在最为流行的Nio网络框架。我们继续

   public Server bind(URL url, ChannelHandler listener) throws RemotingException {        return new NettyServer(url, listener);
    }

剩下的就是Netty的处理了,起一个Server,开启端口等着调用者连进来。OK这是DubboProtocol的export的过程。我们回到上面RegisterProtocol的部分

exporter = new ExporterChangeableWrapper<T>((Exporter<T>)protocol.export(invokerDelegete), originInvoker);

经过DubboProtocol暴露后得到一个具体的Exporter,将这个exporter和原始invoker封装到了ExporterChangeableWrapper里面,进行返回。好了这就是export的全过程。

测试源码

测试代码源码点击获取

预告,看这里

下一篇: Dubbo 服务引用详解

END



作者:数齐
链接:https://www.jianshu.com/p/71be0eb962dc


打开App,阅读手记
0人推荐
发表评论
随时随地看视频慕课网APP