Dubbo Learning Record (11)--Start and run container for service export

Service export start running container and service registration

    protected synchronized void doExport() {
    
    
        if (unexported) {
    
    
            throw new IllegalStateException("The service " + interfaceClass.getName() + " has already unexported!");
        }
        // 已经导出了,就不再导出了
        if (exported) {
    
    
            return;
        }
        exported = true;

        if (StringUtils.isEmpty(path)) {
    
    
            path = interfaceName;
        }
        doExportUrls();
    }

Call layer by layer, and finally call the doExportUrlsFor1Protocol method

doExportUrlsFor1Protocol

Purpose: splice service URL, start container, register to registry;
work:

  1. Get protocol name;
  2. Create a parameter map for the service URL
  3. Get the role of the service (SIDE_KEY) and put it in the map
  4. Get the dubbo version information and put it into the map;
  5. Obtain the parameters configured by the monitoring center and put them into the map
  6. Obtain the parameters of the application configuration and put them into the map;
  7. Obtain the parameters of the module configuration and put them into the map;
  8. Get the parameters of the provider configuration and put them into the map;
  9. Obtain the parameters of the protocol configuration and put them into the map;
  10. Get the parameters of the service itself and put it into the map;
  11. Get the parameters of the Method configuration and put them into the map;
  12. Through the Wrapper corresponding to the interface, get all the method names in the interface and put them into the map ( I don’t know if I haven’t used it in depth )
  13. Get the token configuration of @Service and put it into the map;
  14. Get the host number host;
  15. Get the port number port
  16. Create a URL instance of the service, the parameters are the protocol name (1), the host number (14), the port number (15), the application name (configurable) + / + the interface name is the service name, and the parameter map;
  17. Get the value of SCOPE_KEY;
  18. If the SCOPE_KEY value is local, perform local registration;
  19. If the SCOPE_key value is remote, traverse the registration center and remotely register to each registration center;
  20. Register the metadata of the service with the metadata center;
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
    
    
        // protocolConfig表示某个协议,registryURLs表示所有的注册中心

        // 如果配置的某个协议,没有配置name,那么默认为dubbo
        String name = protocolConfig.getName();
        if (StringUtils.isEmpty(name)) {
    
    
            name = DUBBO;
        }

        // 这个map表示服务url的参数
        Map<String, String> map = new HashMap<String, String>();
        map.put(SIDE_KEY, PROVIDER_SIDE);

        appendRuntimeParameters(map);

        // 监控中心参数
        appendParameters(map, metrics);
        // 应用相关参数
        appendParameters(map, application);
        // 模块相关参数
        appendParameters(map, module);

        // 提供者相关参数
        appendParameters(map, provider);

        // 协议相关参数
        appendParameters(map, protocolConfig);

        // 服务本身相关参数
        appendParameters(map, this);

        // 服务中某些方法参数 暂时没用到,不知这个有什么用;
        if (CollectionUtils.isNotEmpty(methods)) {
    
    
           
        }

        if (ProtocolUtils.isGeneric(generic)) {
    
    
            map.put(GENERIC_KEY, generic);
            map.put(METHODS_KEY, ANY_VALUE);
        } else {
    
    
            String revision = Version.getVersion(interfaceClass, version);
            if (revision != null && revision.length() > 0) {
    
    
                map.put(REVISION_KEY, revision);
            }

            // 通过接口对应的Wrapper,拿到接口中所有的方法名字
            String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();
            if (methods.length == 0) {
    
    
                logger.warn("No method found in service interface " + interfaceClass.getName());
                map.put(METHODS_KEY, ANY_VALUE);
            } else {
    
    
                map.put(METHODS_KEY, StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ","));
            }
        }

        // Token是为了防止服务被消费者直接调用(伪造http请求)
        if (!ConfigUtils.isEmpty(token)) {
    
    
            if (ConfigUtils.isDefault(token)) {
    
    
                map.put(TOKEN_KEY, UUID.randomUUID().toString());
            } else {
    
    
                map.put(TOKEN_KEY, token);
            }
        }

        // export service
        // 通过该host和port访问该服务
        String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
        Integer port = this.findConfigedPorts(protocolConfig, name, map);
        // 服务url
        URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);

        // 可以通过ConfiguratorFactory,在服务导出时候进行统一配置
        if (ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                .hasExtension(url.getProtocol())) {
    
    
            url = ExtensionLoader.getExtensionLoader(ConfiguratorFactory.class)
                    .getExtension(url.getProtocol()).getConfigurator(url).configure(url);
        }

        String scope = url.getParameter(SCOPE_KEY); // scope可能为null,remote, local,none
        // don't export when none is configured
        if (!SCOPE_NONE.equalsIgnoreCase(scope)) {
    
    
            // 如果scope为none,则不会进行任何的服务导出,既不会远程,也不会本地

            // export to local if the config is not remote (export to remote only when config is remote)
            if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
    
    
                // 如果scope不是remote,则会进行本地导出,会把当前url的protocol改为injvm,然后进行导出
                exportLocal(url);
            }
            // export to remote if the config is not local (export to local only when config is local)
            if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
    
    
                // 如果scope不是local,则会进行远程导出

                if (CollectionUtils.isNotEmpty(registryURLs)) {
    
    
                    // 如果有注册中心,则将服务注册到注册中心
                    for (URL registryURL : registryURLs) {
    
    

                        //if protocol is only injvm ,not register
                        // 如果是injvm,则不需要进行注册中心注册
                        if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
    
    
                            continue;
                        }

                        // 该服务是否是动态,对应zookeeper上表示是否是临时节点,对应dubbo中的功能就是静态服务
                        url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));

                        // 基于注册中心地址的到监控中心地址,为什么是基于注册中心地址?
                        URL monitorUrl = loadMonitor(registryURL);

                        // 把监控中心地址添加到服务url中
                        if (monitorUrl != null) {
    
    
                            url = url.addParameterAndEncoded(MONITOR_KEY, monitorUrl.toFullString());
                        }

                        // 服务的register参数,如果为true,则表示要注册到注册中心
                        if (logger.isInfoEnabled()) {
    
    
             
                        }

                        // For providers, this is used to enable custom proxy to generate invoker
                        // 服务使用的动态代理机制,如果为空则使用javassit
                        String proxy = url.getParameter(PROXY_KEY);
                        if (StringUtils.isNotEmpty(proxy)) {
    
    
                            registryURL = registryURL.addParameter(PROXY_KEY, proxy);
                        }


                        // 此invoker表示一个可执行的服务,调用invoker的invoke()方法即可执行服务,同时此invoker也可用来导出
                        Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));

                        // DelegateProviderMetaDataInvoker也表示服务提供者,包括了Invoker和服务的配置
                        DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                        // 使用特定的协议来对服务进行导出,这里的协议为RegistryProtocol,导出成功后得到一个Exporter
                        // 1. 先使用RegistryProtocol进行服务注册
                        // 2. 注册完了之后,使用DubboProtocol进行导出
                        Exporter<?> exporter = protocol.export(wrapperInvoker);
                        exporters.add(exporter);
                    }
                } else {
    
    
                    // 没有配置注册中心时,也会导出服务

                    if (logger.isInfoEnabled()) {
    
    
                        logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                    }


                    Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
                    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

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


                /**
                 * @since 2.7.0
                 * ServiceData Store
                 */
                // 根据服务url,讲服务的元信息存入元数据中心
                MetadataReportService metadataReportService = null;
                if ((metadataReportService = getMetadataReportService()) != null) {
    
    
                    metadataReportService.publishProvider(url);
                }
            }
        }
        this.urls.add(url);
    }

