服务注册与发现-Dubbo篇

本文详细介绍了Dubbo的服务注册与发现机制,包括服务注册的使用、原理,以及注册中心的角色和工作流程。服务提供者通过ServiceConfig和ServiceBean进行服务注册,消费者通过ReferenceConfig和ReferenceBean进行服务发现。注册中心如ZookeeperRegistry实现服务的注册和订阅,使用Adaptive机制动态获取合适的实现。Dubbo通过XML、注解和API三种方式配置服务和消费者,并利用ProxyFactory生成动态代理实现方法调用。
摘要由CSDN通过智能技术生成

关键词:服务注册、服务发现、注册中心


 

Dubbo源码版本:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>dubbo</artifactId>
    <version>2.6.9</version>
</dependency>

(读Dubbo源码,关键是要搞清URL中属性protocol的值是什么,以及@Adaptive注解与getAdaptiveExtension方法的原理以及获取到的具体的对象。)

角色:提供者(服务端)、消费者(客户端、服务调用者)、注册中心

消费者当需要调用服务提供者时,消费者要知道服务提供者的位置,即IP、端口以及接口等信息。在早期的一个消费者对一个提供者,消费者可以将提供者的地址写死在代码中。但是随着分布式、集群的大规模应用,更多的是多个消费者对多个提供者。因为在实际场景中,服务会因为某些原因出现不可用的状态,此时为了提高整个系统的可用性,需要消费者能够动态感知可用服务者的列表。这个感知的能力要么服务提供者将自己可用的信息一一告知每一个服务消费者,如果消费者的数量很多,这样显然是效率非常低的。还有一种就是服务提供者将自己的信息暴露在一个第三方的位置,消费者则去这个第三方的位置获取可用服务端列表。这个第三方的位置就是注册中心。

消费者要想调用服务提供者,首先服务提供者要进行服务注册,然后消费者再进行服务发现。

Dubbo中三种角色对应的类:

提供者:

ServiceConfig

public class ServiceConfig<T> extends AbstractServiceConfig

ServiceBean

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

消费者:

ReferenceConfig

public class ReferenceConfig<T> extends AbstractReferenceConfig

ReferenceBean

public class ReferenceBean<T> extends ReferenceConfig<T> implements FactoryBean, ApplicationContextAware, InitializingBean, DisposableBean

注册中心:

RegistryConfig

public class RegistryConfig extends AbstractConfig

 █ 服务注册

担任远程服务提供者的服务,在启动的时候,会将自身提供的接口服务列表注册到注册中心上。在Dubbo中,接口服务列表就是以接口和实现类方法以URL的形式对外暴露和注册。

使用:

Dubbo提供了三种方式可以像注册中心注册服务,分别是XML配置、注解、以及API。不管哪种方式,服务注册的起点都是export方法。

(1)XML配置

结合Spring框架使用,依靠Spring的功能,DubboNamespaceHandlerDubboBeanDefinitionParser完成配置的解析与Bean的创建,此时每一个接口服务会被创建成ServiceBean对象,ServiceBean继承自ServiceConfig,具有ServiceConfig的功能,还会结合Spring功能, 在Spring启动完成后调用export方法对外暴露和注册服务。

<bean id="serviceIdxxx" class="接口实现类的全限定名" />
<dubbo:service interface="接口的全限定名" ref="serviceIdxxx" />

(2)注解

注解和XML配置的功能差不多,都是依托于Spring使用的。在接口实现类标记@Service注解即可,注意此注解是:com.alibaba.dubbo.config.annotation.Service。DubboComponentScanRegistrarServiceAnnotationBeanPostProcessor完成注解的扫描与解析,根据注解配置创建ServiceBean对象。

package com.alibaba.dubbo.config.annotation;
......
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Inherited
public @interface Service {
    Class<?> interfaceClass() default void.class;

    String interfaceName() default "";

    String version() default "";

    String group() default "";

    String path() default "";

    ......
}

(3)API

当不结合Spring框架使用的时候,可以使用API方式。

ServiceConfig serviceConfig = new ServiceConfig();
// 接口
serviceConfig.setInterface(xxxx.class);
serviceConfig.setId("serviceIdxxx");
......
serviceConfig.export();

原理:

