dubbo-服务注册

Spring自定义标签过程

https://blog.csdn.net/zzg1229059735/article/details/82669955

https://juejin.cn/post/6968333542644252703

spring 自定义标签是一个强大的 Hook,核心组件只有 spring-corespring-beans

自定义标签的制作过程

  • xxx.xsd 文件制定了 xml 文件的规范,该文件中定义标签属性及格式:
    例如定义一个简单的分布式id生成器标签:
<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns="http://www.hexup.com/schema/distributed-id"
            xmlns:xsd="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.hexup.com/schema/distributed-id">


    <xsd:element name="distributed-id">
        <xsd:complexType>
            <xsd:attribute name="id" type="xsd:string"></xsd:attribute>
            <xsd:attribute name="bizCode" type="xsd:string"></xsd:attribute>
            <xsd:attribute name="length" type="xsd:int"></xsd:attribute>
        </xsd:complexType>
    </xsd:element>
            
</xsd:schema>
  • spring.schemes 中说明 xxx.xsd 文件的查找路径,在 spring.handlers 中定义该标签的解析函数入口,该函数需要继承 NamespaceHandlerSupportBeanDefinitionParser 接口。
public class DistributedIdNamespaceHandler extends NamespaceHandlerSupport {

    @Override
    public void init() {
        // 注册标签解析器
        registerBeanDefinitionParser("distributed-id", new DistributedIdParser());
    }
}

自定义标签解析器:

public class DistributedIdParser implements BeanDefinitionParser {
    @Override
    public BeanDefinition parse(Element element, ParserContext parserContext) {
        // 解析xml内的标签
        String bizCode = element.getAttribute("bizCode");
        int length = Integer.valueOf(element.getAttribute("length"));
        String id = element.getAttribute("id");
        
        // 创建DistributedIdFactoryBean bean
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
        builder.getRawBeanDefinition().setBeanClass(DistributedIdFactoryBean.class);
        builder.setScope(BeanDefinition.SCOPE_SINGLETON);

        builder.addPropertyValue("bizCode", bizCode);
        builder.addPropertyValue("length", length);

        BeanDefinition beanDefinition = builder.getBeanDefinition();

        parserContext.getRegistry().registerBeanDefinition(id, beanDefinition);

        return beanDefinition;
    }
}

分布式ID生产工厂:

public class DistributedIdFactoryBean implements InitializingBean, FactoryBean<DistributedIdComponent> {

    private String bizCode;
    private int length;

    private DistributedIdComponent distributedIdComponent;

    @Override
    public DistributedIdComponent getObject() throws Exception {
        return distributedIdComponent;
    }

    @Override
    public Class<?> getObjectType() {
        return DistributedIdComponent.class;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        distributedIdComponent = new DistributedIdComponent(bizCode, length);
    }

    public void setBizCode(String bizCode) {
        this.bizCode = bizCode;
    }

    public void setLength(int length) {
        this.length = length;
    }
}
public class DistributedIdComponent {
    private String bizCode;
    private int length;

    public DistributedIdComponent() {

    }

    public DistributedIdComponent(String bizCode, int length) {
        this.bizCode = bizCode;
        this.length = length;
    }

    public String generateId() {
        System.out.println("mock generate id");
        return String.valueOf(System.currentTimeMillis()).substring(0, length);
    }

    public String getBizCode() {
        return bizCode;
    }

    public void setBizCode(String bizCode) {
        this.bizCode = bizCode;
    }

    public int getLength() {
        return length;
    }

    public void setLength(int length) {
        this.length = length;
    }
}

Dubbo 服务解析———Service标签解析

总的来说实现 Spring 自定义标签过程:

  • 编写 xsd 文件,定义自定义标签配置规则;
  • 编写 spring.schemas 文件,定义自定义标签的导入形式;
  • 编写 NamespaceHandlerSupport 用于自定义解析逻辑;
  • 编写 spring.handlers 文件指定自定义标签的实现类。

