dubbo启动流程分析记录

一、dubbo provider启动流程

1.自动装配

dubbo springboot启动方式,自动装配bean DubboAutoConfiguration,创建bean ServiceAnnotationBeanPostProcessor,是个BeanDefinitionRegistryPostProcessor。因此在com.alibaba.dubbo.config.spring.beans.factory.annotation.ServiceAnnotationBeanPostProcessor.postProcessBeanDefinitionRegistry(BeanDefinitionRegistry)方法内,扫描@com.alibaba.dubbo.config.annotation.Service注解,注册为IOC bean,同时注册IOC内一个 ServiceBean,其ref就是IOC bean。

比如下面定义一个dubbo provider

@com.alibaba.dubbo.config.annotation.Service
public class ProductServiceImpl implements ProductService {
//内容略
}

在IOC 内注册两个bean,一个是beanName=productServiceImpl ,bean对象就是ProductServiceImpl;另外一个是ServiceBean,其ref就是ProductServiceImpl。

PS:值得参考的是ServiceAnnotationBeanPostProcessor对于@com.alibaba.dubbo.config.annotation.Service注解扫描注解为bean的用法挺好,以后我们自定义注解扫描,也可以直接参考拿来使用。

2.ServiceBean处理

ServiceBean是个InitializingBean、Aware、ApplicationListener,因此在IOC bean的初始化过程也会执行InitializingBean、Aware、ApplicationListener。

ServiceBean采用了模板,ServiceBean持有ProviderConfig、ApplicationConfig、ModuleConfig、RegistryConfig、MonitorConfig、ProtocolConfig

在afterPropertiesSet()内就是设置ProviderConfig、ApplicationConfig、ModuleConfig、RegistryConfig、MonitorConfig、ProtocolConfig保存到ServiceBean。 提问:这些XXXConfig是怎么来的呢?后面解释

接着判断非延时暴露,则直接暴露服务,否则在监听器执行时候进行暴露。

3.服务暴露export()

3.1.检测dubbo.xxx.配置属性,配置到对应的XXXConfig对象

检查属性or环境变量的dubbo.xxx.配置开头的属性,配置到XXXConfig对象,本质就是保存到ServiceBean对象。

检测dubbo.provider.开头属性,配置到ProviderConfig

检测dubbo.application.开头属性,配置到ApplicationConfig

检测dubbo.registry.开头属性,配置到RegistryConfig

检测dubbo.service.开头属性,配置到ServiceConfig,即ServiceBean

3.2.把服务暴露到每个注册中心

遍历注册中心,把服务注册到每个注册中心。

本地暴露,感觉鸡肋,本来是个IOC bean了,在当前服务内直接使用就行了,本地暴露忽略。

远程暴露分两步

step1: 使用ProxyFactory对目标ref生成代理对象Invoker。

step2: 把step1生成的Invoker对象暴露到zk注册中心。

3.3.生成Invoker对象

使用ProxyFactory对目标对象ref生成代理对象Invoker。默认使用的是JavassistProxyFactory.getInvoker()

3.4.把Invoker对象暴露注册到注册中心

此时协议是registry,使用的是RegistryProtocol进行暴露服务

3.4.1.暴露Invoker对象 doLocalExport

step1: 使用wrapper类,对Invoker增强,创建filter chain

step2: 使用DubboProtocol进行暴露服务,即封装invoker为DubboExporter,并缓存到DubboProtocol.exporterMap,这个map就是用于接收consumer请求时,根据port+interfaceName+version+group作为key,从此缓存map中获取到DubboExporter。

step3: 创建tcp server,监听20880端口(默认)

核心代码如下

//com.alibaba.dubbo.registry.integration.RegistryProtocol.doLocalExport(Invoker<T>)
private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker) {
String key = getCacheKey(originInvoker);
ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
if (exporter == null) {
synchronized (bounds) {
exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
if (exporter == null) {
final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));
exporter = new ExporterChangeableWrapper<T>((Exporter<T>) protocol.export(invokerDelegete), originInvoker);//DubboProtocol.export
bounds.put(key, exporter);
}
}
}
return exporter;
}
3.4.2.服务注册到zk

step1: 连接zk,与zk长连接

step2: 在zk创建provider服务临时节点

step3: 创建/dubbo/$interfaceName/configurators节点,并订阅此节点,如果此节点有变化,则dubbo provider服务重新暴露(OverrideListener)。这个对应dubbo-admin上的动态配置。

核心代码如下在ZookeeperRegistry.doSubscribe(URL, NotifyListener)

4.总结

dubbo provider启动流程总结,根据官方启动流程图如下

服务提供者暴露服务的主过程:

