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方法,填充默认属性,其具体加载顺序:
-
从系统属性加载对应参数值,参数键:dubbo.provider.属性名,System.getProperty。
-
加载属性配置文件的值。属性配置文件,可通过系统属性: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®istry=zookeeper×tamp=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);
}
}
Step4:根据是否开启令牌机制,如果开启,设置token键,值为静态值或uuid。
ServiceConfig#doExportUrlsFor1Protocol
if (Constants.LOCAL_PROTOCOL.equals(protocolConfig.getName())) {
protocolConfig.setRegister(false);
map.put(“notify”, “false”);
}
Step5:如果协议为本地协议(injvm),则设置protocolConfig#register属性为false,表示不向注册中心注册服务,在map中存储键为notify,值为false,表示当注册中心监听到服务提供者发送变化(服务提供者增加、服务提供者减少等事件时不通知。
ServiceConfig#doExportUrlsFor1Protocol
// export service
String contextPath = protocolConfig.getContextpath();
if ((contextPath == null || contextPath.length() == 0) && provider != null) {
contextPath = provider.getContextpath();
}
Step6:设置协议的contextPath,如果未配置,默认为/interfacename
ServiceConfig#doExportUrlsFor1Protocol
String host = this.findConfigedHosts(protocolConfig, registryURLs, map);
Integer port = this.findConfigedPorts(protocolConfig, name, map);
Step7:解析服务提供者的IP地址与端口。
服务IP地址解析顺序:(序号越小越优先)
-
系统环境变量,变量名:DUBBO_DUBBO_IP_TO_BIND
-
系统属性,变量名:DUBBO_DUBBO_IP_TO_BIND
-
系统环境变量,变量名:DUBBO_IP_TO_BIND
-
系统属性,变量名:DUBBO_IP_TO_BIND
-
dubbo:protocol 标签的host属性 --》 dubbo:provider 标签的host属性
-
默认网卡IP地址,通过InetAddress.getLocalHost().getHostAddress()获取,如果IP地址不符合要求,继续下一个匹配。
判断IP地址是否符合要求的标准是:
public static boolean isInvalidLocalHost(String host) {
return host == null
|| host.length() == 0
|| host.equalsIgnoreCase(“localhost”)
|| host.equals(“0.0.0.0”)
|| (LOCAL_IP_PATTERN.matcher(host).matches());
}
复习的面试资料
这些面试全部出自大厂面试真题和面试合集当中,小编已经为大家整理完毕(PDF版)
- 第一部分:Java基础-中级-高级
- 第二部分:开源框架(SSM:Spring+SpringMVC+MyBatis)
- 第三部分:性能调优(JVM+MySQL+Tomcat)
- 第四部分:分布式(限流:ZK+Nginx;缓存:Redis+MongoDB+Memcached;通讯:MQ+kafka)
- 第五部分:微服务(SpringBoot+SpringCloud+Dubbo)
- 第六部分:其他:并发编程+设计模式+数据结构与算法+网络
进阶学习笔记pdf
- Java架构进阶之架构筑基篇(Java基础+并发编程+JVM+MySQL+Tomcat+网络+数据结构与算法)
- Java架构进阶之开源框架篇(设计模式+Spring+SpringMVC+MyBatis)
- Java架构进阶之分布式架构篇 (限流(ZK/Nginx)+缓存(Redis/MongoDB/Memcached)+通讯(MQ/kafka))
- Java架构进阶之微服务架构篇(RPC+SpringBoot+SpringCloud+Dubbo+K8s)
26)]
- 第六部分:其他:并发编程+设计模式+数据结构与算法+网络
[外链图片转存中…(img-dnsIYIie-1714751429726)]
进阶学习笔记pdf
- Java架构进阶之架构筑基篇(Java基础+并发编程+JVM+MySQL+Tomcat+网络+数据结构与算法)
[外链图片转存中…(img-vkjz5Dv1-1714751429727)]
- Java架构进阶之开源框架篇(设计模式+Spring+SpringMVC+MyBatis)
[外链图片转存中…(img-PLQ639gL-1714751429727)]
[外链图片转存中…(img-i0IeWD9p-1714751429727)]
[外链图片转存中…(img-jrNpPvte-1714751429728)]
- Java架构进阶之分布式架构篇 (限流(ZK/Nginx)+缓存(Redis/MongoDB/Memcached)+通讯(MQ/kafka))
[外链图片转存中…(img-sJliMNE3-1714751429728)]
[外链图片转存中…(img-D3jhEw74-1714751429728)]
[外链图片转存中…(img-FAr8nijQ-1714751429729)]
- Java架构进阶之微服务架构篇(RPC+SpringBoot+SpringCloud+Dubbo+K8s)
[外链图片转存中…(img-5P2k2VCN-1714751429729)]
[外链图片转存中…(img-olAPttzG-1714751429729)]