源码分析Dubbo服务提供者启动流程-上篇,2024年最新什么是JAVA程序设计

从上文中我们得知,服务提供者启动的核心入口为ServiceBean,本节将从源码级别详细剖析ServcieBean的实现原理,即Dubbo服务提供者的启动流程,ServiceBean的继承层次如图所示,dubbo:service标签的所有属性都被封装在此类图结构中。

这里写图片描述

1、源码分析ServiceBean#afterPropertiesSet

ServiceBean#afterPropertiesSet

if (getProvider() == null) { // @1

Map<String, ProviderConfig> provide

ConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext, ProviderConfig.class, false, false); // @2

// … 具体解析代码省略。

}

}

Step1:如果provider为空,说明dubbo:service标签未设置provider属性,如果一个dubbo:provider标签,则取该实例,如果存在多个dubbo:provider配置则provider属性不能为空,否则抛出异常:“Duplicate provider configs”。

ServiceBean#afterPropertiesSet

if (getApplication() == null

&& (getProvider() == null || getProvider().getApplication() == null)) {

Map<String, ApplicationConfig> applicationConfigMap = applicationContext == null ? null : BeanFactoryUtils.beansOfTypeIncludingAncestors(applicationContext,

ApplicationConfig.class, false, false);

// …省略

}

Step2:如果application为空,则尝试从BeanFactory中查询dubbo:application实例,如果存在多个dubbo:application配置,则抛出异常:“Duplicate application configs”。

Step3:如果ServiceBean的module为空,则尝试从BeanFactory中查询dubbo:module实例,如果存在多个dubbo:module,则抛出异常:"Duplicate module configs: "。

Step4:尝试从BeanFactory中加载所有的注册中心,注意ServiceBean的List< RegistryConfig> registries属性,为注册中心集合。

Step5:尝试从BeanFacotry中加载一个监控中心,填充ServiceBean的MonitorConfig monitor属性,如果存在多个dubbo:monitor配置,则抛出"Duplicate monitor configs: "。

Step6:尝试从BeanFactory中加载所有的协议,注意:ServiceBean的List< ProtocolConfig> protocols是一个集合,也即一个服务可以通过多种协议暴露给消费者。

ServiceBean#afterPropertiesSet

if (getPath() == null || getPath().length() == 0) {

if (beanName != null && beanName.length() > 0 && getInterface() != null && getInterface().length() > 0 && beanName.startsWith(getInterface())) {

setPath(beanName);

}

}

Step7:设置ServiceBean的path属性,path属性存放的是dubbo:service的beanName(dubbo:service id)。

ServiceBean#afterPropertiesSet

if (!isDelay()) {

export();

}

Step8:如果为启用延迟暴露机制,则调用export暴露服务。首先看一下isDelay的实现,然后重点分析export的实现原理(服务暴露的整个实现原理)。

ServiceBean#isDelay

private boolean isDelay() {

Integer delay = getDelay();

ProviderConfig provider = getProvider();

if (delay == null && provider != null) {

delay = provider.getDelay();

}

return supportedApplicationListener && (delay == null || delay == -1);

}

如果有设置dubbo:service或dubbo:provider的属性delay,或配置delay为-1,都表示启用延迟机制,单位为毫秒,设置为-1,表示等到Spring容器初始化后再暴露服务。从这里也可以看出,Dubbo暴露服务的处理入口为ServiceBean#export—》ServiceConfig#export。

1.1 源码分析ServiceConfig#export 暴露服务

调用链:ServiceBean#afterPropertiesSet------>ServiceConfig#export

public synchronized void export() {

if (provider != null) {

if (export == null) {

export = provider.getExport();

}

if (delay == null) {

delay = provider.getDelay();

}

}

if (export != null && !export) { // @1

return;

}

if (delay != null && delay > 0) { // @2

delayExportExecutor.schedule(new Runnable() {

@Override

public void run() {

doExport();

}

}, delay, TimeUnit.MILLISECONDS);

} else {

doExport(); //@3

}

}