Traverse the registry URL collection

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
    
    
		//....省略部分代码
        // 服务url
        URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);

        String scope = url.getParameter(SCOPE_KEY); // scope可能为null,remote, local,none
        // don't export when none is configured
        if (!SCOPE_NONE.equalsIgnoreCase(scope)) {
    
    
            // 如果scope为none,则不会进行任何的服务导出,既不会远程,也不会本地

            // export to local if the config is not remote (export to remote only when config is remote)
            if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
    
    
                // 如果scope不是remote,则会进行本地导出,会把当前url的protocol改为injvm,然后进行导出
                exportLocal(url);
            }
            // export to remote if the config is not local (export to local only when config is local)
            if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
    
    
                // 如果scope不是local,则会进行远程导出

                if (CollectionUtils.isNotEmpty(registryURLs)) {
    
    
                    // 如果有注册中心,则将服务注册到注册中心
                    for (URL registryURL : registryURLs) {
    
    

                        //if protocol is only injvm ,not register
                        // 如果本地协议, 即是injvm,则不需要进行注册中心注册
                        if (LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
    
    
                            continue;
                        }
						//从服务URL中获取dynamic参数的值, 如果URL中没有,则从注册中心URL中获取,并放入服务URL的参数
                        // 该服务是否是动态,对应zookeeper上表示是否是临时节点,对应dubbo中的功能就是静态服务
                        url = url.addParameterIfAbsent(DYNAMIC_KEY, registryURL.getParameter(DYNAMIC_KEY));

                        // 基于注册中心地址的到监控中心地址,为什么是基于注册中心地址?
                        //这个有点疑问,可能是监控中心需要知道服务URL的信息,进而监控服务的请求调用情况等;
                        URL monitorUrl = loadMonitor(registryURL);

                        // 把监控中心地址添加到服务url中
                        if (monitorUrl != null) {
    
    
                            url = url.addParameterAndEncoded(MONITOR_KEY, monitorUrl.toFullString());
                        }

                        // 服务的register参数,如果为true,则表示要注册到注册中心
                        if (logger.isInfoEnabled()) {
    
    
                            if (url.getParameter(REGISTER_KEY, true)) {
    
    
                                logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL);
                            } else {
    
    
                                logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                            }
                        }
						//获取服务的代理机制, Dubbo使用javvassist和jdk动态代理;默认不配置的情况下,使用的是Javaassist;
                        // 服务使用的动态代理机制,如果为空则使用javassit, 并放入注册中心URL的参数中;
                        String proxy = url.getParameter(PROXY_KEY);
                        if (StringUtils.isNotEmpty(proxy)) {
    
    
                            registryURL = registryURL.addParameter(PROXY_KEY, proxy);
                        }

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

            
                        DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);


                        Exporter<?> exporter = protocol.export(wrapperInvoker);
                        exporters.add(exporter);
                    }
                } else {
    
    
                    // 没有配置注册中心时,也会导出服务

                    if (logger.isInfoEnabled()) {
    
    
                        logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url);
                    }
                    Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, url);
                    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

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

			//省略部分代码;
            }
        }
        this.urls.add(url);
    }
  • The Invoker object of the service is generated through the dynamic proxy, and the method of the service is executed by calling the Invoker#invoke method;
  • The export will be used as the key, and the service URL instance will be used as the value, which will be put into the URL parameter of the registration center
  • Pass in the service implementation class, service interface, and URL of the registration center, so this invoker is only for the current service;
  • Then wrap the Invoker proxy object instance, pass in the current instance this, and generate a DelegateProviderMetaDataInvoker wrapper object;
  • Call the export method whose protocol is RegistryProtocol, register the packaging object, and get an Exporter after the export is successful
  • In the process of calling the Registryprotocol#export method, DubboProtocol/HttpProtocol will be called to start the Netty/Tomcat running container. After the startup is complete, the service invoker will be registered in the registry;
                        Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));

                        // DelegateProviderMetaDataInvoker也表示服务提供者,包括了Invoker和服务的配置
                        DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
                       Exporter<?> exporter = protocol.export(wrapperInvoker);

