【三】dubbo源码分析之服务提供方export(服务暴露)

本文详细剖析了Dubbo服务框架中服务暴露的具体流程,包括ServiceBean的export方法、doExport方法、doExportUrls方法以及涉及的主要组件和服务暴露的实现原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

上一章讲了服务提供方启动的时候会扫描所有@Service注册,并注册相应的ServiceBean。

spring发送ContextRefreshedEvent后每个ServiceBean会通过自生的export方法暴露。

这章详细讲export怎么暴露服务,注册到注册中心。

简介

暴露服务时序

/dev-guide/images/dubbo-export.jpg

服务提供者暴露一个服务的过程图

 /dev-guide/images/dubbo_rpc_export.jpg

ServiceBean.export

    public void export() {
        super.export();
        // Publish ServiceBeanExportedEvent
        publishExportEvent();
    }

重点在调用父类的export方法

ServiceConfig.export

    public synchronized void export() {

        //
        if (provider != null) {
            if (export == null) {
                export = provider.getExport();
            }
            if (delay == null) {
                delay = provider.getDelay();
            }
        }
        if (export != null && !export) {
            return;
        }

        //如果要进行延迟暴露,则开启一个子线程,在子线程中进行服务暴露的工作
        if (delay != null && delay > 0) {
            delayExportExecutor.schedule(new Runnable() {
                @Override
                public void run() {
                    doExport();
                }
            }, delay, TimeUnit.MILLISECONDS);
        } else {
            //立即暴露服务
            doExport();
        }
    }

做了2件事

1.如果要进行延迟暴露,则开启一个子线程,在子线程中进行服务暴露的工作

2.否则立即暴露服务

doExport方法

