JavaEE 企业级分布式高级架构师(九)Dubbo(2.7)学习笔记(4)

Dubbo内核的源码解析

Dubbo的SPI源码解析

  • 下面以 Protocol 扩展实例的获取过程为例来解析 SPI 的执行过程。

分析思路

在这里插入图片描述

分析入口-ServiceConfig

  • ServiceConfig:
private static final Protocol protocol = ExtensionLoader
                                        .getExtensionLoader(Protocol.class)     // 获取Protocol接口的Loader
                                        .getAdaptiveExtension();
  • 这里分解为两步:
    • 获取扩展类的加载器:getExtensionLoader(Protocol.class)
    • 获取自适应的扩展类:getAdaptiveExtension()
factory实例的loader创建

在这里插入图片描述

  • 至此,ExtensionFactory 的 extensionLoader 实例就创建完毕。接下来调用 getAdaptiveExtension() 获取自适应的 extensionFactory。
获取自适应的 extensionFactory 实例
  • 先整体分析下 getAdaptiveExtension() 这个方法:首先通过“双重检查锁”判断,调用 createAdaptiveExtension() 方法创建当前SPI的自适应实例。在创建自适应实例过程中,先通过 getAdaptiveExtensionClass() 方法获取到扩展类的类型,再通过反射调用无参构造器获取一个自适应实例,并完成实例注入(injectExtension 这个后面详细分析)。

在这里插入图片描述

获取扩展类类型
  • 继续分析 getExtensionClasses() 方法:用于将当前SPI接口的所有扩展类class缓存起来(四类:普通扩展类、adaptive类、wrapper类,及activate类)

在这里插入图片描述

  • 继续分析 loadDirectory()方法:

在这里插入图片描述

  • 继续分析 loadClass() 方法:

在这里插入图片描述

  • 这里我们可以看出,Dubbo 对于具有多个功能性扩展名的扩展类,其采用了三种方式来缓存:
    • 如果这个类是一个 Activate 类,其会专门缓存该类的第一个功能性扩展名到一个 map,这个 map 的 key 为这第一个扩展名,而 value 为 Activate 注解。
    • 对于每一个扩展类,其都会将这个类与其第一个扩展名配对后存放到一个 map,这个 map 的 key 为这个扩展类,而 value 则为第一个扩展名。
    • 对于每一个扩展类,其都会将每一个扩展名与这个类配对后放到一个map,这个 map 的 key 为扩展名,而 value 则为这个扩展类。

Debug分析下

获取ExtensionFactory实例

在这里插入图片描述

  • 可以看到,dubbo 中有两类 ExtensionFactory:SpringExtensionFactory 和 SpiExtensionFactory。由此可知,整个Dubbo框架中,所有的实例都是通过这两种方式创建出来的,一种是 spi 方式,一种是 spring 容器方式。如果 spi 方式的条件不满足,则采用 spring 容器的方式创建。
获取 Protocol 自适应实例
  • 前面的代码执行完毕,然后一层层的返回,最终就返回到了这里:获取到了 Protocol 的 extensionLoader

在这里插入图片描述

  • 此时,ExtensionFactory 的 ExtensionLoader 实例和 Protocol 的 ExtensionLoader 的 objectFactory 实例都创建完毕,返回其调用语句。

在这里插入图片描述

插件的IOC注入源码解析

  • 下面以 Dubbo 的注册中心 Zookeeper 实例是如何创建的为例来解析IOC的过程。

找到IOC源码

在这里插入图片描述

  • 继续分析:默认使用 zookeeper 注册中心作为配置中心

在这里插入图片描述

  • 继续分析 getExtension() 方法:获取动态配置工厂 DynamicConfigurationFactory 的名称为 zookeeper 的扩展类实例

在这里插入图片描述

injectExtension()

  • 接下来继续分析,调用instance实例的setter,完成实例的注入:

在这里插入图片描述

  • 继续分析 getExtension() 方法:逐次尝试通过SPI与Spring两种方式创建扩展类实例

在这里插入图片描述

  • 接下来再看下 Spring 方式获取扩展类实例的实现:其会分别根据名称、类型从Spring容器中获取实例

在这里插入图片描述