RegistryProtocol#export(final Invoker originInvoker)

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

		//originInvoker的协议名称为registry, 而我们使用的zookeeper注册中心, 所以需要吧
		//registry协议名称替换为 zookeepe,再把registry=zookeeper参数去掉;
        // 将registry://xxx?xx=xx&registry=zookeeper 转为---> zookeeper://xxx?xx=xx
        URL registryUrl = getRegistryUrl(originInvoker); 
        // 得到服务提供者url
        URL providerUrl = getProviderUrl(originInvoker); //
        // overrideSubscribeUrl是老版本的动态配置监听url,表示了需要监听的服务以及监听的类型(configurators, 这是老版本上的动态配置)
        // 在服务提供者url的基础上,生成一个overrideSubscribeUrl,协议为provider:
        //增加参数category=configurators&check=false;
        final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);

        // 一个overrideSubscribeUrl对应一个OverrideListener,用来监听变化事件,监听到overrideSubscribeUrl的变化后,
        // OverrideListener就会根据变化进行相应处理,具体处理逻辑看OverrideListener的实现
        final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
        overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener)

        providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);

        // export invoker
        // 根据动态配置重写了providerUrl之后,就会调用DubboProtocol或HttpProtocol去进行导出服务了,
        //会启动容器, DubboProtocol则是netty,HttpProtocol则是tomcat; 
        final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);

        // 得到注册中心-ZookeeperRegistry
        final Registry registry = getRegistry(originInvoker);

        // 得到存入到注册中心去的providerUrl,会对服务提供者url中的参数进行简化
        //providerUrl太长了,有些是无关紧要的key-value, 
        final URL registeredProviderUrl = getRegisteredProviderUrl(providerUrl, registryUrl);

        // 将当前服务提供者Invoker,以及该服务对应的注册中心地址,以及简化后的服务url存入ProviderConsumerRegTable
        ProviderInvokerWrapper<T> providerInvokerWrapper = ProviderConsumerRegTable.registerProvider(originInvoker,
                registryUrl, registeredProviderUrl);


        //to judge if we need to delay publish
        //是否需要注册到注册中心, 没有配置,默认就是true
        boolean register = providerUrl.getParameter(REGISTER_KEY, true);
        if (register) {
    
    
            // 注册服务,把简化后的服务提供者url注册到registryUrl中去
            register(registryUrl, registeredProviderUrl);
            providerInvokerWrapper.setReg(true);
        }

      	//以下是监听的内容;
        registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);


        exporter.setRegisterUrl(registeredProviderUrl);
        exporter.setSubscribeUrl(overrideSubscribeUrl);
        return new DestroyableExporter<>(exporter);
    }