protected synchronized void doExport() {

        //如果是已经解除暴露的接口则抛出异常
        if (unexported) {
            throw new IllegalStateException("Already unexported!");
        }

        //如果已经暴露则返回
        if (exported) {
            return;
        }
        exported = true;
        if (interfaceName == null || interfaceName.length() == 0) {
            throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
        }

        //下面是检查各种配置和填充各种附属信息
        checkDefault();
        if (provider != null) {
            if (application == null) {
                application = provider.getApplication();
            }
            if (module == null) {
                module = provider.getModule();
            }
            if (registries == null) {
                registries = provider.getRegistries();
            }
            if (monitor == null) {
                monitor = provider.getMonitor();
            }
            if (protocols == null) {
                protocols = provider.getProtocols();
            }
        }
        if (module != null) {
            if (registries == null) {
                registries = module.getRegistries();
            }
            if (monitor == null) {
                monitor = module.getMonitor();
            }
        }
        if (application != null) {
            if (registries == null) {
                registries = application.getRegistries();
            }
            if (monitor == null) {
                monitor = application.getMonitor();
            }
        }
        if (ref instanceof GenericService) {
            interfaceClass = GenericService.class;
            if (StringUtils.isEmpty(generic)) {
                generic = Boolean.TRUE.toString();
            }
        } else {
            try {
                interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()
                        .getContextClassLoader());
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
            checkInterfaceAndMethods(interfaceClass, methods);
            checkRef();
            generic = Boolean.FALSE.toString();
        }

        //local属性已被弃用,由stub属性替代
        if (local != null) {
            if ("true".equals(local)) {
                local = interfaceName + "Local";
            }
            Class<?> localClass;
            try {
                localClass = ClassHelper.forNameWithThreadContextClassLoader(local);
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
            if (!interfaceClass.isAssignableFrom(localClass)) {
                throw new IllegalStateException("The local implementation class " + localClass.getName() + " not implement interface " + interfaceName);
            }
        }

                //stub设为true,表示使用缺省代理类名,即:接口名 + Local后缀,服务接口客户端本地代理类名,用于在客户端执行本地逻辑,如本地缓存等,该本地代理类的构造函数必须允许传入远程代理对象,构造函数如:public XxxServiceLocal(XxxService xxxService)

        if (stub != null) {
            if ("true".equals(stub)) {
                stub = interfaceName + "Stub";
            }
            Class<?> stubClass;
            try {
                stubClass = ClassHelper.forNameWithThreadContextClassLoader(stub);
            } catch (ClassNotFoundException e) {
                throw new IllegalStateException(e.getMessage(), e);
            }
            if (!interfaceClass.isAssignableFrom(stubClass)) {
                throw new IllegalStateException("The stub implementation class " + stubClass.getName() + " not implement interface " + interfaceName);
            }
        }
        checkApplication();
        checkRegistry();
        checkProtocol();
        appendProperties(this);
        checkStub(interfaceClass);
        checkMock(interfaceClass);
        if (path == null || path.length() == 0) {
            path = interfaceName;
        }

        //暴露
        doExportUrls();
        ProviderModel providerModel = new ProviderModel(getUniqueServiceName(), this, ref);
        ApplicationModel.initProviderModel(getUniqueServiceName(), providerModel);
    }

做了几件事:

1. 对要暴露的服务进行了一系列的检查

检查provider,application,module,registries,monitor这些参数是否为空,是否是GenericService类型的服务,检查要注册的bean的引用和方法等。

2.调用doExportUrls方法进行暴露

doExportUrls方法

   private void doExportUrls() {

        //获取注册中心的配置
        List<URL> registryURLs = loadRegistries(true);

        //获取配置的服务暴露协义
        for (ProtocolConfig protocolConfig : protocols) {

           //根据不同协议将服务以url形式暴露
            doExportUrlsFor1Protocol(protocolConfig, registryURLs);
        }
    }

做了几件事:

1.获取注册中心的配置

2.获取配置的服务暴露协义

3.根据不同协议将服务以url形式暴露

doExportUrlsFor1Protocol方法

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {

        //默认使用dubbo协议
        String name = protocolConfig.getName();
        if (name == null || name.length() == 0) {
            name = "dubbo";
        }


        //收集各类参数,放入map中,在为服务暴露做参数收集准备工作
        Map<String, String> map = new HashMap<String, String>();
        map.put(Constants.SIDE_KEY, Constants.PROVIDER_SIDE);
        map.put(Constants.DUBBO_VERSION_KEY, Version.getProtocolVersion());
        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);

        //解析服务接口的方法的特殊设置,我debug的时候这里直接是null
        if (methods != null && !methods.isEmpty()) {
            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.isEmpty()) {
                    for (ArgumentConfig argument : arguments) {
                        // convert argument type
                        //参数类型自动转换
                        if (argument.getType() != null && argument.getType().length() > 0) {
                            Method[] methods = interfaceClass.getMethods();
                            // visit all methods
                            //遍历所有方法
                            if (methods != null && methods.length > 0) {
                                for (int i = 0; i < methods.length; i++) {
                                    String methodName = methods[i].getName();
                                    // target the method, and get its signature
                                    //匹配方法名称,获取方法签名
                                    if (methodName.equals(method.getName())) {
                                        Class<?>[] argtypes = methods[i].getParameterTypes();
                                        // one callback in the method
                                        //一个方法中单个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 attribute not match :index :" + argument.getIndex() + ", type:" + argument.getType());
                                            }
                                        } else {
                                            //一个方法中多个callback
                                            // multiple callbacks in the method
                                            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 attribute 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 (ProtocolUtils.isGeneric(generic)) {
            map.put(Constants.GENERIC_KEY, generic);
            map.put(Constants.METHODS_KEY, Constants.ANY_VALUE);
        } else {
            String revision = Version.getVersion(interfaceClass, version);
            if (revision != null && revision.length() > 0) {
                map.put("revision", revision);
            }

          //获取包装类,包装类实质上就是通过javassist生成的类,当我们通过包装类对角调dubbo接口时实际调的还是接口对象的原有方法。此处包装起到的作用无非是可以以统一的代码的去调用用户提供的不同的dubbo接口。这个wrapper其实就是我理解的RPC调用中的代理类。
            String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
            if (methods.length == 0) {
                logger.warn("NO method found in service interface " + interfaceClass.getName());
                map.put(Constants.METHODS_KEY, Constants.ANY_VALUE);
            } else {
                map.put(Constants.METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
            }
        }
        if (!ConfigUtils.isEmpty(token)) {
            if (ConfigUtils.isDefault(token)) {
                map.put(Constants.TOKEN_KEY, UUID.randomUUID().toString());
            } else {
                map.put(Constants.TOKEN_KEY, token);
            }
        }
        if (Constants.LOCAL_PROTOCOL.equals(protocolConfig.getName())) {
            protocolConfig.setRegister(false);
            map.put("notify", "false");
        }
        // export service
        String contextPath = protocolConfig.getContextpath();
        if ((contextPath == null || contextPath.length() == 0) && provider != null) {
            contextPath = provider.getContextpath();
        }

        //获取host(四种方式的获取,1.从配置中获取,2.返回本机的ip地址,3.获取注册中心的主机名,4.遍历本地网卡,返回第一个合理的IP)
        String host = this.findConfigedHosts(protocolConfig, registryURLs, map);

        //获取各个协义需要暴露的端口.获取优先级:   
    //Protocol的实现类的默认端口 ——> Protocol的配置端口 ——> 随机端口
        Integer port = this.findConfigedPorts(protocolConfig, name, map);

        //拼接URL。Dubbo框架是以URL为总线的模式,即运行过程中所有的状态数据信息都可以通过URL来获取
        URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map);

        //这里的ConfiguratorFactory是个扩展,可以用来设计自己的URL组成规则
        if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                .hasExtension(url.getProtocol())) {
            url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                    .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
        }

       //根据scope的配置决定是作本地暴露还是远程暴露,做服务暴露从结果上看就是产生了一个特定服务的 Exporter 类,并将其存储在对应的ServiceBean实例的 exporters属性中
        String scope = url.getParameter(Constants.SCOPE_KEY);

        // 配置为none不暴露
        // don't export when none is configured
        if (!Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) {

            // 配置不是remote的情况下做本地暴露 (配置为remote,则表示只暴露远程服务)
            // export to local if the config is not remote (export to remote only when config is remote)
            if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) {
                exportLocal(url);
            }

            // 如果配置不是local则暴露为远程服务.(配置为local,则表示只暴露远程服务
            // export to remote if the config is not local (export to local only when config is local)
            if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) {
                if (logger.isInfoEnabled()) {
                    logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                }

                //只有远程暴露才需要用到注册中心url
                if (registryURLs != null && !registryURLs.isEmpty()) {
                    for (URL registryURL : registryURLs) {
                        url = url.addParameterIfAbsent(Constants.DYNAMIC_KEY, registryURL.getParameter(Constants.DYNAMIC_KEY));

                       //获取监控中心的URL,此处先暂不关心生成的细节,待到服务治理时才详细分析
                        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);
                        }

                        // For providers, this is used to enable custom proxy to generate invoker
                        String proxy = url.getParameter(Constants.PROXY_KEY);
                        if (StringUtils.isNotEmpty(proxy)) {
                            registryURL = registryURL.addParameter(Constants.PROXY_KEY, proxy);
                        }


                       //这里对参数做个介绍:
                          参数1:ref就是接口实现类   
                          参数2:interfaceClass:接口类   
                          参数3:在registryURL上添加参数,key为"export",value就是前面产生的服务协义的url
                        //proxyFactory的来历
                          ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension()
                          这个类实例最终就是com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory类的实例,这是由于ProxyFactory类上
                           的注解决定的
                        Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
                        DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                        Exporter<?> exporter = protocol.export(wrapperInvoker);
                        exporters.add(exporter);
                    }
                } else {

                  //这个是用来没有注册中心情况下的暴露,应该是用于直连的场景
                    Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);
                    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

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

 做了几件事:

1.收集各类参数,放入map中,在为服务暴露做参数收集准备工作

解析服务接口的方法的特殊设置,我debug的时候这里直接是null

获取包装类,包装类实质上就是通过javassist生成的类,当我们通过包装类对角调dubbo接口时实际调的还是接口对象的原有方法。此处包装起到的作用无非是可以以统一的代码的去调用用户提供的不同的dubbo接口

2.获取host

四种方式的获取:

protocolConfig.getHost() //从配置中获取

InetAddress.getLocalHost().getHostAddress() //返回本机的ip地址

registryURL.getHost() //获取注册中心的主机名

NetUtils.getLocalHost() //遍历本地网卡,返回第一个合理的IP

3.获取各个协义需要暴露的端口

获取优先级:

Protocol的实现类的默认端口 ——> Protocol的配置端口 ——> 随机端口

4.拼接URL

Dubbo框架是以URL为总线的模式,即运行过程中所有的状态数据信息都可以通过URL来获取。

比如当前系统采用什么序列化,采用什么通信,采用什么负载均衡等信息,都是通过URL的参数来呈现的。

所以在框架运行过程中,运行到某个阶段需要相应的数据,都可以通过对应的Key从URL的参数列表中获取。

debug的时候看到的URL是:

dubbo://XXX.XXX.XXX.XX:20880/org.apache.dubbo.samples.api.client.HelloService?

anyhost=true

&application=dubbo-annotation-provider

&bean.name=ServiceBean:org.apache.dubbo.samples.api.client.HelloService

&bind.ip=XXX.XXX.XXX.XX

&bind.port=20880

&default.timeout=1000

&dubbo=2.0.2

&generic=false

&interface=org.apache.dubbo.samples.api.client.HelloService

&methods=sayHello

&pid=6764

&side=provider

&timestamp=1603444165134

5.根据scope的配置决定是作本地暴露还是远程暴露。

做服务暴露从结果上看就是产生了一个特定服务的 Exporter 类,并将其存储在对应的ServiceBean实例的 exporters属性中

配置不是remote的情况下做本地暴露 (配置为remote,则表示只暴露远程服务)

如果配置不是local则暴露为远程服务.(配置为local,则表示只暴露远程服务)

debug的时候会发现,先做本地暴露,再做远程暴露

本地暴露:exportLocal方法

    private void exportLocal(URL url) {
        if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
            URL local = URL.valueOf(url.toFullString())
                    .setProtocol(Constants.LOCAL_PROTOCOL)
                    .setHost(LOCALHOST)
                    .setPort(0);

           // ServiceClassHolder是用来保存当前服务接口实例ref对应的Class的,是一个简单的单例实现
            StaticContext.getContext(Constants.SERVICE_IMPL_CLASS).put(url.getServiceKey(), getServiceClass(ref));
            Exporter<?> exporter = protocol.export(
                    proxyFactory.getInvoker(ref, (Class) interfaceClass, local));

            //把exporter加到一个list中
            exporters.add(exporter);
            logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");
        }
    }

debug的时候看到这个URL local的值是:

injvm://127.0.0.1/org.apache.dubbo.samples.api.client.HelloService?

anyhost=true

&application=dubbo-annotation-provider

&bean.name=ServiceBean:org.apache.dubbo.samples.api.client.HelloService

&bind.ip=XXX.XXX.XXX.XX

&bind.port=20880

&default.timeout=1000

&dubbo=2.0.2

&generic=false

&interface=org.apache.dubbo.samples.api.client.HelloService

&methods=sayHello

&pid=9504

&side=provider

&timestamp=1603444872012

做了几件事

1.得到local的URL

2.把当前服务接口实例ref对应的class缓存到ServiceClassHolder中

3.生成Invoker

4.从Invoker得到exporter

5.把exporter加到一个list中

下面重点来看这句话:

 Exporter<?> exporter = protocol.export(proxyFactory.getInvoker(ref, (Class) interfaceClass, local));

proxyFactory.getInvoker(ref, (Class) interfaceClass, local)

proxyFactory的来历

ServiceConfig类中初始化时进行实例化:

private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); 

这里用到dubbo的SPI机制,这个proxyFactory当是com.alibaba.dubbo.rpc.proxy.javassist.JavassistProxyFactory类的实例,也就是使用Javassist技术来产生Invoker对象。

ProxyFactory接口有两个方法:

                    <T> T getProxy(Invoker<T> invoker) throws RpcException; //供消费者使用

                     <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;  //服务提供者使用

JavassistProxyFactory实例的 getInvoker方法分析

       可以通过如下源码看出Invoker实例在被调用时是通过其内部的wrapper对象调用proxy对象来完成的

下面分析下这个方法传入的参数:

  •      第一个参数proxy传入是的ref变量  ,ref的定义:private T  ref; //接口实现类的引用  ,就是接口实现类的Class的实例 
  •      第二个参数 type  的定义是:private Class<?>            interfaceClass;  //也就是我们定义服务接口
  •      第三个参数url 比较理解,是由之前面的调用方法 doExportUrlsFor1Protocol(protocolConfig, registryURLs) 方法拼接出来的
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // TODO Wrapper类不能正确处理带$的类名
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
        return new AbstractProxyInvoker<T>(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName, 
                                      Class<?>[] parameterTypes, 
                                      Object[] arguments) throws Throwable {
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }

这里就是返回了一个AbstractProxyInvoker,换句话说这里就是返回的Invoker

InjvmProtocol的export方法分析

前置调用

1.Protocol$Adaptive的export方法

/**
 * Protocol. (API/SPI, Singleton, ThreadSafe)
 */
@SPI("dubbo")
public interface Protocol {

    /**
     * Export service for remote invocation: <br>
     * 1. Protocol should record request source address after receive a request:
     * RpcContext.getContext().setRemoteAddress();<br>
     * 2. export() must be idempotent, that is, there's no difference between invoking once and invoking twice when
     * export the same URL<br>
     * 3. Invoker instance is passed in by the framework, protocol needs not to care <br>
     *
     * @param <T>     Service type
     * @param invoker Service invoker
     * @return exporter reference for exported service, useful for unexport the service later
     * @throws RpcException thrown when error occurs during export the service, for example: port is occupied
     */
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

}

2.这里面的protocol是QosProtocolWrapper

    @Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
            startQosServer(invoker.getUrl());
            return protocol.export(invoker);
        }

       // 这里的protocol是ProtocolListenerWrapper
        return protocol.export(invoker);
    }