插件的AOP包装源码解析

  • Dubbo 的 AOP 是对 SPI 扩展类进行增强的方式,而 Wrapper 机制就是对 SPI 扩展类的增强。不同 SPI 的不同 Wrapper ,其增强的功能不同。
  • 这里我们将之前 13-wrapper 工程的代码为例进行源码解析,来分析扩展类是如何被包装起来的。

在这里插入图片描述

找到AOP源码

  • 运行示例代码,进行 Debug 分析:getExtension(extName) 首先获取到的是 TwoOrderWrapper 的实例(也可能是 OneOrderWrapper,wrapper 的缓存是放在 set 集合中的,无序)

在这里插入图片描述

调用执行

  • 逐级返回,直至 Order@Adaptive 类。

在这里插入图片描述

  • 继续 debug 分析完整个 Wrapper 的调用过程:

在这里插入图片描述

自动生成类的动态编译源码解析

  • 当一个 SPI 接口 没有 Adaptive 类时,系统会根据 Adaptive 方法为其自动生成一个 Adaptive 类,这个自动生成的类是一个 java 代码类,这个类是需要编译的。而该编译是由系统动态完成的。

Javassist简介

  • Javassist 是一个开源的分析、编辑和创建 Java 字节码的类库。 一般情况下,对字节码文件进行修改是需要使用虚拟机指令的。而使用 Javassist 可以直接使用 java 编码的形式,而不需要了解虚拟机指令,就能动态改变类的结构,或者动态生成类。

手工实现 Javassist 动态编译 15-javassist

  • 创建一个 Maven 工程,命名为 15-javassist,导入依赖:
<dependency>
  <groupId>org.javassist</groupId>
  <artifactId>javassist</artifactId>
  <version>3.26.0-GA</version>
</dependency>
  • 定义 JavassistCompiler:
public class JavassistCompiler {
    public static void main(String[] args) throws Exception {
        // 创建一个ClassPool实例,其是CtClass的工具类
        ClassPool pool = ClassPool.getDefault();
        // CtClass:Class Type Class,即字节码类型的类
        CtClass ctClass = genericClass(pool);
        // 创建一个自动生成类的实例,并调用其业务方法
        invokeInstance(ctClass);
    }

    private static CtClass genericClass(ClassPool pool) throws Exception {
        // 通过ClassPool生成一个Person类
        CtClass ctClass = pool.makeClass("com.yw.Person");
        // 下面的代码都是初始化CtClass实例的

        // 向CtClass中添加private String name属性
        CtField nameField = new CtField(pool.getCtClass("java.lang.String"), "name", ctClass);
        nameField.setModifiers(Modifier.PRIVATE);
        ctClass.addField(nameField);
        // 向CtClass中添加private int age属性
        CtField ageField = new CtField(pool.getCtClass("int"), "age", ctClass);
        ageField.setModifiers(Modifier.PRIVATE);
        ctClass.addField(ageField);
        // 向CtClass中添加name、age属性的setter与getter方法
        ctClass.addMethod(CtNewMethod.getter("getName", nameField));
        ctClass.addMethod(CtNewMethod.getter("setName", nameField));
        ctClass.addMethod(CtNewMethod.getter("getAge", ageField));
        ctClass.addMethod(CtNewMethod.getter("setAge", ageField));
        // 向CtClass中添加无参构造器
        CtConstructor ctConstructor = new CtConstructor(new CtClass[]{}, ctClass);
        String body = "{\nname=\"zhangsan\";\nage=23;\n}";
        ctConstructor.setBody(body);
        ctClass.addConstructor(ctConstructor);
        // 向CtClass中添加业务方法
        CtMethod ctMethod = new CtMethod(CtClass.voidType, "personInfo", new CtClass[]{}, ctClass);
        ctMethod.setModifiers(Modifier.PUBLIC);
        StringBuffer sb = new StringBuffer();
        sb.append("{\nSystem.out.println(\"name=\"+name);\n")
                .append("System.out.println(\"age=\"+age);\n}");
        ctMethod.setBody(sb.toString());
        ctClass.addMethod(ctMethod);
        // 将生成的字节码写入到.class文件
        byte[] bytes = ctClass.toBytecode();
        try (FileOutputStream fos = new FileOutputStream(new File("./Person.class"))) {
            fos.write(bytes);
        }
        return ctClass;
    }