首先 ServiceConfig 类拿到对外提供服务的实际类 ref(如:HelloWorldImpl),然后通过 ProxyFactory 类的 getInvoker 方法使用 ref 生成一个 AbstractProxyInvoker 实例,到这一步就完成具体服务到 Invoker 的转化。接下来就是 Invoker 转换到 Exporter 的过程。

Dubbo 处理服务暴露的关键就在 Invoker 转换到 Exporter 的过程,上图中的红色部分

Dubbo 协议的 Invoker 转为 Exporter 发生在 DubboProtocol 类的 export 方法,它主要是打开 socket 侦听服务,并接收客户端发来的各种请求,通讯细节由 Dubbo 自己实现。

zk协议是把Exporter 注册到注册中心,并监听注册中心对应节点的变化。

dubbo provider启动流程

provider启动记录xmind地址https://gitee.com/yulewo123/mdpicture/blob/master/document/dubbo/dubbo provider启动记录.xmind

二、dubbo provider generic启动流程

1.dubbo provider服务泛化例子

dubbo 服务泛化不能采用注解方式,只能使用xml或java api方式。以java api方式为例

//定义一个service
public class MyGenericService implements GenericService {//必须实现GenericService public Object $invoke(String methodName, String[] parameterTypes, Object[] args) throws GenericException {
if ("sayHello".equals(methodName)) {
return "Welcome " + args[0];
}
}
} // @org.springframework.stereotype.Service
public class DubboGenericService implements SmartInitializingSingleton { @Override
public void afterSingletonsInstantiated() {
ServiceConfig<GenericService> service = new ServiceConfig<GenericService>();
ApplicationConfig application = new ApplicationConfig("test-provider");
service.setApplication(application);
RegistryConfig registryConfig = new RegistryConfig();
registryConfig.setAddress("zookeeper://127.0.0.1:2181");
service.setRegistry(registryConfig);
service.setInterface("com.suibian.WhateverService");//用于指定要发布的接口名称,这个名称可以不对应任何的java接口类,甚至可以随便写。 GenericService genericService = (method, parameterTypes, args) -> {
if ("method1".equals(method)) {
return "method1 result:" + args[0];
}
if ("method2".equals(method)) {
return 12345;
}
if (parameterTypes.length == 2
&& parameterTypes[0].equals("java.lang.String")
&& parameterTypes[1].equals("java.lang.Integer")
&& "method3".equals(method)) {
return "method3,param1:" + args[0] + ",param2:" + args[1];
}
return null;
};
service.setRef(genericService);
service.export();
}
}

ServiceConfig.setInterface()用于指定要发布的接口名称,这个名称可以不对应任何的java接口类,甚至可以随便写。在上面的例子中com.suibian.WhateverService这个名字就是随便写的,实际上代码中并没有这个接口类。

因为这样发布的Dubbo服务没有具体的接口类,所以invoke()方法的第一个参数String var1(原本是方法名)和第二个参数String[] var2(原本是方法参数类型列表)就脱离了具体接口类的束缚,可以接收任意值,就像例子中所写的一样,按照不同的参数执行不同的逻辑,就像一个网关一样(一个接口行天下)。

因此当服务提供者使用GenericService接口时,可以省略Interface的代码,省略方法和参数的声明,通过指定接口名称的方式向zookeeper发布Dubbo服务。这时GenericService就像是一个网关。

2.dubbo provider服务泛化启动流程

泛化服务启动和普通服务启动流程完全一致,ref通过ProxyFactory生成Invoker,Invoker通过DubboProtocol进行服务暴露,通过RegistryProtocol注册到zk。 dubbo泛化服务注册到zk例子 dubbo://192.168.5.1:20880/com.suibian.WhateverService?anyhost=true&application=test-provider&dubbo=2.0.2&generic=true&interface=com.suibian.WhateverService&methods=*&pid=12744&side=provider&timestamp=1625669557658

和普通服务细节不同之处是:

1.ServiceConfig.interfaceClass是GenericService,ServiceConfig.interfaceName是自定义的服务名称

2.会在filter chain增加个GenericImplFilter,用于解析泛化请求

三、dubbo consumer启动流程

@Reference注解由ReferenceBean处理,实现了Aware、InitializingBean、FactoryBean。因此

首先,在afterPropertiesSet()操作内保存ConsumerConfig、ApplicationConfig、ModuleConfig、RegistryConfig、MonitorConfig到ReferenceBean。

然后,由于是FactoryBean,因此在getObject()获取真实的对象。

consumer生成核心在createProxy,作用是生成代理。本质是由DubboProtocol生成DubboInvoker,DubboInvoker封装了引用的接口和nettyClient。然后把生成的Invoker缓存到注册表RegistryDirectory.methodInvokerMap。这样在consumer请求provider时候,就可以根据methodName从缓存中获取Invoker。生成Invoker后,订阅providers、configurators、routers节点,这些节点有变动,则会通过zk通知到consumer端进行Invoker重新生成。