Dubbo 自定义标签过程:

  • 编写 dubbo.xsd 文件,在文件中声明标签可以配置哪些属性:
    <!-- service 标签定义 -->
    <xsd:element name="service" type="serviceType">
        <xsd:annotation>
            <xsd:documentation><![CDATA[ Export service config ]]></xsd:documentation>
            <xsd:appinfo>
                <tool:annotation>
                    <tool:exports type="org.apache.dubbo.config.ServiceConfig"/>
                </tool:annotation>
            </xsd:appinfo>
        </xsd:annotation>
    </xsd:element>

    <!-- ServiceType 类型详细定义 -->
    <xsd:complexType name="serviceType">
        <xsd:complexContent>
            <xsd:extension base="abstractServiceType">
                <xsd:choice minOccurs="0" maxOccurs="unbounded">
                    <xsd:element ref="method" minOccurs="0" maxOccurs="unbounded"/>
                    <xsd:element ref="parameter" minOccurs="0" maxOccurs="unbounded"/>
                    <xsd:element ref="beans:property" minOccurs="0" maxOccurs="unbounded"/>
                </xsd:choice>
                <xsd:attribute name="interface" type="xsd:token" use="required">
                    <xsd:annotation>
                        <xsd:documentation>
                            <![CDATA[ Defines the interface to advertise for this service in the service registry. ]]></xsd:documentation>
                        <xsd:appinfo>
                            <tool:annotation>
                                <tool:expected-type type="java.lang.Class"/>
                            </tool:annotation>
                        </xsd:appinfo>
                    </xsd:annotation>
                </xsd:attribute>
                <xsd:attribute name="ref" type="xsd:string">
                    <xsd:annotation>
                        <xsd:documentation>
                            <![CDATA[ The service implementation instance bean id. ]]></xsd:documentation>
                    </xsd:annotation>
                </xsd:attribute>
                <xsd:attribute name="class" type="xsd:string">
                    <xsd:annotation>
                        <xsd:documentation><![CDATA[ The service implementation class name. ]]></xsd:documentation>
                    </xsd:annotation>
                </xsd:attribute>
                <xsd:attribute name="path" type="xsd:string">
                    <xsd:annotation>
                        <xsd:documentation><![CDATA[ The service path. ]]></xsd:documentation>
                    </xsd:annotation>
                </xsd:attribute>
                <xsd:attribute name="provider" type="xsd:string">
                    <xsd:annotation>
                        <xsd:documentation><![CDATA[ Deprecated. Replace to protocol. ]]></xsd:documentation>
                    </xsd:annotation>
                </xsd:attribute>
                <xsd:attribute name="generic" type="xsd:string">
                    <xsd:annotation>
                        <xsd:documentation><![CDATA[ Generic service. ]]></xsd:documentation>
                    </xsd:annotation>
                </xsd:attribute>
                <xsd:anyAttribute namespace="##other" processContents="lax"/>
            </xsd:extension>
        </xsd:complexContent>
    </xsd:complexType>
  • 编写 spring.schemasspring.handlers 文件;
  • 自定义 DubboNamespaceHandler,继承自 NamespaceHandlerSupport,将 <dubbo:service> 解析为 ServiceBean 对象:
public class DubboNamespaceHandler extends NamespaceHandlerSupport {
    @Override
    public void init() {
        // 注册 Service 标签对应的 Bean 对象
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
    }
}

// 解析标签属性,生成 BeanDefinition 对象并开始实例化 ServiceBean 对象
private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
    RootBeanDefinition beanDefinition = new RootBeanDefinition();
    beanDefinition.setBeanClass(beanClass);
    beanDefinition.setLazyInit(false);
    String id = element.getAttribute("id");
    //...省略代码
    // 解析ServiceBean
    if (ServiceBean.class.equals(beanClass)) {
        String className = element.getAttribute("class");
        if (StringUtils.isNotEmpty(className)) {
            RootBeanDefinition classDefinition = new RootBeanDefinition();
            classDefinition.setBeanClass(ReflectUtils.forName(className));
            classDefinition.setLazyInit(false);
            parseProperties(element.getChildNodes(), classDefinition);
            beanDefinition.getPropertyValues().addPropertyValue("ref", new BeanDefinitionHolder(classDefinition, id + "Impl"));
        }
    }
    //...省略代码
    return beanDefinition;
}

Dubbo 服务解析———服务启动

服务注册是指将启动后的服务信息写到注册中心并暴露,这样消费方就可以订阅注册中心的服务信息。

需要在 ServiceBean 对象完成加载时,开始注册和启动服务,因此 ServiceBean 实现了 ApplicationListener 接口,具体的 OnApplicationEvent() 方法定义了服务注册和启动的详细过程。