这里的protocol是ProtocolListenerWrapper 

3. 这里面的protocol是ProtocolListenerWrapper

    @Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {

        //这里不进,要远程暴露才进
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
            return protocol.export(invoker);
        }


        //这里的protocol是ProtocolFilterWrapper
        return new ListenerExporterWrapper<T>(protocol.export(invoker),
                Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
                        .getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
    }

这里的protocol是ProtocolFilterWrapper 

4.实际上调用的impl是ProtocolFilterWrapper

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

        //这里的protocol是InjvmProtocol
        return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
    }

5. 这里的protocol是InjvmProtocol

    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap);
    }

这里就是根据Invoker返回Exporter

远程暴露

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

                        Exporter<?> exporter = protocol.export(wrapperInvoker);
                        exporters.add(exporter);

1.调用proxyFactory的getInvoker方法

package com.alibaba.dubbo.rpc;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.Adaptive;
import com.alibaba.dubbo.common.extension.SPI;

/**
 * ProxyFactory. (API/SPI, Singleton, ThreadSafe)
 */
@SPI("javassist")
public interface ProxyFactory {

    /**
     * create invoker.
     *
     * @param <T>
     * @param proxy
     * @param type
     * @param url
     * @return invoker
     */
    @Adaptive({Constants.PROXY_KEY})
    <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException;

}