代码@1:判断是否暴露服务,由dubbo:service export="true|false"来指定。

代码@2:如果启用了delay机制,如果delay大于0,表示延迟多少毫秒后暴露服务,使用ScheduledExecutorService延迟调度,最终调用doExport方法。

代码@3:执行具体的暴露逻辑doExport,需要大家留意:delay=-1的处理逻辑(基于Spring事件机制触发)。

1.2 源码分析ServiceConfig#doExport暴露服务

调用链:ServiceBean#afterPropertiesSet—调用------>ServiceConfig#export------>ServiceConfig#doExport

ServiceConfig#checkDefault

private void checkDefault() {

if (provider == null) {

provider = new ProviderConfig();

}

appendProperties(provider);

}

Step1:如果dubbo:servce标签也就是ServiceBean的provider属性为空,调用appendProperties方法,填充默认属性,其具体加载顺序:

  1. 从系统属性加载对应参数值,参数键:dubbo.provider.属性名,System.getProperty。

  2. 加载属性配置文件的值。属性配置文件,可通过系统属性:dubbo.properties.file,如果该值未配置,则默认取dubbo.properties属性配置文件。

ServiceConfig#doExport

if (ref instanceof GenericService) {

interfaceClass = GenericService.class;

if (StringUtils.isEmpty(generic)) {

generic = Boolean.TRUE.toString();

}

} else {

try {

interfaceClass = Class.forName(interfaceName, true, Thread.currentThread()

.getContextClassLoader());

} catch (ClassNotFoundException e) {

throw new IllegalStateException(e.getMessage(), e);

}

checkInterfaceAndMethods(interfaceClass, methods);

checkRef();

generic = Boolean.FALSE.toString();

}

Step2:校验ref与interface属性。如果ref是GenericService,则为dubbo的泛化实现,然后验证interface接口与ref引用的类型是否一致。

ServiceConfig#doExport

if (local != null) {

if (“true”.equals(local)) {

local = interfaceName + “Local”;

}

Class<?> localClass;

try {

localClass = ClassHelper.forNameWithThreadContextClassLoader(local);

} catch (ClassNotFoundException e) {

throw new IllegalStateException(e.getMessage(), e);

}

if (!interfaceClass.isAssignableFrom(localClass)) {

throw new IllegalStateException("The local implementation class " + localClass.getName() + " not implement interface " + interfaceName);

}

}

Step3:dubbo:service local机制,已经废弃,被stub属性所替换。

Step4:处理本地存根Stub,<dubbo:service 的stub属性,可以设置为true,此时Stub的类名为:interface+Stub,stub也可以指定自定义的全类名。本地存根说明如图所示(Dubbo官方文档)

这里写图片描述

ServiceConfig#doExport

checkApplication();

checkRegistry();

checkProtocol();

appendProperties(this);

Step5:校验ServiceBean的application、registry、protocol是否为空,并从系统属性(优先)、资源文件中填充其属性。

系统属性、资源文件属性的配置如下:

application dubbo.application.属性名,例如 dubbo.application.name

registry dubbo.registry.属性名,例如 dubbo.registry.address

protocol dubbo.protocol.属性名,例如 dubbo.protocol.port

service dubbo.service.属性名,例如 dubbo.service.stub

ServiceConfig#doExport

checkStubAndMock(interfaceClass);

Step6:校验stub、mock类的合理性,是否是interface的实现类。

ServiceConfig#doExport

doExportUrls();

Step7:执行doExportUrls()方法暴露服务,接下来会重点分析该方法。

ServiceConfig#doExport

ProviderModel providerModel = new ProviderModel(getUniqueServiceName(), this, ref);

ApplicationModel.initProviderModel(getUniqueServiceName(), providerModel);

Step8:将服务提供者信息注册到ApplicationModel实例中。

1.3 源码分析ServiceConfig#doExportUrls暴露服务具体实现逻辑

调用链:ServiceBean#afterPropertiesSet------>ServiceConfig#export------>ServiceConfig#doExport