// ServiceBean.java
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean,
        ApplicationContextAware, ApplicationListener<ContextRefreshedEvent>, BeanNameAware,
        ApplicationEventPublisherAware {

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

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

    public synchronized void export() {
        // 检查配置
        checkAndUpdateSubConfigs();

        if (!shouldExport()) {
            return;
        }

        // 延迟注册
        if (shouldDelay()) {
            DELAY_EXPORT_EXECUTOR.schedule(this::doExport, getDelay(), TimeUnit.MILLISECONDS);
        } else {
            doExport();
        }
    }

    // 服务注册核心逻辑
    private void doExportUrls() {
        // 加载所有的注册中心信息
        List<URL> registryURLs = loadRegistries(true);
        for (ProtocolConfig protocolConfig : protocols) {
            // 解析服务路径,比如 com.yuqiao.deeplearningdubbo.analysis.base.DemoService
            String pathKey = URL.buildKey(getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), group, version);
            ProviderModel providerModel = new ProviderModel(pathKey, ref, interfaceClass);
            ApplicationModel.initProviderModel(pathKey, providerModel);
            // 执行服务的启动和暴露
            doExportUrlsFor1Protocol(protocolConfig, registryURLs);
        }
    }
}

doExportUrlsFor1Protocol() 方法根据 scope 配置决定如何暴露服务(本地/远程)?

//ServiceConfig.doExportUrlsFor1Protocol()
private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
    //协议名,默认为dubbo
    String name = protocolConfig.getName();
    if (StringUtils.isEmpty(name)) {
        name = DUBBO;
    }

    //下面一大段代码,都是初始化服务信息
    //比如dubbo application名、接口名、方法名、端、等等。
    //...省略代码
    

    
    //服务机器IP
    String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
    //服务端口
    Integer port = this.findConfigedPorts(protocolConfig, name, map);
    //将服务信息,转化为URL对象
    //将来暴露服务,都是从URL中解析的。
    //url数据示例:dubbo://172.16.184.39:20880/com.yuqiao.deeplearningdubbo.analysis.base.DemoService?anyhost=true&application=provider-app&bean.name=com.yuqiao.deeplearningdubbo.analysis.base.DemoService&bind.ip=172.16.184.39&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=com.yuqiao.deeplearningdubbo.analysis.base.DemoService&methods=sayHello,sayHello2&pid=78430&release=2.7.4.1&side=provider&timestamp=1644652477685
    URL url = new URL(name, host, port, getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), map);

    //...省略代码
    
    //scope默认为null,当scope配置为none时,才不暴露
    String scope = url.getParameter(SCOPE_KEY);
    if (!SCOPE_NONE.equalsIgnoreCase(scope)) {

        // 如果没有明确配置为remote,则向本地暴露服务
        if (!SCOPE_REMOTE.equalsIgnoreCase(scope)) {
            exportLocal(url);
        }
        // 如果配置不是local,则向远程暴露服务
        if (!SCOPE_LOCAL.equalsIgnoreCase(scope)) {
            if (CollectionUtils.isNotEmpty(registryURLs)) {
                for (URL registryURL : registryURLs) {

                    // 生成服务调用 invoker 对象  
                    Invoker<?> invoker = PROXY_FACTORY.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(EXPORT_KEY, url.toFullString()));
                    // 包装 invoker
                    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

                    //注册服务
                    //注意,这里的protocol是自适应类(Protocol$Adaptive),url里面的协议参数是registry
                    //所以,实际上是调用了RegistryProtocol.export
                    //最终,1、在RegistryProtocol.export里调用了DubboProtocol.export启动服务
                    //     2、在RegistryProtocol.export里调用了Registry.register暴露服务
                    Exporter<?> exporter = protocol.export(wrapperInvoker);
                    exporters.add(exporter);
                }
            } else {
                //...省略代码
            }
            //...省略代码
        }
    }
    this.urls.add(url);
}

上述 protocol 是通过 Dubbo SPI 动态加载获取的:

  • private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();

protocol.export(xxx) 方法根据参数类型识别,实际调用的是 RegistryProtocol.export(xxx) 方法,该方法内部通过 doLocalExport() 完成服务启动、register(xxx, xxx) 完成服务注册。