    private static void invokeInstance(CtClass ctClass) throws Exception {
        Class<?> clazz = ctClass.toClass();
        Object obj = clazz.newInstance();
        obj.getClass().getMethod("personInfo", new Class[]{}).invoke(obj, new Object[]{});
    }
}
  • 运行测试,控制台结果输出如下,并生成 Person.class 文件:
name=zhangsan
age=23

解析Dubbo的动态编译

  • 分析入口:ServiceConfig 中获取 Protocol 实例的自适应扩展类。

在这里插入图片描述

  • 继续 Debug 看一下如何生成 Adaptive 类的代码的:

在这里插入图片描述

  • 至此我们就知道了自适应扩展类的代码是如何动态生成的,接下来的工作就是对其进行动态编译,Debug 返回上一步,继续分析:获取默认扩展名的实例,即javassistCompiler的实例,并调用 javassistCompiler 的 compile() 方法

在这里插入图片描述

  • 继续分析 javassistCompiler 的 compile() 方法:

在这里插入图片描述

  • 接着我们看一下 doCompile 这个方法:使用的是 Javassist 技术完成的动态编译,至此就了解到了自适应扩展类的动态编译过程。

在这里插入图片描述

Dubbo业务框架的源码解析

Dubbo与Spring整合源码解析

查找解析器

  • 从 dubbo 的 xml 配置文件开始:找到 dubbo 依赖,可以看到如下的三个文件,其中就包含 spring.schemas 和 spring.handlers 文件;打开 spring.schemas 和 spring.handlers 文件:DubboNamespaceHandler 就是 Dubbo 命名空间处理器。

在这里插入图片描述

Dubbo标签的解析

  • DubboNamespaceHandler 中有一个 init() 初始化方法,注册一个一个的 BeanDefinition 解析器,将配置文件中的各个标签通过 DubboBeanDefinitionParser 解析器进行解析,解析出来的结果封装到对应的 xxxConfig 对象中。
public class DubboNamespaceHandler extends NamespaceHandlerSupport {

    static {
        Version.checkDuplicate(DubboNamespaceHandler.class);
    }

    @Override
    public void init() {
        registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true));
        registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true));
        registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true));
        registerBeanDefinitionParser("config-center", new DubboBeanDefinitionParser(ConfigCenterBean.class, true));
        registerBeanDefinitionParser("metadata-report", new DubboBeanDefinitionParser(MetadataReportConfig.class, true));
        registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true));
        registerBeanDefinitionParser("metrics", new DubboBeanDefinitionParser(MetricsConfig.class, true));
        registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true));
        registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true));
        registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true));
        registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
        registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
        registerBeanDefinitionParser("annotation", new AnnotationBeanDefinitionParser());
    }
}
  • DubboBeanDefinitionParser:
public class DubboBeanDefinitionParser implements BeanDefinitionParser {
	/**
     * Dubbo标签解析器构造器
     * @param beanClass 标签属性读出值所封装的类型
     * @param required 指定id属性在当前标签中是否是必须的
     */
    public DubboBeanDefinitionParser(Class<?> beanClass, boolean required) {
        this.beanClass = beanClass;
        this.required = required;
    }

    /**
     * 解析指定标签
     * @param element 要解析的标签元素
     * @param parserContext 解析上下文,其中包含了当前配置文件中所有其它标签的解析信息
     * @param beanClass  标签属性读出值所封装的类型,是一种物理性封装
     * @param required  指定id属性在当前标签中是否是必须的
     * @return 返回解析对象定义器,这里暂且称其为"解析对象"。即当前标签最终解析出的“逻辑实例”的Bean定义器实例
     */
    @SuppressWarnings("unchecked")
    private static BeanDefinition parse(Element element, ParserContext parserContext, Class<?> beanClass, boolean required) {
		// ...略
	}
	// ...略
}
  • 接下来就简单分析下 DubboBeanDefinitionParser 的 parse() 方法:
step1-创建并初始化解析对象
  • 创建 RootBeanDefinition 对象,并设置不进行延迟初始化

在这里插入图片描述

step2-解决id问题
  • 解决 id 为空和 id 重复的问题:
    • 若标签中 id 属性值为空,且必须,则获取 beanName 值作为 id 值;若 id 值重复,则从 2 开始一次加1;
    • beanName 值首先从 name 属性值获取,若 name 属性值为空,且标签为 <dubbo:protocol>,则 name 属性值设为 dubbo,否则取 interface 属性值;若 beanName 仍为空,则去 beanClass 的类名,这样一定能保证 beanName 不为空,即 id 不为空。