private void doExportUrls() {

List registryURLs = loadRegistries(true); // @1

for (ProtocolConfig protocolConfig : protocols) {

doExportUrlsFor1Protocol(protocolConfig, registryURLs); // @2

}

}

代码@1:首先遍历ServiceBean的List< RegistryConfig> registries(所有注册中心的配置信息),然后将地址封装成URL对象,关于注册中心的所有配置属性,最终转换成url的属性(?属性名=属性值),loadRegistries(true),参数的意思:true,代表服务提供者,false:代表服务消费者,如果是服务提供者,则检测注册中心的配置,如果配置了register=“false”,则忽略该地址,如果是服务消费者,并配置了subscribe="false"则表示不从该注册中心订阅服务,故也不返回,一个注册中心URL示例:

registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.0&pid=7072&qos.port=22222&registry=zookeeper&timestamp=1527308268041

代码@2:然后遍历配置的所有协议,根据每个协议,向注册中心暴露服务,接下来重点分析doExportUrlsFor1Protocol方法的实现细节。

1.4 源码分析doExportUrlsFor1Protocol

调用链:ServiceBean#afterPropertiesSet------>ServiceConfig#export------>ServiceConfig#doExport------>ServiceConfig#doExportUrlsFor1Protocol

ServiceConfig#doExportUrlsFor1Protocol

String name = protocolConfig.getName();

if (name == null || name.length() == 0) {

name = “dubbo”;

}

Map<String, String> map = new HashMap<String, String>();

map.put(Constants.SIDE_KEY, Constants.PROVIDER_SIDE);

map.put(Constants.DUBBO_VERSION_KEY, Version.getVersion());

map.put(Constants.TIMESTAMP_KEY, String.valueOf(System.currentTimeMillis()));

if (ConfigUtils.getPid() > 0) {

map.put(Constants.PID_KEY, String.valueOf(ConfigUtils.getPid()));

}

appendParameters(map, application);

appendParameters(map, module);

appendParameters(map, provider, Constants.DEFAULT_KEY);

appendParameters(map, protocolConfig);

appendParameters(map, this);

Step1:用Map存储该协议的所有配置参数,包括协议名称、dubbo版本、当前系统时间戳、进程ID、application配置、module配置、默认服务提供者参数(ProviderConfig)、协议配置、服务提供Dubbo:service的属性。

ServiceConfig#doExportUrlsFor1Protocol