consumer启动的核心在RegistryDirectory。

启动流程图如下

consumer 启动记录xmind地址https://gitee.com/yulewo123/mdpicture/blob/master/document/dubbo/dubbo consumer启动记录.xmind

四、dubbo consumer generic启动流程

dubbo泛化调用通常用于网关,例子如下

ReferenceConfigCache referenceCache = ReferenceConfigCache.getCache();

ReferenceConfig<GenericService> reference = new ReferenceConfig<GenericService>();//缓存,否则每次请求都会创建一个ReferenceConfig,并在zk注册节点,最终可能导致zk节点过多影响性能
ApplicationConfig application = new ApplicationConfig();
application.setName("pangu-client-consumer-generic");
// 连接注册中心配置
RegistryConfig registry = new RegistryConfig();
registry.setAddress("zookeeper://127.0.0.1:2181"); // 服务消费者缺省值配置
ConsumerConfig consumer = new ConsumerConfig();
consumer.setTimeout(10000);
consumer.setRetries(0); reference.setApplication(application);
reference.setRegistry(registry);
reference.setConsumer(consumer);
reference.setInterface(org.pangu.api.ProductService.class); // 弱类型接口名
// reference.setVersion("");
// reference.setGroup("");
reference.setGeneric(true); // 声明为泛化接口
GenericService svc = referenceCache.get(reference); Object target = svc.$invoke("findProduct", new String[]{ProductDTO.class.getName()}, new Object[]{dto});//实际网关中,方法名、参数类型、参数是作为参数传入

consumer泛化,无法以注解方式启动,通常都是使用java api方式启动,通常都是在请求时候进行创建并缓存。

consumer是泛化,启动流程和普通consumer基本相同,不同之处有两点:

  1. 除了在zk注册节点path是com.alibaba.dubbo.rpc.service.GenericService且有generic,其它两者基本相同。也会订阅对应接口的providers、configurators、routers节点。
  2. 会在filter chain增加GenericImplFilter,用于组装泛化请求

备注:

普通consumer在zk注册节点例子:consumer://192.168.5.1/org.pangu.api.ProductService?application=pangu-client-consumer&category=consumers&check=false&dubbo=2.0.2&interface=org.pangu.api.ProductService&methods=findProduct,selectProduct&pid=19276&qos.enable=true&retries=0&revision=1.0-SNAPSHOT&side=consumer&timeout=900000000&timestamp=1625758276910

泛化consumer在zk注册节点例子:consumer://192.168.5.1/com.alibaba.dubbo.rpc.service.GenericService?application=pangu-client-consumer-generic&category=consumers&check=false&default.retries=0&default.timeout=60000&dubbo=2.0.2&generic=true&interface=org.pangu.api.ProductService&pid=19276&side=consumer&timestamp=1625758288017

五、Invoker是什么

通过分析dubbo启动,发现provider和consumer都是转成Invoker。

provider端首先通过javaassit对目标接口进行动态代理,生成代理对象Invoker,然后对此Invoker进行Decorator增强(封装目标服务和nettyserver),接着再继续Decorator增强(生成filter chain),得到最终增强后的Invoker对象,并缓存到DubboProtocol.exporterMap。

consumer端是直接创建DubboInvoker,该Invoker封装了目标服务类型clazz和nettyClient,接着也对此Invoker进行Decorator增强(生成filter chain),得到最终增强后的Invoker对象,并缓存到RegistryDirectory.methodInvokerMap。

使用Invoker,统一不同的接口类型,便于管理以及重要的是可以对一个类型设置拦截,用于功能增强。这也是梁飞大神提到的在重要的过程上设置拦截接口