在这里插入图片描述

step3-将id属性写入到解析对象
  • id 属性不为空时,又进行了一次判重,主要防止用户自定义id重复。然后将 id 与 BeanDefinition 注册到解析上下文,并将 id 属性添加到 BeanDefinition 中。

在这里插入图片描述

step4-对特殊标签的特殊处理
  • 主要特殊处理了四类标签:<dubbo:protocol/>、<dubbo:service/>、<dubbo:provider/>、<dubbo:consumer/>,以及 嵌套解析<dubbo:servie/>与<dubbo:provider/>标签,<dubbo:reference/>与<dubbo:consumer/>标签。

在这里插入图片描述

step5-对所有标签的普适性处理

在这里插入图片描述

重要接口

Invocation

  • Invocation 封装了远程调用的具体信息:Invoker 服务提供者、方法名、参数与参数类型、附件

在这里插入图片描述

Invoker

  • Invoker 是提供者 provider 的代理对象,在代码中就代表提供者。特别是在消费者进行远程调用时,其通过服务路由、负载均衡、集群容错等机制要查找的就是 Invoker。找到了其需要的 Invoker 实例就可以进行远程调用了。

在这里插入图片描述

Exporter

  • Exporter 是服务暴露对象。其包含一个很重要的方法 getInvoker(),用于获取当前服务暴露实例所包含的远程调用实例 Invoker,即可以进行的远程调用。而 unexport()方法会使服务不进行服务暴露。

在这里插入图片描述

Directory

  • Directory 中包含一个很重要的方法 list(),其返回结果为一个 List<Invoker>。其实简单来说,可以将 Directory 理解为一个动态的 Invoker 列表。Directory 也继承自 Node,因此也可以作为 adaptive 方法的参数。

在这里插入图片描述

  • 继承体系:

在这里插入图片描述

服务发布

分析入口

  • DubboNamespaceHandler 命名空间处理器的初始化方法:从 ServiceBean 这个类开始分析,ServiceBean 中实现了很多 Spring 的接口,其中有一个 ApplicationListener 接口,这个接口只有一个方法 onApplicationEvent(event),当Spring容器被刷新时会触发该方法的执行。
registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));
  • 正式进行服务暴露之前,做了一些校验工作:
    • 检测注册中心的可用性,不可用则直接抛异常结束
    • 判断是否需要进行服务暴露,已经是否延迟服务暴露

在这里插入图片描述

分析 doExport() 方法

  • 首先判断是否已经取消发布,或者已经发布过了。然后将注册中心与服务暴露协议相结合进行服务暴露:获取所有注册中心的标准化URL,遍历当前服务所支持的所有服务暴露协议,并与每一个注册中心形成一个 invoker 进行服务暴露。

在这里插入图片描述

获取所有注册中心的标准化URL
  • 详细分析一下 loadRegistries 这个方法:解析 <dubbo:registry/> 标签,封装了很多属性到一个 Map 集合中,并将所有注册中心地址都进行 URL 标准化。

在这里插入图片描述

  • 分析一下 appendRuntimeParameters(map) 方法:将运行时的一些参数值写入到map

在这里插入图片描述

  • 分析 parseURLs() 方法:获取当前的address属性,并解析出的所有注册中心URL

在这里插入图片描述

  • Debug 一下:

在这里插入图片描述

为每一个协议暴露到所有注册中心
  • 首先,判断协议名称是否为空,若为空,则使用 dubbo 协议进行暴露。然后,创建一个 Map 用于存放各种属性,这些属性将来要封装到 URL 的元数据中。

在这里插入图片描述

  • 形成服务暴露 URL,并且可以对现有的扩展类进行再配置(通过定义ConfiguratorFactory SPI接口的实现类),完成对 URL 的再配置。这又是 Dubbo 扩展性的体现。