//RegistryProtocol.export()
@Override
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
    //注册中心URL,
    //当前URL中的protocol=registry
    //将protocol替换成注册中心的协议,如zookeeper
    URL registryUrl = getRegistryUrl(originInvoker);
    // 服务提供者URL
    // 与registryUrl对比,这里的protocol变成了dubbo
    // 数据示例:dubbo://172.16.184.39:20880/com.yuqiao.deeplearningdubbo.analysis.base.DemoService?anyhost=true&application=provider-app&bean.name=com.yuqiao.deeplearningdubbo.analysis.base.DemoService&bind.ip=172.16.184.39&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=com.yuqiao.deeplearningdubbo.analysis.base.DemoService&methods=sayHello,sayHello2&pid=92866&release=2.7.4.1&side=provider&timestamp=1644830907146
    URL providerUrl = getProviderUrl(originInvoker);

    // ...省略代码
    
    //导出invoker,
    //这里面再次通过Protocol$Adaptive调用Protocol的扩展点
    //因为providerUrl里的protocol已经变为了dubbo,所以这里面实际上调用了DubboProtocol.export。
    //在DubboProtocol.export()里,实现了服务的启动
    final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);

    // ...省略代码
    
    //注册服务,核心方法在register()里
    boolean register = providerUrl.getParameter(REGISTER_KEY, true);
    if (register) {
        register(registryUrl, registeredProviderUrl);
        providerInvokerWrapper.setReg(true);
    }

    //...省略代码
}
// RegistryProtocol.doLocalExport()
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 是 RegistryProtocol 实例化时通过 ExtensionLoader 依赖注入的,最终调用 DubboProtocol.export(xxx) 
        return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
    });
}
// RegistryProtocol.export()
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
    URL registryUrl = getRegistryUrl(originInvoker);
    // url to export locally
    URL providerUrl = getProviderUrl(originInvoker);

    // 这里逻辑没看懂????
    // 好像是在根据配置重新构造 url

    // 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.
    final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
    final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
    overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);

    providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
    //export invoker
    final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);

    // url to registry
    final Registry registry = getRegistry(originInvoker);
    final URL registeredProviderUrl = getRegisteredProviderUrl(providerUrl, registryUrl);
    ProviderInvokerWrapper<T> providerInvokerWrapper = ProviderConsumerRegTable.registerProvider(originInvoker,
            registryUrl, registeredProviderUrl);
    //to judge if we need to delay publish
    boolean register = providerUrl.getParameter(REGISTER_KEY, true);
    if (register) {
        register(registryUrl, registeredProviderUrl);
        providerInvokerWrapper.setReg(true);
    }

    // Deprecated! Subscribe to override rules in 2.6.x or before.
    registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);

    exporter.setRegisterUrl(registeredProviderUrl);
    exporter.setSubscribeUrl(overrideSubscribeUrl);
    //Ensure that a new exporter instance is returned every time export
    return new DestroyableExporter<>(exporter);
}

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);
        // 真正的服务启动
        return new ExporterChangeableWrapper<>((Exporter<T>) protocol.export(invokerDelegate), originInvoker);
    });
}
// DubboProtocol.export(xxx)
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
    URL url = invoker.getUrl();

    // export service.
    String key = serviceKey(url);
    DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
    exporterMap.put(key, exporter);

    //export an stub service for dispatching event
    Boolean isStubSupportEvent = url.getParameter(STUB_EVENT_KEY, DEFAULT_STUB_EVENT);
    Boolean isCallbackservice = url.getParameter(IS_CALLBACK_SERVICE, false);
    if (isStubSupportEvent && !isCallbackservice) {
        String stubServiceMethods = url.getParameter(STUB_EVENT_METHODS_KEY);
        if (stubServiceMethods == null || stubServiceMethods.length() == 0) {
            if (logger.isWarnEnabled()) {
                logger.warn(new IllegalStateException("consumer [" + url.getParameter(INTERFACE_KEY) +
                        "], has set stubproxy support event ,but no stub methods founded."));
            }

        } else {
            stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
        }
    }

    // ※:启动对应 URL 的服务
    openServer(url);
    optimizeSerialization(url);

    return exporter;
}

// 启动指定 URL 的服务
private void openServer(URL url) {
    // find server.
    String key = url.getAddress();
    //client can export a service which's only for server to invoke
    boolean isServer = url.getParameter(IS_SERVER_KEY, true);

    // 单例模式启动服务,先从 serverMap 服务列表里获取服务信息,不存在就创建服务
    if (isServer) {
        ExchangeServer server = serverMap.get(key);
        if (server == null) {
            synchronized (this) {
                server = serverMap.get(key);
                if (server == null) {
                    serverMap.put(key, createServer(url));
                }
            }
        } else {
            // server supports reset, use together with override
            server.reset(url);
        }
    }
}
// DubboProtocol.createServer(xxx)
private ExchangeServer createServer(URL 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();
    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);
    }

    ExchangeServer server;
    try {
        // 服务启动核心逻辑
        server = Exchangers.bind(url, requestHandler);
    } catch (RemotingException e) {
        throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);
    }

    str = url.getParameter(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(xxx)
public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
    if (url == null) {
        throw new IllegalArgumentException("url == null");
    }
    if (handler == null) {
        throw new IllegalArgumentException("handler == null");
    }
    url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
    return getExchanger(url).bind(url, handler);
}

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

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

