Dubbo系列笔记之服务引用过程,不服不行

if (shouldInit()) {

getObject();

}

}

  • FactoryBean

Spring 通过调用 #getBean 方法可以返回 bean 的实例,而实现了 FactoryBean 接口,会调用 #getObject 方法返回 bean 的实例。

@Override

public Object getObject() {

// 调用 ReferenceConfig 的 get 方法获取 bean 实例

return get();

}

比较上面两个方法,我们可以知道 Spring 在实例化 Dubbo 的 ReferenceBean 时会调用 ReferenceConfig 的 #get 方法获取 bean 实例,执行 Dubbo 服务引用的过程。

三、配置检查处理

============================================================================

继续跟随 ReferenceConfig 的 #get 方法:

public synchronized T get() {

if (destroyed) {

throw new IllegalStateException(“The invoker of ReferenceConfig(” + url + “) has already destroyed!”);

} // 若服务引用代理为空,执行 init 方法

if (ref == null) {

// 处理配置,调用 createProxy 生成代理类

init();

}

return ref;

}

  • ReferenceConfig 的 init( ) 方法

public synchronized void init() {

// 标识是否已经初始化,避免重复初始化

if (initialized) {

return;

} // 获取 DubboBootstrap 引导类实例 if (bootstrap == null) {

bootstrap = DubboBootstrap.getInstance(); bootstrap.init(); } // 检查接口、consumer 等配置是否合法,并对相应的配置赋值 checkAndUpdateSubConfigs(); // 本地存根检查

checkStubAndLocal(interfaceClass); ConfigValidationUtils.checkMock(interfaceClass, this); Map<String, String> map = new HashMap<String, String>();

map.put(SIDE_KEY, CONSUMER_SIDE); // 加入运行时参数,Dubbo 版本号、时间戳、进程号等

ReferenceConfigBase.appendRuntimeParameters(map);

// 是否为泛化接口

if (!ProtocolUtils.isGeneric(generic)) {

String revision = Version.getVersion(interfaceClass, version); if (revision != null && revision.length() > 0) {

map.put(REVISION_KEY, revision); } // 获取接口的方法列表,加入 map

String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames(); if (methods.length == 0) {

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

map.put(METHODS_KEY, ANY_VALUE); } else {

map.put(METHODS_KEY, StringUtils.join(new HashSet(Arrays.asList(methods)), COMMA_SEPARATOR)); } } map.put(INTERFACE_KEY, interfaceName); // 将 ApplicationConfig、ConsumerConfig、ReferenceConfig 等对象的字段信息添加到 map 中

AbstractConfig.appendParameters(map, getMetrics());

AbstractConfig.appendParameters(map, getApplication());

AbstractConfig.appendParameters(map, getModule());

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 的创建过程。Invoker 作为 Dubbo 的通用模型,代表着一个可执行体。在服务提供者来看,Invoker 用于调用真实的服务实现类;而在服务消费者来看,Invoker 用于执行远程调用,在上面创建代理的方法中,我们注意到创建 Invoker 的一个关键方法 Protocol#refer(Class type, URL url) 。

Protocol 的实现有很多,我们还是以常见的 DubboProtocol 和 RegistryProtocol 来分析 refer 方法如何构建 Invoker 。

1. DubboProtocol

=====================================================================================

DubboProtocol 继承了 AbstractProtocol 抽象类,从其 refer 方法入手:

@Override

public Invoker refer(Class type, URL url) throws RpcException {

return new AsyncToSyncInvoker<>(protocolBindingRefer(type, url));

}

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

深知大多数Java工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Java开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Java开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Java)

最后

这份清华大牛整理的进大厂必备的redis视频、面试题和技术文档

祝大家早日进入大厂,拿到满意的薪资和职级~~~加油!!

感谢大家的支持!!

image.png

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!**

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Java)

[外链图片转存中…(img-v4gU1sen-1713827122366)]

最后

这份清华大牛整理的进大厂必备的redis视频、面试题和技术文档

祝大家早日进入大厂,拿到满意的薪资和职级~~~加油!!

感谢大家的支持!!

[外链图片转存中…(img-o1yWwfiI-1713827122366)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 27
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Dubbo是一个分布式服务框架,它可以帮助我们快速开发分布式系统。Dubbo服务的暴露和引用过程分为以下几个步骤: 1. 服务提供者暴露服务服务提供者需要在Dubbo中注册服务,并将服务暴露出来。Dubbo提供了多种方式来注册和暴露服务,比如XML配置方式、注解方式等。服务提供者可以选择合适的方式来注册和暴露服务。 2. 服务消费者引用服务服务消费者需要在Dubbo引用服务Dubbo提供了多种方式来引用服务,比如XML配置方式、注解方式等。服务消费者可以选择合适的方式来引用服务。 3. 服务调用:服务消费者可以通过调用Dubbo提供的API来调用服务Dubbo支持多种调用方式,比如同步调用、异步调用、单向调用等。服务调用的过程中,Dubbo会自动进行负载均衡、容错处理等操作,从而提高系统的可用性和稳定性。 4. 服务监控和管理:Dubbo提供了丰富的监控和管理功能,可以帮助我们了解系统的运行情况和性能瓶颈。比如可以通过Dubbo的监控中心来查看服务的调用次数、调用时间、响应时间等指标,从而帮助我们优化系统的性能。 总的来说,Dubbo服务的暴露和引用过程包括服务提供者的服务暴露、服务消费者的服务引用服务的调用和服务的监控和管理等步骤。在实际应用中,需要根据具体的业务需求和场景选择合适的Dubbo配置方式和调用方式,从而实现高效、稳定的分布式服务

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值