doLocalExport(originInvoker, providerUrl)

    private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker, URL providerUrl) {
    
    
        String key = getCacheKey(originInvoker);
        return (ExporterChangeableWrapper<T>) bounds.computeIfAbsent(key, s -> {
    
    
            Invoker<?> invokerDelegate = new InvokerDelegate<>(originInvoker, providerUrl);
            // protocol属性的值是哪来的,是在SPI中注入进来的,是一个代理类
            // 这里实际利用的就是DubboProtocol或HttpProtocol去export  NettyServer
            return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
        });
    }

DubboProtocol#export

Purpose: start tomcat, start related services;
work:

  1. Get the service URL;
  2. Get the ServiceKey of the service,
  3. Construct a DubboExporter, pass in and execute invoker, service serviceKey, exporterMap;
  4. Put into the exporter's map;
  5. start the running container;
    @Override
    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    
    
        URL url = invoker.getUrl();

        // export service.
        String key = serviceKey(url);
        // 构造一个Exporter
        DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
        // exporter的Map,  以服务serviceKey为键, exporter为值的map; 查找服务比较方便;
        exporterMap.put(key, exporter);
		//...省略部分代码;

        // 开启NettyServer
        openServer(url);

        optimizeSerialization(url);

        return exporter;
    }

DubboProtocol#openServer(URL url)

Purpose: start the running container, start the local service;
work:

  1. Get the ip:port value of the service URL'
  2. Get the isserver value of the service URL, the default is true;
  3. Get the container through servermap
  4. The container is empty, the service is created and set;
  5. The container is not empty, set the service;
    private void openServer(URL url) {
    
    
        // find server.
         // 获得ip地址和port, 192.168.40.17:20880
        String key = url.getAddress();
        // NettyClient, NettyServer
        //client can export a service which's only for server to invoke
        boolean isServer = url.getParameter(IS_SERVER_KEY, true);
        if (isServer) {
    
    
            // 缓存Server对象, 运行容器对象, key为ip:port;
            ExchangeServer server = serverMap.get(key);

            // DCL,Double Check LocK
            //容器对象为空,那就创建容器, DCL保证线程安全,避免重复创建容器;
            //容器创建完后,放入serverMap, 下一个服务导出就使用reset方法;
            if (server == null) {
    
    
                synchronized (this) {
    
    
                    server = serverMap.get(key);
                    if (server == null) {
    
    
                        // 创建Server,并进行缓存
                        serverMap.put(key, createServer(url));
                    }
                }
            } else {
    
    
                // server supports reset, use together with override
                // 服务重新导出时,就会走这里
                server.reset(url);
            }
        }
    }

DubboProtocol# createServer(URL url)

Purpose: Create container
Job:

  1. Generate service URL, add channel.readonly.sent=TRUE, heartbeat=60000, codec=dubbo parameters;
    ===> heartbeat is 1 minute;
  2. Obtain the server-side implementation type of the protocol;