// 这里通过 SPI 获取自适应扩展点对象
public static Transporter getTransporter() {
    return ExtensionLoader.getExtensionLoader(Transporter.class).getAdaptiveExtension();
}

Transporter 是一个接口,通过 Dubbo SPI 定义自适应扩展点类型:

@SPI("netty")
public interface Transporter {
    /**
     * Bind a server.
     *
     * @param url     server url
     * @param handler
     * @return server
     * @throws RemotingException
     * @see org.apache.dubbo.remoting.Transporters#bind(URL, ChannelHandler...)
     */
    @Adaptive({Constants.SERVER_KEY, Constants.TRANSPORTER_KEY})
    Server bind(URL url, ChannelHandler handler) throws RemotingException;

    /**
     * Connect to a server.
     *
     * @param url     server url
     * @param handler
     * @return client
     * @throws RemotingException
     * @see org.apache.dubbo.remoting.Transporters#connect(URL, ChannelHandler...)
     */
    @Adaptive({Constants.CLIENT_KEY, Constants.TRANSPORTER_KEY})
    Client connect(URL url, ChannelHandler handler) throws RemotingException;
}

可以发现 Transporter 默认获取的是 nettyTransporter 对象,所以最终调用 NettyTransporter.bind(),之后就为 NettyServer 服务端启动逻辑。

public class NettyServer extends AbstractServer implements Server {

    private static final Logger logger = LoggerFactory.getLogger(NettyServer.class);

    private Map<String, Channel> channels; // <ip:port, channel>

    private ServerBootstrap bootstrap;

    private org.jboss.netty.channel.Channel channel;

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

    @Override
    protected void doOpen() throws Throwable {
        NettyHelper.setNettyLoggerFactory();
        ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true));
        ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true));
        ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, getUrl().getPositiveParameter(IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS));
        bootstrap = new ServerBootstrap(channelFactory);

        final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
        channels = nettyHandler.getChannels();
        // https://issues.jboss.org/browse/NETTY-365
        // https://issues.jboss.org/browse/NETTY-379
        // final Timer timer = new HashedWheelTimer(new NamedThreadFactory("NettyIdleTimer", true));
        bootstrap.setOption("child.tcpNoDelay", true);
        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            @Override
            public ChannelPipeline getPipeline() {
                NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyServer.this);
                ChannelPipeline pipeline = Channels.pipeline();
                /*int idleTimeout = getIdleTimeout();
                if (idleTimeout > 10000) {
                    pipeline.addLast("timer", new IdleStateHandler(timer, idleTimeout / 1000, 0, 0));
                }*/
                pipeline.addLast("decoder", adapter.getDecoder());
                pipeline.addLast("encoder", adapter.getEncoder());
                pipeline.addLast("handler", nettyHandler);
                return pipeline;
            }
        });
        // bind
        channel = bootstrap.bind(getBindAddress());
    }

    @Override
    protected void doClose() throws Throwable {
        try {
            if (channel != null) {
                // unbind.
                channel.close();
            }
        } catch (Throwable e) {
            logger.warn(e.getMessage(), e);
        }
        try {
            Collection<org.apache.dubbo.remoting.Channel> channels = getChannels();
            if (CollectionUtils.isNotEmpty(channels)) {
                for (org.apache.dubbo.remoting.Channel channel : channels) {
                    try {
                        channel.close();
                    } catch (Throwable e) {
                        logger.warn(e.getMessage(), e);
                    }
                }
            }
        } catch (Throwable e) {
            logger.warn(e.getMessage(), e);
        }
        try {
            if (bootstrap != null) {
                // release external resource.
                bootstrap.releaseExternalResources();
            }
        } catch (Throwable e) {
            logger.warn(e.getMessage(), e);
        }
        try {
            if (channels != null) {
                channels.clear();
            }
        } catch (Throwable e) {
            logger.warn(e.getMessage(), e);
        }
    }

    @Override
    public Collection<Channel> getChannels() {
        Collection<Channel> chs = new HashSet<Channel>();
        for (Channel channel : this.channels.values()) {
            if (channel.isConnected()) {
                chs.add(channel);
            } else {
                channels.remove(NetUtils.toAddressString(channel.getRemoteAddress()));
            }
        }
        return chs;
    }

    @Override
    public Channel getChannel(InetSocketAddress remoteAddress) {
        return channels.get(NetUtils.toAddressString(remoteAddress));
    }

    @Override
    public boolean isBound() {
        return channel.isBound();
    }

}