实际上是调用的ExtensionLoader的getExtensionLoader方法

2.ExtensionLoader的getExtensionLoader方法

public static <T> ExtensionLoader<T> getExtensionLoader(Class<T> type) {
        if (type == null)
            throw new IllegalArgumentException("Extension type == null");
        if (!type.isInterface()) {
            throw new IllegalArgumentException("Extension type(" + type + ") is not interface!");
        }
        if (!withExtensionAnnotation(type)) {
            throw new IllegalArgumentException("Extension type(" + type +
                    ") is not extension, because WITHOUT @" + SPI.class.getSimpleName() + " Annotation!");
        }

        ExtensionLoader<T> loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        if (loader == null) {
            EXTENSION_LOADERS.putIfAbsent(type, new ExtensionLoader<T>(type));
            loader = (ExtensionLoader<T>) EXTENSION_LOADERS.get(type);
        }
        return loader;
    }

这里返回的loader是com.alibaba.dubbo.common.extension.ExtensionLoader[com.alibaba.dubbo.rpc.ProxyFactory] 

3.然后调用ExtensionLoader的getExtension方法

入参name=javassist

    public T getExtension(String name) {
        if (name == null || name.length() == 0)
            throw new IllegalArgumentException("Extension name == null");
        if ("true".equals(name)) {
            return getDefaultExtension();
        }
        Holder<Object> holder = cachedInstances.get(name);
        if (holder == null) {
            cachedInstances.putIfAbsent(name, new Holder<Object>());
            holder = cachedInstances.get(name);
        }
        Object instance = holder.get();
        if (instance == null) {
            synchronized (holder) {
                instance = holder.get();
                if (instance == null) {
                    instance = createExtension(name);
                    holder.set(instance);
                }
            }
        }
        return (T) instance;
    }