在这里插入图片描述

  • 下面继续分析 doExportUrlsFor1Protocol() 这个方法后面的代码:首先获取 scope 属性值,scope 属性值不为 none,才进行服务暴露。并且当 scope 属性值不为remote,则进行本地暴露;当 scope 属性值不为local,则进行远程暴露。这意味着 scope 如果随便配置一个值,例如 scope=“xxx”,则会本地和远程都会进行服务暴露。
  • 在进行远程服务暴露时,会根据有没有注册中心,生成不同的 invoker 代理对象,最后完成服务暴露,并将 exporter 添加到 List 集合中。
  • 分析到这里,我们就已经找到了 本地暴露和远程暴露 分析的入口,同时也找到了动态生成提供者代理对象分析的入口,这些在后面会进行详细分析。

在这里插入图片描述

泛化服务/泛化引用

  • 在<dubbo:service/> 与 <dubbo:reference> 中都有一个 generic 属性,其在 <dubbo:service/> 标签中可以提供泛化服务,在 <dubbo:reference> 中可以提供泛化引用。泛化服务与泛化引用无需同时使用。其主要是针对某一方没有具体业务接口的 .class 情况的。这又是 Dubbo 扩展性的一个体现。
提供者工程 16-provider-generic
  • 复制 02-provider-zk 工程,重命名为 16-provider-generic,在此基础上修改。去掉 00-api 工程的依赖,即不再需要该业务接口了。
  • 定义 GerericServiceImpl 类:
/**
 * 定义一个泛化服务:通用服务、一般性服务
 */
public class GenericServiceImpl implements GenericService {
    /**
     * @param method         消费者通过远程调用的方法名
     * @param parameterTypes 方法参数类型列表
     * @param args           消费者通过远程调用传递来的实参值
     */
    @Override
    public Object $invoke(String method, String[] parameterTypes, Object[] args) throws GenericException {
        // 根据不同的调用方法,实现不同的服务
        if ("hello".equals(method)) {
            return "Generic hello, " + args[0];
        }
        return null;
    }
}
  • 修改 spring-provider.xml:

在这里插入图片描述

消费者工程 16-consumer-generic
  • 复制 02-consumer-zk 工程,重命名为 16-consumer-generic,在此基础上修改。去掉 00-api 工程的依赖,即不再需要该业务接口了。
  • 修改 spring-consumer.xml:

在这里插入图片描述

  • 修改启动测试类:

在这里插入图片描述

  • 测试:启动 provider 和 consumer:

在这里插入图片描述

进行本地服务暴露

  • 首先修改 URL 的协议、主机和端口号,然后调用 InjvmProtocol 的 export() 方法,创建 InjvmExporter 对象完成服务暴露,即存放到 Map 集合,key 是服务接口名,value 是 InjvmExporter 对象。

在这里插入图片描述

进行远程服务暴露

  • 分析入口:在 ServiceConfig 的 doExportUrlsFor1Protocol 方法中
Exporter<?> exporter = protocol.export(wrapperInvoker);

在这里插入图片描述

  • 接下来一步步 Debug 跟踪 export 方法的调用:这里是注册中心的一系列export,依次经过 Protocol$Adaptive、ProtocolListenerWrapper、ProtocolFilterWrapper、RegistryProtocol

在这里插入图片描述

  • 接下来整体先分析一下 RegistryProtocol 的 export() 这个方法:

在这里插入图片描述

获取注册中心URL和提供者URL
  • 获取注册中心URL:通过源 invoker 提取出元数据 registry=zookeeper,并将 registry 替换为 zookeeper,这样就拿到了指定协议的注册中心 URL,例如:
zookeeper://192.168.254.120:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&export=dubbo%3A%2F%2F192.168.0.100%3A20880%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26bean.name%3Dorg.apache.dubbo.demo.DemoService%26bind.ip%3D192.168.0.100%26bind.port%3D20880%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D25811%26qos.port%3D22222%26register%3Dtrue%26release%3D%26side%3Dprovider%26timestamp%3D1624979190366&pid=25811&qos.port=22222&timestamp=1624979190354
  • 获取提供者URL:也是通过源 invoker 提取出元数据 export 的值,并进行解码得到提供者URL,例如:
dubbo://192.168.0.100:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bean.name=org.apache.dubbo.demo.DemoService&bind.ip=192.168.0.100&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=25811&qos.port=22222&register=true&release=&side=provider&timestamp=1624979190366

在这里插入图片描述

