AbstractConfig.appendParameters(map, consumer);
AbstractConfig.appendParameters(map, this);
Map<String, AsyncMethodInfo> attributes = null; if (CollectionUtils.isNotEmpty(getMethods())) {
attributes = new HashMap<>(); for (MethodConfig methodConfig : getMethods()) {
AbstractConfig.appendParameters(map, methodConfig, methodConfig.getName());
String retryKey = methodConfig.getName() + “.retry”;
if (map.containsKey(retryKey)) {
String retryValue = map.remove(retryKey); if (“false”.equals(retryValue)) {
map.put(methodConfig.getName() + “.retries”, “0”);
} } AsyncMethodInfo asyncMethodInfo = AbstractConfig.convertMethodConfig2AsyncInfo(methodConfig); if (asyncMethodInfo != null) {
attributes.put(methodConfig.getName(), asyncMethodInfo); } } } // 从系统变量中获取服务消费者 ip String hostToRegistry = ConfigUtils.getSystemProperty(DUBBO_IP_TO_REGISTRY); if (StringUtils.isEmpty(hostToRegistry)) {
hostToRegistry = NetUtils.getLocalHost(); } else if (isInvalidLocalHost(hostToRegistry)) {
throw new IllegalArgumentException(“Specified invalid registry ip from property:” + DUBBO_IP_TO_REGISTRY + “, value:” + hostToRegistry);
} map.put(REGISTER_IP_KEY, hostToRegistry); // 存储配置数据
serviceMetadata.getAttachments().putAll(map);
// 创建代理
ref = createProxy(map);
serviceMetadata.setTarget(ref);
serviceMetadata.addAttribute(PROXY_CLASS_REF, ref);
ConsumerModel consumerModel = repository.lookupReferredService(serviceMetadata.getServiceKey()); consumerModel.setProxyObject(ref);
consumerModel.init(attributes); initialized = true; // 发布 ReferenceConfigInitializedEvent 事件
dispatch(new ReferenceConfigInitializedEvent(this, invoker)); }
代码较长,主要是各种配置的检查和初始化,并收集这些信息加入 map 存储,以及创建代理。
==========================================================================
接着上面我们继续看 #createProxy 方法,其不仅执行创建代理的逻辑,同时还会调用其他方法创建、合并 Invoker 实例。
private T createProxy(Map<String, String> map) {
// 判断是否本地暴露,包含指定服务 url 直连的情况判断、或根据参数配置是否进行本地暴露,如协议、scope、injvm 等
if (shouldJvmRefer(map)) { // 本地引用
// 创建 URL,协议为 njvm
URL url = new URL(LOCAL_PROTOCOL, LOCALHOST_VALUE, 0, interfaceClass.getName()).addParameters(map);
// 调用 refer 方法创建 InjvmInvoker 实例
invoker = REF_PROTOCOL.refer(interfaceClass, url); if (logger.isInfoEnabled()) {
logger.info("Using injvm service " + interfaceClass.getName());
} // 远程引用
} else {
urls.clear(); // 若 url 不为空
if (url != null && url.length() > 0) {
// 配置多个 url 时,用分号分隔
String[] us = SEMICOLON_SPLIT_PATTERN.split(url); if (us != null && us.length > 0) {
for (String u : us) {
URL url = URL.valueOf(u); if (StringUtils.isEmpty(url.getPath())) {
// 设置 url 路径为接口全限定名
url = url.setPath(interfaceName); } // 协议为 registry 时,指定注册中心
if (UrlUtils.isRegistry(url)) {
// 将 map 转换为查询字符串,赋值给 refer
urls.add(url.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map))); } else {
// 合并 url,移除服务提供者的一些配置(这些配置来源于用户配置的 url 属性),
// 比如线程池相关配置。并保留服务提供者的部分配置,比如版本,group,时间戳等
// 最后将合并后的配置设置为 url 查询字符串中。
urls.add(ClusterUtils.mergeUrl(url, map)); } } } } else { // 从注册中心的配置中组装 URL
// 协议不是 injvm
if (!LOCAL_PROTOCOL.equalsIgnoreCase(getProtocol())) {
checkRegistry(); List us = ConfigValidationUtils.loadRegistries(this, false);
if (CollectionUtils.isNotEmpty(us)) {
for (URL u : us) {
URL monitorUrl = ConfigValidationUtils.loadMonitor(this, u);
if (monitorUrl != null) {
map.put(MONITOR_KEY, URL.encode(monitorUrl.toFullString())); } // 添加 refer 参数到 url,并加入 urls 集合
urls.add(u.addParameterAndEncoded(REFER_KEY, StringUtils.toQueryString(map))); } } // 没有配置注册中心,抛出异常
if (urls.isEmpty()) {
throw new IllegalStateException(“No such any registry to reference " + interfaceName + " on the consumer " + NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion() + “, please config <dubbo:registry address=”…” /> to your spring config.");
} } } // 只有一个注册中心或者服务提供者
if (urls.size() == 1) {
// 构建 invoker 实例
invoker = REF_PROTOCOL.refer(interfaceClass, urls.get(0));
} else { // 多个注册中心或多个服务提供者,或者两者混合
List<Invoker<?>> invokers = new ArrayList
URL registryURL = null;
for (URL url : urls) {
invokers.add(REF_PROTOCOL.refer(interfaceClass, url)); if (UrlUtils.isRegistry(url)) {
// 最后一个注册中心 URL
registryURL = url; } } if (registryURL != null) {
// 如果注册中心链接不为空,则将使用 ZoneAwareCluster
String cluster = registryURL.getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME); // 创建 StaticDirectory 实例,并由 Cluster 对多个 Invoker 进行合并
invoker = Cluster.getCluster(cluster, false).join(new StaticDirectory(registryURL, invokers));
} else { // not a registry url, must be direct invoke.
-
String cluster = CollectionUtils.isNotEmpty(invokers) ? (invokers.get(0).getUrl() != null ? invokers.get(0).getUrl().getParameter(CLUSTER_KEY, ZoneAwareCluster.NAME) : Cluster.DEFAULT)
- Cluster.DEFAULT; invoker = Cluster.getCluster(cluster).join(new StaticDirectory(invokers));
} } } if (shouldCheck() && !invoker.isAvailable()) {
invoker.destroy(); throw new IllegalStateException("Failed to check the status of the service "
-
interfaceName + ". No provider available for the service "
-
(group == null ? “” : group + “/”)
-
interfaceName + (version == null ? “” : “:” + version)
-
" from the url "
-
invoker.getUrl() + " to the consumer "
-
NetUtils.getLocalHost() + " use dubbo version " + Version.getVersion());
} if (logger.isInfoEnabled()) {
logger.info("Refer dubbo service " + interfaceClass.getName() + " from url " + invoker.getUrl());
} // create service proxy
return (T) PROXY_FACTORY.getProxy(invoker, ProtocolUtils.isGeneric(generic));
}
在 #createProxy 方法中,首先检查配置是否是本地暴露,如果是,则根据自适应扩展机制获取 InjvmProtocol,并调用 #refer 方法生成 InjvmInvoker 实例,完成服务引用。相反,则读取直连 url 配置,或读取注册中心 url ,并将其存储到 urls 集合中,根据 urls 的大小进行不同的处理。如果 urls 大小为 1,则直接根据自适应扩展调用调用 #refer 方法生成 invoker 。如果 urls 大于 1,则分别根据 url 生成 invoker,然后再通过 Cluster 合并多个 invoker ,最后调用 ProxyFactory 生成代理类。
================================================================================
讲到服务暴露时,我们同样分析了 Invoker 的创建过程。Invoker 作为 Dubbo 的通用模型,代表着一个可执行体。在服务提供者来看,Invoker 用于调用真实的服务实现类;而在服务消费者来看,Invoker 用于执行远程调用,在上面创建代理的方法中,我们注意到创建 Invoker 的一个关键方法 Protocol#refer(Class type, URL url) 。
Protocol 的实现有很多,我们还是以常见的 DubboProtocol 和 RegistryProtocol 来分析 refer 方法如何构建 Invoker 。
=====================================================================================
DubboProtocol 继承了 AbstractProtocol 抽象类,从其 refer 方法入手:
@Override
public Invoker refer(Class type, URL url) throws RpcException {
return new AsyncToSyncInvoker<>(protocolBindingRefer(type, url));
}
其调用了模板方法 #protocolBindingRefer(type, url) 。
回到 DubboProtocol#protocolBindingRefer(type, url) 方法:
@Override
public Invoker protocolBindingRefer(Class serviceType, URL url) throws RpcException {
optimizeSerialization(url); // create rpc invoker.
DubboInvoker invoker = new DubboInvoker(serviceType, url, getClients(url), invokers);
invokers.add(invoker);
return invoker;
}
小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Java工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新Java开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Java)
最后
光给面试题不给答案不是我的风格。这里面的面试题也只是凤毛麟角,还有答案的话会极大的增加文章的篇幅,减少文章的可读性
Java面试宝典2021版
最常见Java面试题解析(2021最新版)
2021企业Java面试题精选
[外链图片转存中…(img-NWmWeZZr-1710397461153)]
[外链图片转存中…(img-GUrulr3s-1710397461153)]
最常见Java面试题解析(2021最新版)
[外链图片转存中…(img-9gOrJAGF-1710397461153)]
[外链图片转存中…(img-slbTPFLk-1710397461154)]
2021企业Java面试题精选
[外链图片转存中…(img-5VhLtQgZ-1710397461154)]
[外链图片转存中…(img-I2hgUfZa-1710397461155)]