这里返回的instance是StubProxyFactoryWrapper

4.StubProxyFactoryWrapper.getInvoker

    @Override
    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) throws RpcException {
        return proxyFactory.getInvoker(proxy, type, url);
    }

这里的proxyFactory是JavassistProxyFactory

5.JavassistProxyFactory

    @Override
    public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
        // TODO Wrapper cannot handle this scenario correctly: the classname contains '$'
        final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
        return new AbstractProxyInvoker<T>(proxy, type, url) {
            @Override
            protected Object doInvoke(T proxy, String methodName,
                                      Class<?>[] parameterTypes,
                                      Object[] arguments) throws Throwable {
                return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments);
            }
        };
    }

6.Protocol调用

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)
 */
@SPI("dubbo")
public interface Protocol {

    /**
     * Export service for remote invocation: <br>
     * 1. Protocol should record request source address after receive a request:
     * RpcContext.getContext().setRemoteAddress();<br>
     * 2. export() must be idempotent, that is, there's no difference between invoking once and invoking twice when
     * export the same URL<br>
     * 3. Invoker instance is passed in by the framework, protocol needs not to care <br>
     *
     * @param <T>     Service type
     * @param invoker Service invoker
     * @return exporter reference for exported service, useful for unexport the service later
     * @throws RpcException thrown when error occurs during export the service, for example: port is occupied
     */
    @Adaptive
    <T> Exporter<T> export(Invoker<T> invoker) throws RpcException;

}

7.这里又会调用ExtensionLoader.getExtensionLoader,入参type=interface com.alibaba.dubbo.rpc.Protocol

返回的loader=com.alibaba.dubbo.common.extension.ExtensionLoader[com.alibaba.dubbo.rpc.Protocol]

8.这里又会调用ExtensionLoader.getExtension,入参name=registry

返回的instance是QosProtocolWrapper

9.QosProtocolWrapper.export

    @Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {

            //这次debug是走的这个里面
            startQosServer(invoker.getUrl());
            return protocol.export(invoker);
        }

       //本地暴露的时候debug是走的这里
        return protocol.export(invoker);
    }

QosProtocolWrapper.startQosServer方法

private void startQosServer(URL url) {
        try {
            boolean qosEnable = url.getParameter(QOS_ENABLE,true);

            //debug的时候这个没进
            if (!qosEnable) {
                logger.info("qos won't be started because it is disabled. " +
                        "Please check dubbo.application.qos.enable is configured either in system property, " +
                        "dubbo.properties or XML/spring boot configuration.");
                return;
            }

            //debug的时候这个没进
            if (!hasStarted.compareAndSet(false, true)) {
                return;
            }

            int port = url.getParameter(QOS_PORT, DEFAULT_PORT);
            boolean acceptForeignIp = Boolean.parseBoolean(url.getParameter(ACCEPT_FOREIGN_IP,"false"));
            Server server = com.alibaba.dubbo.qos.server.Server.getInstance();
            server.setPort(port);
            server.setAcceptForeignIp(acceptForeignIp);
            server.start();

        } catch (Throwable throwable) {
            logger.warn("Fail to start qos server: ", throwable);
        }
    }

这里面主要就是server.start方法,跟进去可以看到是开始QOS服务并且绑定端口,这个是用的netty.

强调是开始qos服务,既是dubbo的在线运维功能。

Server类