Dubbo 服务解析———服务注册

服务注册就是将服务器信息保存到注册中心里。

// RegistryProtocol.export()
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
    URL registryUrl = getRegistryUrl(originInvoker);
    // url to export locally
    URL providerUrl = getProviderUrl(originInvoker);

    final URL overrideSubscribeUrl = getSubscribedOverrideUrl(providerUrl);
    final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
    overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);

    providerUrl = overrideUrlWithConfig(providerUrl, overrideSubscribeListener);
    //export invoker
    final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker, providerUrl);

    // url to registry
    final Registry registry = getRegistry(originInvoker);
    final URL registeredProviderUrl = getRegisteredProviderUrl(providerUrl, registryUrl);
    ProviderInvokerWrapper<T> providerInvokerWrapper = ProviderConsumerRegTable.registerProvider(originInvoker,
            registryUrl, registeredProviderUrl);
    

    // 服务注册核心方法
    boolean register = providerUrl.getParameter(REGISTER_KEY, true);
    if (register) {
        register(registryUrl, registeredProviderUrl);
        providerInvokerWrapper.setReg(true);
    }

    // Deprecated! Subscribe to override rules in 2.6.x or before.
    registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);

    exporter.setRegisterUrl(registeredProviderUrl);
    exporter.setSubscribeUrl(overrideSubscribeUrl);
    //Ensure that a new exporter instance is returned every time export
    return new DestroyableExporter<>(exporter);
}
//RegistryProtocol.register()
public void register(URL registryUrl, URL registeredProviderUrl) {
    //获取注册中心对象Registry
    //这里的registryFactory,是自适应扩展RegistryFactory@Adaptive
    //registryUrl.protocol=zookeeper,所以获取到的是ZookeeperRegistryFactory
    //最后通过ZookeeperRegistryFactory.createRegistry创建并获取注册中心对象,ZookeeperRegistry
    Registry registry = registryFactory.getRegistry(registryUrl);
    //调用register方法,注册服务
    //Registry运用的是模板方法设计模式
    //底层的抽象实现FailbackRegistry,调用各个实现类的doRegister方法。如ZookeeperRegister.doRegister.
    //最终通过ZookeeperRegister.doRegister,注册服务信息,即将服务信息写到注册中心里。这样其他服务就可以从注册中心读到服务信息了。
    registry.register(registeredProviderUrl);
}

registryFactoryRegsitryFactory 接口,并标记了 @SPI@Adaptive 注解,说明动态获取扩展类型,这里根据 URL 信息(dubbo-provider.xml 配置文件中关于注册中心的信息)最终获取 ZookeeperRegistryFactory 注册中心工厂。

为了兼容多种注册中心,采用了模板设计模式, FailbackRegistry 提供了 register() 接口,该接口由多个注册中心 zookeeper、redis 等等分别实现注册方法。

//FailbackRegistry.register()
@Override
public void register(URL url) {
    //... 省略代码
    try {
        // 调用子类实现的模板方法,完成注册
        doRegister(url);
    } catch (Exception e) {
        //... 省略代码
    }
}
// ZookeeperRegistry.doRegistry()
public void doRegister(URL url) {
    try {
        // zk 服务注册
        zkClient.create(toUrlPath(url), url.getParameter(DYNAMIC_KEY, true));
    } catch (Throwable e) {
        throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
    }
}
// NacosRegistry.doRegistry()
public void doRegister(URL url) {
    final String serviceName = getServiceName(url);
    final Instance instance = createInstance(url);
    execute(namingService -> namingService.registerInstance(serviceName, instance));
}

到这里为止就完成了服务的启动和注册源码分析,以 zk 为例,服务信息的存储格式为:

  • /dubbo:所有数据保存在 /dubbo 目录下;
  • /dubbo/configurators:配置类信息;
  • /dubbo/consumers:消费者信息;
  • /dubbo/providers:服务提供者信息;
  • /dubbo/routers:路由信息;
posted @ 2024-06-14 15:42  Stitches  阅读(35)  评论(0)    收藏  举报