Dubbo Learning Record (11) - Démarrer et exécuter le conteneur pour l'exportation de services

L'exportation de service démarre l'exécution de l'enregistrement du conteneur et du service

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

Appelez couche par couche, et enfin appelez la méthode doExportUrlsFor1Protocol

doExportUrlsFor1Protocol

Objectif : fusionner l'URL du service, démarrer le conteneur, s'enregistrer dans le registre ;
travail :

  1. Obtenir le nom du protocole ;
  2. Créer une carte de paramètres pour l'URL du service
  3. Obtenez le rôle du service (SIDE_KEY) et mettez-le dans la carte
  4. Obtenez les informations de version dubbo et mettez-les dans la carte ;
  5. Obtenir les paramètres configurés par le centre de surveillance et les mettre dans la carte
  6. Obtenir les paramètres de la configuration de l'application et les mettre dans la carte ;
  7. Obtenir les paramètres de la configuration du module et les mettre dans la carte ;
  8. Obtenez les paramètres de la configuration du fournisseur et mettez-les dans la carte ;
  9. Obtenir les paramètres de la configuration du protocole et les mettre dans la carte ;
  10. Obtenez les paramètres du service lui-même et mettez-les dans la carte ;
  11. Obtenez les paramètres de la configuration de la méthode et mettez-les dans la carte ;
  12. Via le Wrapper correspondant à l'interface, récupérez tous les noms de méthodes dans l'interface et mettez-les dans la carte ( je ne sais pas si je ne l'ai pas utilisé en profondeur )
  13. Obtenez la configuration du jeton de @Service et placez-la dans la carte ;
  14. Obtenez le numéro d'hôte hôte ;
  15. Obtenir le numéro de port port
  16. Créez une instance d'URL du service, les paramètres sont le nom du protocole (1), le numéro d'hôte (14), le numéro de port (15), le nom de l'application (configurable) + / + le nom de l'interface est le nom du service, et la carte des paramètres ;
  17. Obtenez la valeur de SCOPE_KEY ;
  18. Si la valeur SCOPE_KEY est locale, effectuez un enregistrement local ;
  19. Si la valeur SCOPE_key est distante, traversez le centre d'enregistrement et enregistrez-vous à distance auprès de chaque centre d'enregistrement ;
  20. Enregistrez les métadonnées du service auprès du centre de métadonnées ;
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);
    }

Parcourir la collection d'URL du registre

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);
    }
  • L'objet Invoker du service est généré via le proxy dynamique et la méthode du service est exécutée en appelant la méthode Invoker#invoke ;
  • L'exportation sera utilisée comme clé, et l'instance d'URL de service sera utilisée comme valeur, qui sera mise dans le paramètre URL du centre d'enregistrement
  • Transmettez la classe d'implémentation de service, l'interface de service et l'URL du centre d'enregistrement, de sorte que cet invocateur ne concerne que le service actuel ;
  • Ensuite, encapsulez l'instance de l'objet proxy Invoker, transmettez this à l'instance actuelle et générez un objet wrapper DelegateProviderMetaDataInvoker ;
  • Appelez la méthode d'exportation dont le protocole est RegistryProtocol, enregistrez l'objet d'emballage et obtenez un exportateur une fois l'exportation réussie
  • Lors du processus d'appel de la méthode Registryprotocol#export, DubboProtocol/HttpProtocol sera appelé pour démarrer le conteneur en cours d'exécution Netty/Tomcat. Une fois le démarrage terminé, l'invocateur de service sera enregistré dans le registre ;
                        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

Objectif : démarrer tomcat, démarrer les services associés ;
travail :

  1. Obtenez l'URL du service ;
  2. Obtenir la ServiceKey du service,
  3. Construisez un DubboExporter, transmettez et exécutez l'invocateur, le service serviceKey, exporterMap ;
  4. Mettre dans la carte de l'exportateur ;
  5. démarrer le conteneur en cours d'exécution ;
    @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)

Objectif : démarrer le conteneur en cours d'exécution, démarrer le service local ;
travail :

  1. Obtenir la valeur ip:port de l'URL du service'
  2. Obtenez la valeur isserver de l'URL du service, la valeur par défaut est true ;
  3. Obtenir le conteneur via servermap
  4. Le conteneur est vide, le service est créé et paramétré ;
  5. Le conteneur n'est pas vide, définissez le 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)