public void start() throws Throwable {
        if (!hasStarted.compareAndSet(false, true)) {
            return;
        }
        boss = new NioEventLoopGroup(0, new DefaultThreadFactory("qos-boss", true));
        worker = new NioEventLoopGroup(0, new DefaultThreadFactory("qos-worker", true));
        ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(boss, worker);
        serverBootstrap.channel(NioServerSocketChannel.class);
        serverBootstrap.childOption(ChannelOption.TCP_NODELAY, true);
        serverBootstrap.childOption(ChannelOption.SO_REUSEADDR, true);
        serverBootstrap.childHandler(new ChannelInitializer<Channel>() {

            @Override
            protected void initChannel(Channel ch) throws Exception {
                ch.pipeline().addLast(new QosProcessHandler(welcome, acceptForeignIp));
            }
        });
        try {
            serverBootstrap.bind(port).sync();
            logger.info("qos-server bind localhost:" + port);
        } catch (Throwable throwable) {
            logger.error("qos-server can not bind localhost:" + port, throwable);
            throw throwable;
        }
    }

这里不细讲这个,后面会专门有一篇来讲dubbo的通讯底层跟netty相关的地方

10.ProtocolListenerWrapper.export

    @Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {

           //从这里返回,这里的protocol是ProtocolFilterWrapper
            return protocol.export(invoker);
        }
        return new ListenerExporterWrapper<T>(protocol.export(invoker),
                Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
                        .getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
    }

这里的protocol是ProtocolFilterWrapper 

11.ProtocolFilterWrapper.export

    @Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {

            //从这里就返回了,这里的protocol是RegistryProtocol 
            return protocol.export(invoker);
        }
        return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
    }

这里的protocol是RegistryProtocol 

 12.RegistryProtocol.export

@Override
    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
        //export invoker
        // 暴露invoker,就是服务暴露
        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);

         获取注册中心 URL,以 zookeeper 注册中心为例,得到的示例 URL 如下:
    //zookeeper://localhost:2181/com.alibaba.dubbo.registry.RegistryService?application=dubbo-annotation-provider&dubbo=2.0.2&export=dubbo%3A%2F%2FXXX.XXX.XXX.XXX%3A20880%2Forg.apache.dubbo.samples.api.client.HelloService%3Fanyhost%3Dtrue%26application%3Ddubbo-annotation-provider%26bean.name%3DServiceBean%3Aorg.apache.dubbo.samples.api.client.HelloService%26bind.ip%3DXXX.XXX.XXX.XXX%26bind.port%3D20880%26default.timeout%3D1000%26dubbo%3D2.0.2%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.samples.api.client.HelloService%26methods%3DsayHello%26pid%3D392%26side%3Dprovider%26timestamp%3D1603694032387&pid=392&timestamp=1603694031591
        URL registryUrl = getRegistryUrl(originInvoker);

        //registry provider
       // 根据 URL 加载 Registry 实现类,比如 ZookeeperRegistry
        final Registry registry = getRegistry(originInvoker);

       // 获取已注册的服务提供者 URL,比如:
    // dubbo://XXX.XXX.XXX.XXX:20880/org.apache.dubbo.samples.api.client.HelloService?anyhost=true&application=dubbo-annotation-provider&bean.name=ServiceBean:org.apache.dubbo.samples.api.client.HelloService&default.timeout=1000&dubbo=2.0.2&generic=false&interface=org.apache.dubbo.samples.api.client.HelloService&methods=sayHello&pid=392&side=provider&timestamp=1603694032387
        final URL registeredProviderUrl = getRegisteredProviderUrl(originInvoker);

        //to judge to delay publish whether or not
        // 获取 register 参数 来判断是否延迟发布
        boolean register = registeredProviderUrl.getParameter("register", true);

        // 向服务提供者与消费者注册表中注册服务提供者
        ProviderConsumerRegTable.registerProvider(originInvoker, registryUrl, registeredProviderUrl);

         // 根据 register 的值决定是否注册服务
        if (register) {

            // 向注册中心注册服务
            register(registryUrl, registeredProviderUrl);
            ProviderConsumerRegTable.getProviderWrapper(originInvoker).setReg(true);
        }

        // Subscribe the override data
        // FIXME When the provider subscribes, it will affect the scene : a certain JVM exposes the service and call the same service. Because the subscribed is cached key with the name of the service, it causes the subscription information to cover.
         // 获取订阅 URL,比如:
    //provider://XXX.XXX.XXX.XXX:20880/org.apache.dubbo.samples.api.client.HelloService?anyhost=true&application=dubbo-annotation-provider&bean.name=ServiceBean:org.apache.dubbo.samples.api.client.HelloService&category=configurators&check=false&default.timeout=1000&dubbo=2.0.2&generic=false&interface=org.apache.dubbo.samples.api.client.HelloService&methods=sayHello&pid=392&side=provider&timestamp=1603694032387
        final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registeredProviderUrl);
 
         // 创建监听器
        final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
        overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);

       // 向注册中心进行订阅 override 数据
        registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);

        //Ensure that a new exporter instance is returned every time export
        // 创建并返回 DestroyableExporter
        return new DestroyableExporter<T>(exporter, originInvoker, overrideSubscribeUrl, registeredProviderUrl);
    }

 做了几件事

1.暴露invoker,就是服务暴露,doLocalExport方法里面调用了DubboProtocol.export,生成并返回exporter

2.获取注册中心 URL,以 zookeeper 注册中心为例,得到的示例 URL 如下:

