dubbo系列一、dubbo启动流程
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×tamp=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基本相同,不同之处有两点:
- 除了在zk注册节点path是com.alibaba.dubbo.rpc.service.GenericService且有generic,其它两者基本相同。也会订阅对应接口的providers、configurators、routers节点。
- 会在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×tamp=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×tamp=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启动流程的更多相关文章
- 源码分析Dubbo服务消费端启动流程
通过前面文章详解,我们知道Dubbo服务消费者标签dubbo:reference最终会在Spring容器中创建一个对应的ReferenceBean实例,而ReferenceBean实现了Spring生 ...
- dubbo源码学习(一)dubbo容器启动流程简略分析
最近在学习dubbo,dubbo的使用感觉非常的简单,方便,基于Spring的容器加载配置文件就能直接搭建起dubbo,之前学习中没有养成记笔记的习惯,时间一久就容易忘记,后期的复习又需要话费较长的时 ...
- Dubbo 系列(05-1)服务发布
目录 Dubbo 系列(05-1)服务发布 Spring Cloud Alibaba 系列目录 - Dubbo 篇 1. 背景介绍 1.1 服务暴露整体机制 2. 源码分析 2.1 前置工作 2.2 ...
- Dubbo 系列(07-3)集群容错 - 负载均衡
目录 Dubbo 系列(07-3)集群容错 - 负载均衡 Spring Cloud Alibaba 系列目录 - Dubbo 篇 1. 背景介绍 1.1 负载均衡算法 1.2 继承体系 2. 源码分析 ...
- Dubbo 系列(07-2)集群容错 - 服务路由
目录 Dubbo 系列(07-2)集群容错 - 服务路由 1. 背景介绍 1.1 继承体系 1.2 SPI 2. 源码分析 2.1 创建路由规则 2.2 RouteChain 2.3 条件路由 Dub ...
- Spring Cloud 系列之 Dubbo RPC 通信
Dubbo 介绍 官网:http://dubbo.apache.org/zh-cn/ Github:https://github.com/apache/dubbo 2018 年 2 月 15 日,阿里 ...
- dubbo系列十一、dubbo transport层记录
前言 在dubbo接口方法重载且入参未显式指定序列化id导致ClassCastException分析时候用到了dubbo的通信层和编解码,dubbo有个transport层,默认使用netty4进行网 ...
- 分布式学习系列【dubbo入门实践】
分布式学习系列[dubbo入门实践] dubbo架构 组成部分:provider,consumer,registry,monitor: provider,consumer注册,订阅类似于消息队列的注册 ...
- 深度学习Dubbo系列(入门开篇)
此文档为系列学习文档 这系列文档详细讲解了dubbo的使用,基本涵盖dubbo的所有功能特性.在接下来的文章里会详细介绍. 如果你正依赖dubbo作为你业务工程的RPC通信框架,这里可以作为你的参考手 ...
随机推荐
- css实现hover显示下拉菜单
原理比较简单,首先先隐藏下拉菜单即display:none,当鼠标hover后,设置display:block. <style> .menu-title { postion: relati ...
- Spring核心原理之 IoC容器中那些鲜为人知的细节(3)
本文节选自<Spring 5核心原理> Spring IoC容器还有一些高级特性,如使用lazy-init属性对Bean预初始化.使用FactoryBean产生或者修饰Bean对象的生成. ...
- 【LeetCode】270. Closest Binary Search Tree Value 解题报告(C++)
作者: 负雪明烛 id: fuxuemingzhu 个人博客:http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 动态规划 日期 题目地址:https://leetco ...
- 【LeetCode】937. Reorder Log Files 解题报告(Python)
作者: 负雪明烛 id: fuxuemingzhu 个人博客: http://fuxuemingzhu.cn/ 目录 题目描述 题目大意 解题方法 分割和排序 日期 题目地址:https://leet ...
- [LeetCode] 448. Find All Numbers Disappeared in an Array 找到数组中消失的数字
题目描述 给定n个数字的数组,里面的值都是1-n,但是有的出现了两遍,因此有的没有出现,求没有出现值这个数组中的值有哪些. 要求不能用额外的空间(除了返回列表之外),时间复杂度n 思路 因为不能用额外 ...
- 1016 - Brush (II)
1016 - Brush (II) PDF (English) Statistics Forum Time Limit: 2 second(s) Memory Limit: 32 MB Afte ...
- 第十六个知识点:描述DSA,Schnorr,RSA-FDH的密钥生成,签名和验证
第十六个知识点:描述DSA,Schnorr,RSA-FDH的密钥生成,签名和验证 这是密码学52件事系列中第16篇,这周我们描述关于DSA,Schnorr和RSA-FDH的密钥生成,签名和验证. 1. ...
- Java基础(八)——IO流3_对象流
一.对象流 1.序列化与反序列化 序列化:将内存中的Java对象保存到磁盘中或通过网络传输出去. 反序列化:将磁盘文件中的对象还原为内存中的一个Java对象. 用途: (1)将对象保存到物理硬盘:比如 ...
- SpringCloud创建Gateway模块
1.说明 本文详细介绍Spring Cloud创建Gateway模块的方法, 基于已经创建好的Spring Cloud父工程, 请参考SpringCloud创建项目父工程, 和已经创建好的Eureka ...
- .net core的配置介绍(二):自定义配置(Zookeeper,数据库,Redis)
上一篇介绍了.net core的配置原理已经系统提供的一些常用的配置,但有时我们的配置是存放在Zookeeper,DB,Redis中的,这就需要我们自己去实现集成了. 这里再介绍几个我们用的多的配置集 ...