dubbo系列一、dubbo启动流程的更多相关文章

  1. 源码分析Dubbo服务消费端启动流程

    通过前面文章详解,我们知道Dubbo服务消费者标签dubbo:reference最终会在Spring容器中创建一个对应的ReferenceBean实例,而ReferenceBean实现了Spring生 ...

  2. dubbo源码学习(一)dubbo容器启动流程简略分析

    最近在学习dubbo,dubbo的使用感觉非常的简单,方便,基于Spring的容器加载配置文件就能直接搭建起dubbo,之前学习中没有养成记笔记的习惯,时间一久就容易忘记,后期的复习又需要话费较长的时 ...

  3. Dubbo 系列(05-1)服务发布

    目录 Dubbo 系列(05-1)服务发布 Spring Cloud Alibaba 系列目录 - Dubbo 篇 1. 背景介绍 1.1 服务暴露整体机制 2. 源码分析 2.1 前置工作 2.2 ...

  4. Dubbo 系列(07-3)集群容错 - 负载均衡

    目录 Dubbo 系列(07-3)集群容错 - 负载均衡 Spring Cloud Alibaba 系列目录 - Dubbo 篇 1. 背景介绍 1.1 负载均衡算法 1.2 继承体系 2. 源码分析 ...

  5. Dubbo 系列(07-2)集群容错 - 服务路由

    目录 Dubbo 系列(07-2)集群容错 - 服务路由 1. 背景介绍 1.1 继承体系 1.2 SPI 2. 源码分析 2.1 创建路由规则 2.2 RouteChain 2.3 条件路由 Dub ...

  6. Spring Cloud 系列之 Dubbo RPC 通信

    Dubbo 介绍 官网:http://dubbo.apache.org/zh-cn/ Github:https://github.com/apache/dubbo 2018 年 2 月 15 日,阿里 ...

  7. dubbo系列十一、dubbo transport层记录

    前言 在dubbo接口方法重载且入参未显式指定序列化id导致ClassCastException分析时候用到了dubbo的通信层和编解码,dubbo有个transport层,默认使用netty4进行网 ...

  8. 分布式学习系列【dubbo入门实践】

    分布式学习系列[dubbo入门实践] dubbo架构 组成部分:provider,consumer,registry,monitor: provider,consumer注册,订阅类似于消息队列的注册 ...

  9. 深度学习Dubbo系列(入门开篇)

    此文档为系列学习文档 这系列文档详细讲解了dubbo的使用,基本涵盖dubbo的所有功能特性.在接下来的文章里会详细介绍. 如果你正依赖dubbo作为你业务工程的RPC通信框架,这里可以作为你的参考手 ...

随机推荐

  1. 在react项目中实现表格导出为Excel

    需求背景 数据表格有时需要增加导出Excel功能,大多数情况下都是后端出下载接口,前端去调用. 对于数据量少的数据,可以通过前端技术实现,减少后端工作. 实现方式 使用插件--xlsx 根据自己项目情 ...

  2. iframe父子页面js之间的调用

    父级页面:mian.html 子页面1:top.html 子页面2:index.html 页面关系 <div onclick="_top()">调用contentTop ...

  3. JAVA实现QQ第三方登录

    首先在QQ互联: https://connect.qq.com/manage.html 申请账号,并且进行资料审核,同时创建应用(设置回调地址) 申请应用完后,会有app_ID.app_KEY等参数 ...

  4. vue常用技巧-动态btn的封装

    @1.要求: 1.点击某个按钮后激活active样式,其余按钮则为normal样式 2.要满足任意个数btn(btn个数不确定) @2.思路: 1.首先,btn个数不确定则意味着必须使用v-for循环 ...

  5. UDP&串口调试助手用法(3)

    发送参数配置 下面以 UDP 通道为例介绍 发送数据配置 概览 选择数据源 文件: 选择发送的文件 源码: 自己手动键入发送数据,默认输入的为16进制数据, 定时器发送周期 单位为毫秒. 发送帧计数 ...

  6. 快速登陆linux服务器

    前言 本文适用于喜欢原生终端的用户,钟爱第三方ssh客户端的可以无视....客户端可以保存用户信息和密码,比较无脑.mac可以使用终端,win可以使用git的bash. 上次分享了配置非对称秘钥免密登 ...

  7. C++输出控制小数点后位数的方法

    以C++输出小数点两位数为例 setprecision(n)使用方法总结 首先要记住写头文件 #include <iomanip> //不要忘了头文件 以下是控制小数位数的三种写法 //t ...

  8. 人脸识别中的重要环节-对齐之3D变换-Java版(文末附开源地址)

    一.人脸对齐基本概念 人脸对齐通过人脸关键点检测得到人脸的关键点坐标,然后根据人脸的关键点坐标调整人脸的角度,使人脸对齐,由于输入图像的尺寸是大小不一的,人脸区域大小也不相同,角度不一样,所以要通过坐 ...

  9. 适用于 Flutter 的 Google 移动广告 SDK 正式版现已发布

    作者 / Zoey Fan,Flutter 产品经理 应用变现有多种方法: 通过实体企业的店面接受付款.提供订阅或应用内购买,或者直接在应用中投放广告.经过六个月的 beta 测试期,我们很高兴能够推 ...

  10. layedit赋值方法(layedit.setContent赋值时报错-ReferenceError: layedit is not defined)

    layedit.setContent赋值时报错 - ReferenceError: layedit is not defined 报错代码: 控制台报错信息 解决方法: 步骤: 1)先赋值: 2)在渲 ...