zookeeper://localhost:2181/com.alibaba.dubbo.registry.RegistryService?application=dubbo-annotation-provider&dubbo=2.0.2&export=dubbo%3A%2F%2FXXX.XXX.XXX.XXX%3A20880%2Forg.apache.dubbo.samples.api.client.HelloService%3Fanyhost%3Dtrue%26application

%3Ddubbo-annotation-provider%26bean.name%3DServiceBean%3Aorg.apache.dubbo.samples.api.client.HelloService%26bind.ip%3DXXX.XXX.XXX.XXX%26bind.port

%3D20880%26default.timeout%3D1000%26dubbo%3D2.0.2%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.samples.api.client.HelloService%26methods

%3DsayHello%26pid%3D392%26side%3Dprovider%26timestamp%3D1603694032387&pid=392&timestamp=1603694031591

3.根据 URL 加载 Registry 实现类,比如 ZookeeperRegistry

4.获取已注册的服务提供者 URL,

比如:  dubbo://XXX.XXX.XXX.XXX:20880/org.apache.dubbo.samples.api.client.HelloService?anyhost=true&application=dubbo-annotation-provider&bean.name=ServiceBean:org.apache.dubbo.samples.api.client.HelloService&default.timeout=1000

&dubbo=2.0.2&generic=false&interface=org.apache.dubbo.samples.api.client.HelloService&methods=sayHello&pid=392&side=provider&timestamp=1603694032387

5.获取 register 参数

6.向服务提供者与消费者注册表中注册服务提供者

7.根据 register 的值决定是否注册服务

8.获取订阅 URL,

比如: provider://XXX.XXX.XXX.XXX:20880/org.apache.dubbo.samples.api.client.HelloService?anyhost=true

&application=dubbo-annotation-provider&bean.name=ServiceBean:org.apache.dubbo.samples.api.client.HelloService

&category=configurators&check=false&default.timeout=1000&dubbo=2.0.2&generic=false

&interface=org.apache.dubbo.samples.api.client.HelloService&methods=sayHello&pid=392&side=provider&timestamp=1603694032387

9.创建监听器

10.向注册中心进行订阅 override 数据

11.创建并返回 DestroyableExporter

13.DubboProtocol.export

@Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
        URL url = invoker.getUrl();

        // export service.
        // 获取服务标识,理解成服务坐标也行。由服务组名,服务名,服务版本号以及端口组成。比如:
    // org.apache.dubbo.samples.api.client.HelloService:20880
        String key = serviceKey(url);

        // 创建 DubboExporter
        DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);

        // 将 <key, exporter> 键值对放入缓存中
        exporterMap.put(key, exporter);

        //export an stub service for dispatching event
         // 本地存根相关代码
        Boolean isStubSupportEvent = url.getParameter(Constants.STUB_EVENT_KEY, Constants.DEFAULT_STUB_EVENT);
        Boolean isCallbackservice = url.getParameter(Constants.IS_CALLBACK_SERVICE, false);

        //debug的时候这里面没走
        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);
      
        // 优化序列化
        optimizeSerialization(url);
        return exporter;
    }

做了几件事

1.获取服务标识,理解成服务坐标也行。由服务组名,服务名,服务版本号以及端口组成。

比如:org.apache.dubbo.samples.api.client.HelloService:20880

2.创建 DubboExporter

3.将 <key, exporter> 键值对放入缓存中

4.做本地存根。debug的时候这里的if里面没走

5.openServer启动服务器

6.optimizeSerialization优化序列化

 深入跟一下openServer这个方法

DubboProtocol.openServer

  private void openServer(URL url) {
        // find server.
        // 获取 host:port,并将其作为服务器实例的 key,用于标识当前的服务器实例
        String key = url.getAddress();
        //client can export a service which's only for server to invoke
        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 supports reset, use together with override
                // 服务器已创建,则根据 url 中的配置重置服务器
                server.reset(url);
            }
        }
    }

主要做了一件事,就是看服务实例ExchangeServer是否创建,如果没创建则创建,如果创建则根据RUL中的配置重置

在同一台机器上(单网卡),同一个端口上仅允许启动一个服务器实例。若某个端口上已有服务器实例,此时则调用 reset 方法重置服务器的一些配置 

DubboProtocol.createServer方法

private ExchangeServer createServer(URL url) {
        // send readonly event when server closes, it's enabled by default
        url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());

        // 添加心跳检测配置到 url 中
        // enable heartbeat by default
        url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));

        // 获取 server 参数,默认为 netty
        String str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);


        // 通过 SPI 检测是否存在 server 参数所代表的 Transporter 拓展,不存在则抛出异常
        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, DubboCodec.NAME);
        ExchangeServer server;
        try {

            // 创建 ExchangeServer
            server = Exchangers.bind(url, requestHandler);
        } catch (RemotingException e) {
            throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
        }

         // 获取 client 参数,可指定 netty,mina
        str = url.getParameter(Constants.CLIENT_KEY);
        if (str != null && str.length() > 0) {

            // 获取所有的 Transporter 实现类名称集合,比如 supportedTypes = [netty, mina]
            Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();

            // 检测当前 Dubbo 所支持的 Transporter 实现类名称列表中,
        // 是否包含 client 所表示的 Transporter,若不包含,则抛出异常
            if (!supportedTypes.contains(str)) {
                throw new RpcException("Unsupported client type: " + str);
            }
        }
        return server;
    }