private ExchangeServer createServer(URL url) {
    
    
        url = URLBuilder.from(url)
                // send readonly event when server closes, it's enabled by default
                .addParameterIfAbsent(CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString())
                // enable heartbeat by default
                .addParameterIfAbsent(HEARTBEAT_KEY, String.valueOf(DEFAULT_HEARTBEAT))
                .addParameter(CODEC_KEY, DubboCodec.NAME)
                .build();

        // 协议的服务器端实现类型,比如:dubbo协议的mina,netty等,http协议的jetty,servlet等,默认为netty
        String str = url.getParameter(SERVER_KEY, 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绑定端口,和对应的请求处理器
        ExchangeServer server;
        try {
    
    
            // requestHandler是请求处理器,类型为ExchangeHandler
            // 表示从url的端口接收到请求后,requestHandler来进行处理
            server = Exchangers.bind(url, requestHandler);
        } catch (RemotingException e) {
    
    
            throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
        }

        // 协议的客户端实现类型,比如:dubbo协议的mina,netty等
        str = url.getParameter(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)

·Work:

  1. Scan the Exchanger file through the SPI mechanism, the type is header, and the final scanned file is HeaderExchanger;
  2. Call the HeaderExchanger#bind method;
  3. HeaderExchanger#bind calls the Transporter#bind method again;
    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");
        }
        // codec表示协议编码方式
        url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
        // 通过url得到HeaderExchanger, 利用HeaderExchanger进行bind,将得到一个HeaderExchangeServer
        return getExchanger(url).bind(url, handler);
    }
    //getExchanger(url):是通过SPI去获取一个Exchanger的, 这里的实现类是HeaderExchanger;
    public static Exchanger getExchanger(URL url) {
    
    
        String type = url.getParameter(Constants.EXCHANGER_KEY, Constants.DEFAULT_EXCHANGER);
        return getExchanger(type);
    }
    //通过SPI获取Exchanger文件, 类型为header; 返回一个HeaderExchanger
    public static Exchanger getExchanger(String type) {
    
    
        return ExtensionLoader.getExtensionLoader(Exchanger.class).getExtension(type);
    }
	public class HeaderExchanger implements Exchanger {
    
    
	
	    public static final String NAME = "header";

	    @Override
	    public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
    
    
	
	        // 下面会去启动Netty
	        // 对handler包装了两层,表示当处理一个请求时,每层Handler负责不同的处理逻辑
	        // 为什么在connect和bind时都是DecodeHandler,解码,解的是把InputStream解析成RpcInvocation对象
	        // 客户端调用服务时,会将服务的信息例如:接口路径名,参数, 版本号, ip端口等与服务香瓜你的信息;然后服务端在更根据这些信息,找到服务实现类,进而执行;
	        return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
	    }
	
	}

Transporters# bind(URL url, ChannelHandler… handlers)

Purpose: start the container, add ChannelHander processing class;
work:

  1. The processing of handlers, if there are more than one, create a ChannelHandlerDispatcher instance;
  2. Call getTransporter() to get the Transporter instance, and find the Transporter configuration file through the SPI mechanism. Since the Transporter interface uses the annotation @SPI("netty"), the NettryTransporter implementation class is used by default;
  3. Call the NettyTransporter#bind method to create a NettryServer instance;
    public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {
    
    
		//....省略无关代码

        // 如果bind了多个handler,那么当有一个连接过来时,会循环每个handler去处理连接
        ChannelHandler handler;
        if (handlers.length == 1) {
    
    
            handler = handlers[0];
        } else {
    
    
            handler = new ChannelHandlerDispatcher(handlers);
        }

        // 调用NettyTransporter去绑定,Transporter表示网络传输层
        return getTransporter().bind(url, handler);
    }
	//通过SPI机制, 查找Transporter配置文件, 由于Transporter接口使用了注解@SPI("netty"), 默认使用的时NettryTransporter实现类;
    public static Transporter getTransporter() {
    
    
        return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
    }
    @SPI("netty")
	public interface Transporter {
    
    ....}
    @Override
    public Server bind(URL url, ChannelHandler listener) throws RemotingException {
    
    
        return new NettyServer(url, listener);
    }