Objectif : Créer un
conteneur

  1. Générez l'URL du service, ajoutez les paramètres channel.readonly.sent=TRUE, heartbeat=60000, codec=dubbo ;
    ===> heartbeat est de 1 minute ;
  2. Obtenez le type d'implémentation côté serveur du protocole ;
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)

·Travail:

  1. Scannez le fichier Exchanger via le mécanisme SPI, le type est header et le fichier scanné final est HeaderExchanger ;
  2. Appelez la méthode HeaderExchanger#bind ;
  3. HeaderExchanger#bind appelle à nouveau la méthode Transporter#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");
        }
        // 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))));
	    }
	
	}

Transporteurs # bind (url URL, ChannelHandler… gestionnaires)

Objectif : démarrer le conteneur, ajouter la classe de traitement ChannelHander ;
travail :

  1. Le traitement des gestionnaires, s'il y en a plusieurs, crée une instance ChannelHandlerDispatcher ;
  2. Appelez getTransporter() pour obtenir l'instance de Transporter et recherchez le fichier de configuration de Transporter via le mécanisme SPI. Étant donné que l'interface de Transporter utilise l'annotation @SPI("netty"), la classe d'implémentation NettryTransporter est utilisée par défaut ;
  3. Appelez la méthode NettyTransporter#bind pour créer une instance NettryServer ;
    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()

Objectif : créer un conteneur ;
travail :

  1. créer un serveur netty
  2. Créez un groupe de threads de réception de service ;
  3. Créez un groupe de threads d'exécution de service ;
  4. Créez un NettyServerHandler pour recevoir les connexions, les demandes de pulsation, etc. ;
  5. Le serveur définit certains paramètres, tels que NettyServerHandler, le groupe de threads, etc.
  6. Adresse de liaison du serveur ;

    @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();

    }

exécuter le conteneur

  • Le protocole est spécifié dans l'URL du service, tel que le protocole Http et le protocole Dubbo. Démarrez le serveur correspondant selon différents protocoles.
    Par exemple, le protocole Http démarre Tomcat et Jetty.
    Par exemple, le protocole Dubbo démarre Netty.

  • Vous ne pouvez pas simplement démarrer le serveur, mais vous devez également lier un RequestHandler pour traiter la demande.
    Par exemple, le protocole Http correspond à InternalHandler. Le protocole Dubbo correspond à ExchangeHandler.

  1. Appelez la méthode openServer(URL url) de DubboProtocol pour démarrer le serveur
  2. Appelez la méthode createServer(url) de DubboProtocol et appelez Exchangers.bind(url, requestHandler) dans la méthode createServer() pour obtenir un ExchangeServer ; où requestHandler représente le gestionnaire de requêtes, qui est utilisé pour traiter la requête
  3. Dans Exchangers.bind(url, requestHandler), un échangeur sera d'abord obtenu en fonction de l'URL, et la valeur par défaut est HeaderExchanger ;
  4. HeaderExchanger inclut HeaderExchangeClient et HeaderExchangeServer. HeaderExchangeClient est responsable de l'envoi des pulsations, et HeaderExchangeServer est responsable de la réception des pulsations. S'il expire, il fermera le canal
  5. Avant de construire HeaderExchangeServer, il appellera la méthode Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))) à un serveur
  6. Par défaut, getTransporter sera utilisé pour lier (url URL, écouteur ChannelHandler) pour obtenir un servlet. L'écouteur à ce moment est le DecodeHandler passé de l'extérieur
  7. Dans la méthode bind de NettyTransporter, new NettyServer(url, listener) sera ajouté, donc le serveur renvoyé ci-dessus est NettyServer par défaut.
  8. Lors de la construction de NettyServer, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)) est appelé pour construire un ChannelHandler.
  9. Le gestionnaire dans le wrap est l'écouteur ci-dessus ; dans la méthode wrap, new MultiMessageHandler(new HeartbeatHandler(ExtensionLoader.getExtensionLoader(Dispatcher.class).getAdaptiveExtension().dispatch(handler, url))); est appelé pour construire un ChannelHandler.
  10. Une fois le ChannelHandler construit, il est temps d'ouvrir réellement le serveur et la méthode doOpen de la classe abstraite AbstractServer sera appelée.
  11. Dans NettyServer, la méthode doOpen sera implémentée, new NettyServerHandler(getUrl(), this) sera appelé pour construire un NettyServerHandler, et l'adresse de liaison
  12. À ce stade, le processus de démarrage du serveur du protocole DubboProtocol est terminé

Je suppose que tu aimes

Origine blog.csdn.net/yaoyaochengxian/article/details/123595195
conseillé
Classement