获取注册中心实例
  • 先跳过 doLocalExport(originInvoker, providerUrl) 这个方法,即在完成服务暴露之后,分析下如何获取到注册中心实例。getRegistry 这个方法依次经过:RegistryProtocol、RegistryFactory$Adaptive、AbstractRegistryFactory,在 AbstractRegistryFactory 中使用 ReentrantLock 加锁进行创建,并将创建出的注册中心实例缓存到Map中。

在这里插入图片描述

  • 继续分析 createRegistry 这个方法:通过 ZK 注册中心工厂类创建 ZK 注册中心,创建 ZK 客户端时,使用的是 Dubbo 自己经过一定封装的 Curator,连接失败会在重试范围内进行重连,连接的URL:
zookeeper://192.168.254.120:2181/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&interface=org.apache.dubbo.registry.RegistryService&pid=28537&qos.port=22222&timestamp=1624981736384

在这里插入图片描述

完成真正的服务暴露

  • 现在来分析 doLocalExport 这个方法,即完成正在的服务暴露:这里是dubbo服务的一系列export,依次进过 RegistryProtocol、Protocol$Adaptive、ProtocolFilterWrapper、ProtocolListenerWrapper、DubboProtocol

在这里插入图片描述

分析 DubboProtocol 的 export 方法
  • 主要完成两件事:
    • 创建一个与服务暴露协议相绑定的 exporter 对象,并写入到缓存 map 中;
    • 创建并启动一个 Netty Server
      在这里插入图片描述
分析 openServer 方法
  • 会创建出一个 ExchangeServer(十层架构里面的同异步转换服务对象),并缓存到 map 中,一个 ExchangeServer 仅会负载一个 NettyServer 的同异步转换工作,即一个 ExchangeServer 就会对应一个 NettyServer

在这里插入图片描述

  • 继续分析一堆 bind 方法:依次进过 Exchangers、HanderExchanger、Transporters、Transporter$Adaptive、NettyTransporter

在这里插入图片描述

  • 继续跟踪 doOpen() 方法:终于找到 NettyServer 创建的地方了。

在这里插入图片描述

重置 server(更新url)
  • 前面创建 ExchangeServer 时使用了双重检查锁,是为了保证同一个服务暴露协议,在同时暴露当前主机中的多个服务时,只会创建出一个 ExchangeServer。那么当第一个服务完成 ExchangeServer 创建后,后面的服务再进行服务暴露时,需要重置 server,即更新url的值,例如第一个服务暴露后的url:
dubbo://192.168.0.100:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bean.name=org.apache.dubbo.demo.DemoService&bind.ip=192.168.0.100&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello,sayHi&pid=34143&qos.port=22222&register=true&release=&side=provider&timestamp=1624986940711
  • 重置URL:即将后暴露的URL数据写入到之前暴露的URL中,重置后的 url 变为了:
dubbo://192.168.0.100:20880/org.apache.dubbo.demo.SomeService?anyhost=true&application=demo-provider&bean.name=org.apache.dubbo.demo.SomeService&bind.ip=192.168.0.100&bind.port=20880&deprecated=false&dubbo=2.0.2&dynamic=true&generic=false&interface=org.apache.dubbo.demo.SomeService&methods=sayGood,sayHi&pid=33760&qos.port=22222&register=true&release=&side=provider&timestamp=1624986628596

在这里插入图片描述

完成服务注册
  • 现在再来详细分析一下如何将提供者注册到 ZK 中,即前面分析到的 RegistryProtocol 中的 register 这个方法,register 方法依次进过 RegistryProtocol、FailbackRegistry、AbstractRegistry、FailbackRegistry、ZookeeperRegistry、AbstractZookeeperClient。
  • 从缓存中获取注册中心实例,将提供者 URL 发送到注册中心ZK,完成服务注册,即写入临时节点,节点信息例如:
/dubbo/org.apache.dubbo.demo.DemoService/providers/dubbo%3A%2F%2F192.168.0.100%3A20880%2Forg.apache.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26bean.name%3Dorg.apache.dubbo.demo.DemoService%26deprecated%3Dfalse%26dubbo%3D2.0.2%26dynamic%3Dtrue%26generic%3Dfalse%26interface%3Dorg.apache.dubbo.demo.DemoService%26methods%3DsayHello%2CsayHi%26pid%3D35309%26register%3Dtrue%26release%3D%26side%3Dprovider%26timestamp%3D1625016941968

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

讲文明的喜羊羊拒绝pua

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值