做了几件事:

1.检测是否存在 server 参数所代表的 Transporter 拓展,不存在则抛出异常。

2.Exchangers.bind(url, requestHandler);创建服务器实例。

3.检测是否支持 client 参数所表示的 Transporter 拓展,不存在也是抛出异常。

 再跟进去看创建服务器实例的方法

Exchangers.bind

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");

        // 获取 Exchanger,默认为 HeaderExchanger。
    // 紧接着调用 HeaderExchanger 的 bind 方法创建 ExchangeServer 实例
        return getExchanger(url).bind(url, handler);
    }

获取 Exchanger,默认为 HeaderExchanger。然后调用 HeaderExchanger 的 bind 方法创建 ExchangeServer 实例 

HeaderExchanger.bind

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

HeaderExchangeServer的构造函数 里面有个开始心跳的方法startHeartbeatTimer();

Transporters.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 {

            // 如果 handlers 元素数量大于1,则创建 ChannelHandler 分发器
            handler = new ChannelHandlerDispatcher(handlers);
        }

        // 获取自适应 Transporter 实例,并调用实例方法
        return getTransporter().bind(url, handler);
    }

 

getTransporter() 方法获取的 Transporter 是在运行时动态创建的,类名为 TransporterAdaptive,也就是自适应拓展类。

TransporterAdaptive 会在运行时根据传入的 URL 参数决定加载什么类型的 Transporter,默认为 NettyTransporter

跟入NettyTransporter

NettyTransporter.bind

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

NettyServer的构造函数

    public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
        super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
    }

其实就是调用父类的构造函数

AbstractServer的构造函数

public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
        super(url, handler);
        localAddress = getUrl().toInetSocketAddress();


        // 获取 ip 和端口
        String bindIp = getUrl().getParameter(Constants.BIND_IP_KEY, getUrl().getHost());
        int bindPort = getUrl().getParameter(Constants.BIND_PORT_KEY, getUrl().getPort());
        if (url.getParameter(Constants.ANYHOST_KEY, false) || NetUtils.isInvalidLocalHost(bindIp)) {

            // 设置 ip 为 0.0.0.0
            bindIp = NetUtils.ANYHOST;
        }
        bindAddress = new InetSocketAddress(bindIp, bindPort);

        // 获取最大可接受连接数
        this.accepts = url.getParameter(Constants.ACCEPTS_KEY, Constants.DEFAULT_ACCEPTS);
        this.idleTimeout = url.getParameter(Constants.IDLE_TIMEOUT_KEY, Constants.DEFAULT_IDLE_TIMEOUT);
        try {

            // 调用模板方法 doOpen 启动服务器
            doOpen();
            if (logger.isInfoEnabled()) {
                logger.info("Start " + getClass().getSimpleName() + " bind " + getBindAddress() + ", export " + getLocalAddress());
            }
        } catch (Throwable t) {
            throw new RemotingException(url.toInetSocketAddress(), null, "Failed to bind " + getClass().getSimpleName()
                    + " on " + getLocalAddress() + ", cause: " + t.getMessage(), t);
        }
        //fixme replace this with better method
        DataStore dataStore = ExtensionLoader.getExtensionLoader(DataStore.class).getDefaultExtension();
        executor = (ExecutorService) dataStore.get(Constants.EXECUTOR_SERVICE_COMPONENT_KEY, Integer.toString(url.getPort()));
    }

这里主要就是

1.获取IP和端口

2.设置IP为0.0.0.0

3.获取最大可连接数

4.获取空闲超时时间

5.调用模板方法doOpen启动服务

NettyServer.doOpen方法

@Override
    protected void doOpen() throws Throwable {

        // 创建 ServerBootstrap
        bootstrap = new ServerBootstrap();

        bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("NettyServerBoss", true));
        workerGroup = new NioEventLoopGroup(getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),
                new DefaultThreadFactory("NettyServerWorker", true));

        final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);
        channels = nettyServerHandler.getChannels();

        bootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE)
                .childOption(ChannelOption.SO_REUSEADDR, Boolean.TRUE)
                .childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
                .childHandler(new ChannelInitializer<NioSocketChannel>() {
                    @Override
                    protected void initChannel(NioSocketChannel ch) throws Exception {
                        NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
                        ch.pipeline()//.addLast("logging",new LoggingHandler(LogLevel.INFO))//for debug
                                .addLast("decoder", adapter.getDecoder())
                                .addLast("encoder", adapter.getEncoder())
                                .addLast("handler", nettyServerHandler);
                    }
                });
        // bind
        ChannelFuture channelFuture = bootstrap.bind(getBindAddress());
        channelFuture.syncUninterruptibly();
        channel = channelFuture.channel();

    }

这里就是真正的启动了服务,在监听端口等待被人调用

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值