if (methods != null && !methods.isEmpty()) {

for (MethodConfig method : methods) {

appendParameters(map, method, method.getName());

String retryKey = method.getName() + “.retry”;

if (map.containsKey(retryKey)) {

String retryValue = map.remove(retryKey);

if (“false”.equals(retryValue)) {

map.put(method.getName() + “.retries”, “0”);

}

}

List arguments = method.getArguments();

if (arguments != null && !arguments.isEmpty()) {

for (ArgumentConfig argument : arguments) {

// convert argument type

if (argument.getType() != null && argument.getType().length() > 0) {

Method[] methods = interfaceClass.getMethods();

// visit all methods

if (methods != null && methods.length > 0) {

for (int i = 0; i < methods.length; i++) {

String methodName = methods[i].getName();

// target the method, and get its signature

if (methodName.equals(method.getName())) {

Class<?>[] argtypes = methods[i].getParameterTypes();

// one callback in the method

if (argument.getIndex() != -1) {

if (argtypes[argument.getIndex()].getName().equals(argument.getType())) {

appendParameters(map, argument, method.getName() + “.” + argument.getIndex());

} else {

throw new IllegalArgumentException(“argument config error : the index attribute and type attribute not match :index :” + argument.getIndex() + “, type:” +

argument.getType());

}

} else {

// multiple callbacks in the method

for (int j = 0; j < argtypes.length; j++) {

Class<?> argclazz = argtypes[j];

if (argclazz.getName().equals(argument.getType())) {

appendParameters(map, argument, method.getName() + “.” + j);

if (argument.getIndex() != -1 && argument.getIndex() != j) {

throw new IllegalArgumentException(“argument config error : the index attribute and type attribute not match :index :” + argument.getIndex() + ",

type:" + argument.getType());

}

}

}

}

}

}

}

} else if (argument.getIndex() != -1) {

appendParameters(map, argument, method.getName() + “.” + argument.getIndex());

} else {

throw new IllegalArgumentException(“argument config must set index or type attribute.eg: <dubbo:argument index=‘0’ …/> or <dubbo:argument type=xxx …/>”);

}

}

}

} // end of methods for

}

Step2:如果dubbo:service有dubbo:method子标签,则dubbo:method以及其子标签的配置属性,都存入到Map中,属性名称加上对应的方法名作为前缀。dubbo:method的子标签dubbo:argument,其键为方法名.参数序号。

ServiceConfig#doExportUrlsFor1Protocol

if (ProtocolUtils.isGeneric(generic)) {

map.put(Constants.GENERIC_KEY, generic);

map.put(Constants.METHODS_KEY, Constants.ANY_VALUE);

} else {

String revision = Version.getVersion(interfaceClass, version);

if (revision != null && revision.length() > 0) {

map.put(“revision”, revision);

}

String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames();

if (methods.length == 0) {

logger.warn("NO method found in service interface " + interfaceClass.getName());

map.put(Constants.METHODS_KEY, Constants.ANY_VALUE);

} else {

map.put(Constants.METHODS_KEY, StringUtils.join(new HashSet(Arrays.asList(methods)), “,”));

}

}

Step3:添加methods键值对,存放dubbo:service的所有方法名,多个方法名用,隔开,如果是泛化实现,填充genric=true,methods为"*";

ServiceConfig#doExportUrlsFor1Protocol

if (!ConfigUtils.isEmpty(token)) {

if (ConfigUtils.isDefault(token)) {

map.put(Constants.TOKEN_KEY, UUID.randomUUID().toString());

} else {

map.put(Constants.TOKEN_KEY, token);

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数同学面临毕业设计项目选题时,很多人都会感到无从下手,尤其是对于计算机专业的学生来说,选择一个合适的题目尤为重要。因为毕业设计不仅是我们在大学四年学习的一个总结,更是展示自己能力的重要机会。

因此收集整理了一份《2024年计算机毕业设计项目大全》,初衷也很简单,就是希望能够帮助提高效率,同时减轻大家的负担。
img
img
img

既有Java、Web、PHP、也有C、小程序、Python等项目供你选择,真正体系化!

由于项目比较多,这里只是将部分目录截图出来,每个节点里面都包含素材文档、项目源码、讲解视频

如果你觉得这些内容对你有帮助,可以添加VX:vip1024c (备注项目大全获取)
img

nfigUtils.isEmpty(token)) {

if (ConfigUtils.isDefault(token)) {

map.put(Constants.TOKEN_KEY, UUID.randomUUID().toString());

} else {

map.put(Constants.TOKEN_KEY, token);

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数同学面临毕业设计项目选题时,很多人都会感到无从下手,尤其是对于计算机专业的学生来说,选择一个合适的题目尤为重要。因为毕业设计不仅是我们在大学四年学习的一个总结,更是展示自己能力的重要机会。

因此收集整理了一份《2024年计算机毕业设计项目大全》,初衷也很简单,就是希望能够帮助提高效率,同时减轻大家的负担。
[外链图片转存中…(img-R7Fy4SPA-1712584969940)]
[外链图片转存中…(img-h5th23dz-1712584969941)]
[外链图片转存中…(img-YLKZMnOP-1712584969941)]

既有Java、Web、PHP、也有C、小程序、Python等项目供你选择,真正体系化!

由于项目比较多,这里只是将部分目录截图出来,每个节点里面都包含素材文档、项目源码、讲解视频

如果你觉得这些内容对你有帮助,可以添加VX:vip1024c (备注项目大全获取)
[外链图片转存中…(img-7eAqalSo-1712584969941)]

  • 13
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值