Dubbo会将服务提供者中的某一个接口实现类作为整体,封装成一个ServiceConfig对象(XML配置<dubbo:service>或注解@Service或手动编写ServiceConfig),然后通过export方法暴露服务和完成向注册中心注册服务。ServiceBean继承了ServiceConfig,与Spring完成契合,实现将ServiceConfig对象注册成Spring容器中的bean。下面从ServiceConfig的export开始,一步步看Dubbo是如何完成服务注册的。(在读源码之前,建议先学习Dubbo中@SPI@Adaptive@Activate三个注解的功能以及ExtensionLoader获取三个注解类的方法。尤其是ExtensionLoader.getAdaptiveExtension。Dubbo大量使用了此方法来获取扩展类对象,实现了根据参数动态获取不同的扩展类的功能。有兴趣的同学,请戳这里《Dubbo之@Adaptive》

(1)export

// 通过此方法开始服务暴露与注册
public synchronized void export() {
    ......
    else {
            // 去暴露和注册服务
            this.doExport();
        }
}

(2)doExport

// 该方法大部分内容就是完成属性的初始化工作,比如协议、注册中心、监控中心等。这些配置也是Spring在启动的时候,根据XML配置
// 或注解配置完成赋值的,比如<dubbo:protocol />
protected synchronized void doExport() {
    if (this.unexported) {
        throw new IllegalStateException("Already unexported!");
    } else if (!this.exported) {
            ......
            // 暴露工作又转移到了这个方法
            this.doExportUrls();
            ......
        } else {
            throw new IllegalStateException("<dubbo:service interface=\"\" /> interface not allow null!");
        }
    }
}

(3)doExportUrls

private void doExportUrls() {
    // 获取所有的注册中心
    List<URL> registryURLs = this.loadRegistries(true);
    // this.protocols是协议集合
    Iterator var2 = this.protocols.iterator();
    // 一个服务按照每一种协议都向所有的注册中心注册 
    while(var2.hasNext()) {
        ProtocolConfig protocolConfig = (ProtocolConfig)var2.next();
        // 进入此方法
        this.doExportUrlsFor1Protocol(protocolConfig, registryURLs);
    }

}

(4)doExportUrlsFor1Protocol

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {
    ......
    if (!"none".toString().equalsIgnoreCase(scope)) {
        // 本地暴露服务
        if (!"remote".toString().equalsIgnoreCase(scope)) {
            this.exportLocal(url);
        }
        // 向注册中心注册服务
        if (!"local".toString().equalsIgnoreCase(scope)) {
            if (logger.isInfoEnabled()) {
                logger.info("Export dubbo service " + this.interfaceClass.getName() + " to url " + url);
            }
            // 根据配置获取到的注册中心服务列表,依次向注册中心注册服务
            if (registryURLs != null && !registryURLs.isEmpty()) {
                Iterator var25 = registryURLs.iterator();

                while(var25.hasNext()) {
                    ......
                    // proxy的值决定了proxyFactory对象,该值要么在配置的时候指定,否则默认为javassist
                    proxy = url.getParameter("proxy");
                    if (StringUtils.isNotEmpty(proxy)) {
                        registryURL = registryURL.addParameter("proxy", proxy);
                        }
                    // 获取Invoker,下面进入proxyFactory.getInvoker看看
                    Invoker<?> invoker = proxyFactory.getInvoker(this.ref, this.interfaceClass, registryURL.addParameterAndEncoded("export", url.toFullString()));
                    DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);
                    // 协议对外暴露
                    Exporter<?> exporter = protocol.export(wrapperInvoker);
                    this.exporters.add(exporter);
                }
            } 
            ......
        }
    }

    this.urls.add(url);
}

Invoker<?> invoker = proxyFactory.getInvoker(this.ref, this.interfaceClass, registryURL.addParameterAndEncoded("export", url.toFullString()));其中proxyFactory是private static final ProxyFactory proxyFactory = (ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(),学习过《Dubbo之Adaptive》的同学应该知道这里的功能?看看ProxyFacoty,对于getInvoker等三个方法,会根据参数Invoker或URL中的proxy值来决定具体的ProxyFactory对象。即若proxy=javassist,则会调用JavassistProxyFactory中的getProxy方法。则上面这句代码,会根据registryURL中proxy的值来决定使用哪个ProxyFactory对象,proxy要么在XML或注解中指定,默认为JavassistProxyFactory:

@SPI("javassist")
public interface ProxyFactory {
    @Adapti
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值