NettyServer

    public NettyServer(URL url, ChannelHandler handler) throws RemotingException {
    
    
        // 设置线程名,wrap方法会返回一个MultiMessageHandler,这个Handler会被设置到AbstractPeer的handler属性上
        // 而当netty接收到数据时,会调用AbstractPeer的handler属性的received方法
        // 所以MultiMessageHandler就是负责处理请求
        // 而MultiMessageHandler
        super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
    }
    public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
    
    
        super(url, handler);
        localAddress = getUrl().toInetSocketAddress();

        String bindIp = getUrl().getParameter(Constants.BIND_IP_KEY, getUrl().getHost());
        int bindPort = getUrl().getParameter(Constants.BIND_PORT_KEY, getUrl().getPort());
        if (url.getParameter(ANYHOST_KEY, false) || NetUtils.isInvalidLocalHost(bindIp)) {
    
    
            bindIp = ANYHOST_VALUE;
        }
        bindAddress = new InetSocketAddress(bindIp, bindPort);
        this.accepts = url.getParameter(ACCEPTS_KEY, DEFAULT_ACCEPTS);
        this.idleTimeout = url.getParameter(IDLE_TIMEOUT_KEY, DEFAULT_IDLE_TIMEOUT);
        try {
    
    
            doOpen();

        } catch (Throwable 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()));
    }

NettryServer# doOpen()

Purpose: create container;
job:

  1. create netty server
  2. Create a service receiving thread group;
  3. Create a service execution thread group;
  4. Create a NettyServerHandler to receive connections, heartbeat requests, etc.;
  5. The server sets some parameters, such as NettyServerHandler, thread group, etc.
  6. Server binding address;

    @Override
    protected void doOpen() throws Throwable {
    
    
    	//创建服务器
        bootstrap = new ServerBootstrap();
		//创建一个服务接收线程组;
        bossGroup = new NioEventLoopGroup(1, new DefaultThreadFactory("NettyServerBoss", true));
        //创建一个服务执行线程组;
        workerGroup = new NioEventLoopGroup(getUrl().getPositiveParameter(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 {
    
    
                        // FIXME: should we use getTimeout()?
                        int idleTimeout = UrlUtils.getIdleTimeout(getUrl());
                        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("server-idle-handler", new IdleStateHandler(0, 0, idleTimeout, MILLISECONDS))
                                .addLast("handler", nettyServerHandler);
                    }
                });
        // bind
        ChannelFuture channelFuture = bootstrap.bind(getBindAddress());
        channelFuture.syncUninterruptibly();
        channel = channelFuture.channel();

    }

run container

  • The protocol is specified in the service URL, such as the Http protocol and the Dubbo protocol. Start the corresponding Server according to different protocols.
    For example, the Http protocol starts Tomcat and Jetty.
    For example, the Dubbo protocol starts Netty.

  • You can't just start the Server, but also need to bind a RequestHandler to process the request.
    For example, the Http protocol corresponds to InternalHandler. The Dubbo protocol corresponds to ExchangeHandler.

  1. Call the openServer(URL url) method of DubboProtocol to start the server
  2. Call the createServer(url) method of DubboProtocol, and call Exchangers.bind(url, requestHandler) in the createServer() method to get an ExchangeServer; where requestHandler represents the request handler, which is used to process the request
  3. In Exchangers.bind(url, requestHandler), first an Exchanger will be obtained according to the URL, and the default is HeaderExchanger;
  4. HeaderExchanger includes HeaderExchangeClient and HeaderExchangeServer. HeaderExchangeClient is responsible for sending heartbeats, and HeaderExchangeServer is responsible for receiving heartbeats. If it times out, it will close the channel
  5. Before constructing HeaderExchangeServer, it will call Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))) method to a Server
  6. By default, getTransporter will be used to bind (URL url, ChannelHandler listener) to obtain a Servlet. The listener at this time is the DecodeHandler passed in from the outside
  7. In the bind method of NettyTransporter, new NettyServer(url, listener) will be added, so the Server returned above is NettyServer by default.
  8. When constructing NettyServer, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)) is called to construct a ChannelHandler.
  9. The handler in the wrap is the above listener; in the wrap method, new MultiMessageHandler(new HeartbeatHandler(ExtensionLoader.getExtensionLoader(Dispatcher.class).getAdaptiveExtension().dispatch(handler, url))); is called to construct a ChannelHandler.
  10. After the ChannelHandler is constructed, it is time to actually open the Server, and the doOpen method of the AbstractServer abstract class will be called.
  11. In NettyServer, the doOpen method will be implemented, new NettyServerHandler(getUrl(), this) will be called to construct a NettyServerHandler, and the bind address
  12. At this point, the server startup process of the DubboProtocol protocol is over

Guess you like

Origin blog.csdn.net/yaoyaochengxian/article/details/123595195