Dubbo源码解析-服务导出(四)

一、服务导出

当我们在某个接口的实现类上加上@DubboService后,就表示定义了一个Dubbo服务,应用启动时Dubbo只要扫描到了@DubboService,就会解析对应的类,得到服务相关的配置信息,比如:

1. 服务的类型,也就是接口,接口名就是服务名
2. 服务的具体实现类,也就是当前类
3. 服务的version、timeout等信息,就是@DubboService中所定义的各种配置


解析完服务的配置信息后,就会把这些配置信息封装成为一个ServiceConfig对象,并调用其export()方法进行服务导出,此时一个ServiceConfig对象就表示一个Dubbo服务。而所谓的服务导出,主要就是完成三件事情:

1. 确定服务的最终参数配置
2. 按不同协议启动对应的Server(服务暴露)
3. 将服务注册到注册中心(服务注册)

二、确定服务参数

一个Dubbo服务,除开服务的名字,也就是接口名,还会有很多其他的属性,比如超时时间、版本
号、服务所属应用名、所支持的协议及绑定的端口等众多信息。但是,通常这些信息并不会全部在@DubboService中进行定义,比如,一个Dubbo服务肯定是属于某个应用的,而一个应用下可以有多个Dubbo服务,所以我们可以在应用级别定义一些通用的配置,比如协议。

我们在application.properties中定义:

表示当前应用下所有的Dubbo服务都支持通过tri协议进行访问,并且访问端口为20880,所以在进行某个服务的服务导出时,就需要将应用中的这些配置信息合并到当前服务的配置信息中。另外,除开可以通过@DubboService来配置服务,我们也可以在配置中心对服务进行配置,比如在
配置中心中配置:

dubbo.service.org.apache.dubbo.samples.api.DemoService.timeout=5000

表示当前服务的超时时间为5s。所以在服务导出时,也需要从配置中心获取当前服务的配置,如果在@DubboService中也定义了timeout,那么就用配置中心的覆盖掉,配置中心的配置优先级更高。最终确定出服务的各种参数。

三、服务暴漏

服务暴露就是根据不同的协议启动不同的Server,比如dubbo协议启动的都是Netty,我们也主要是讲解dubbo协议。

protected synchronized void doExport() {if (unexported) {throw new IllegalStateException("The service " + interfaceClass.getName() + " has already unexported!");}if (exported) {return;}if (StringUtils.isEmpty(path)) {path = interfaceName;}doExportUrls();exported();}

继续进入到doExportUrls()方法里面,for循环ProtocolConfig进行服务的暴漏

for (ProtocolConfig protocolConfig : protocols) {String pathKey = URL.buildKey(getContextPath(protocolConfig).map(p -> p + "/" + path).orElse(path), group, version);// In case user specified path, register service one more time to map it to path.repository.registerService(pathKey, interfaceClass);doExportUrlsFor1Protocol(protocolConfig, registryURLs);
}

例如暴漏的协议可以是dubbo,tri,http等,本节我们只讨论dubbo协议的暴漏,也是生产上用的最多的。下图就是当ProtocolConfig为dubbo协议时的信息

在服务暴漏的方法中有一个比较关键的入参,即url,通过buildUrl(protocolConfig, map)方法创建,其实就是提取一些配置信息得到,那么在后续exportUrl暴漏的过程中,会通过dubbo的SPI机制获取到url上面的信息,从而获取到真正的实现类完成服务的暴漏,注册等过程,这种做法在dubbo中屡见不鲜。

private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) {Map<String, String> map = buildAttributes(protocolConfig);// remove null key and null valuemap.keySet().removeIf(key -> key == null || map.get(key) == null);// init serviceMetadata attachmentsserviceMetadata.getAttachments().putAll(map);URL url = buildUrl(protocolConfig, map);//20881这里赋值exportUrl(url, registryURLs);
}

可以看下url具体长什么样:

一、本地暴漏

继续执行方法exportUrl,这里会进行exportLocal,也就是本地暴漏,这里又重新生成了一个injvm协议的url,通过这种方式的暴漏,允许在同一个 JVM 中进行直连调用,而不需要通过网络。

举个栗子:DemoService定义如下

@Service
public class DemoServiceImpl implements DemoService {@Overridepublic String sayHello(String name) {return "Hello " + name;}
}

如果同一个jvm内部服务消费者希望调用这个方法,服务消费者通过 protocol=“injvm”配置引用本地的 InJvm 服务。

@Reference(protocol = "injvm")
private DemoService demoService;

使用 InJvm 协议暴露服务在 Dubbo 中具有多种用途,包括提高性能、简化测试、方便应用内部模块间调用等。通过 InJvm 协议,可以在同一个 JVM 内快速高效地调用服务,无需网络开销,适用场景广泛且灵活。

二、远程暴漏

我们重点讲解下远程暴漏,也就是依据dubbo协议,在exportRemote方法中;最终来到最最核心的方法,这里的ref就是我们的原始service类,可以看到这里是通过动态代理的方式将其最终包装了一个Invoker,可想而知最终执行业务逻辑的还是ref,dubbo的一些强大的功能扩展就是在Invoker进行再次封装,Invoker也非常的重要。

private void doExportUrl(URL url, boolean withMetaData) {Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url);if (withMetaData) {invoker = new DelegateProviderMetaDataInvoker(invoker, this);}Exporter<?> exporter = protocolSPI.export(invoker);exporters.add(exporter);
}

这行代码中的protocolSPI通过名字可以看出来他会动态的读取invoker属性中的url获取具体的类执行export方法,所以我们要时刻查看invoker中的url属性,

Exporter<?> exporter = protocolSPI.export(invoker);

当前也即表示目前是registry协议的暴漏,意思就是要将服务在注册中心,就是会将服务的url注册到zk上。

很明显下面判断UrlUtils.isRegistry(invoker.getUrl())会等于true,最终来到RegistryProtocol类的export方法中

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {if (UrlUtils.isRegistry(invoker.getUrl())) {return protocol.export(invoker);}FilterChainBuilder builder = getFilterChainBuilder(invoker.getUrl());return protocol.export(builder.buildInvokerChain(invoker, SERVICE_FILTER_KEY, CommonConstants.PROVIDER));
}

这个方法中代码比较多,我们只看几个关键步骤

1、doLocalExport,当执行到这个方法的时候,注意到此时url上的协议已经是dubbo了,也就是说接下来会根据providerUrl上面的协议去进行服务暴露了。

2、Registry registry = getRegistry(registryUrl)获取到注册中心实现类

3、register(registry, registeredProviderUrl)最终将url写到注册中心,供服务消费者订阅使用

关于服务注册部分,下面会进行详细解释,我们继续看dubbo协议的服务暴漏,那么就会来到DubboProtocol类中的export方法,这里将invoker传递给了DubboExporter然后返回,服务暴露的返回对象就是这个exporter。

下面还有一行openServer,这里就是去启动netty,因为要供消费者进行网络调用,所以netty启动后作为服务端能够接受消费端的传过来的信息,最终这些信息会传递到exporter中的invoker方法,由invoker方法中的ref完成业务逻辑最终再通过netty发送给消费端。

public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {checkDestroyed();URL url = invoker.getUrl();// export service.String key = serviceKey(url);DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);openServer(url);optimizeSerialization(url);return exporter;
}

最终operServer会执行到下面的doOpen()方法,可以看到这里是标准的netty启动流程 

protected void doOpen() throws Throwable {bootstrap = new ServerBootstrap();bossGroup = NettyEventLoopFactory.eventLoopGroup(1, EVENT_LOOP_BOSS_POOL_NAME);workerGroup = NettyEventLoopFactory.eventLoopGroup(getUrl().getPositiveParameter(IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS),EVENT_LOOP_WORKER_POOL_NAME);final NettyServerHandler nettyServerHandler = new NettyServerHandler(getUrl(), this);channels = nettyServerHandler.getChannels();boolean keepalive = getUrl().getParameter(KEEP_ALIVE_KEY, Boolean.FALSE);bootstrap.group(bossGroup, workerGroup).channel(NettyEventLoopFactory.serverSocketChannelClass()).option(ChannelOption.SO_REUSEADDR, Boolean.TRUE).childOption(ChannelOption.TCP_NODELAY, Boolean.TRUE).childOption(ChannelOption.SO_KEEPALIVE, keepalive).childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT).childHandler(new ChannelInitializer<SocketChannel>() {@Overrideprotected void initChannel(SocketChannel ch) throws Exception {ChannelFuture channelFuture = bootstrap.bind(getBindAddress());channelFuture.syncUninterruptibly();channel = channelFuture.channel();}

至此服务暴漏过程结束。

四、服务注册(下个章节继续解析服务注册)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/16945.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

NPOI 实现Excel模板导出

记录一下使用NPOI实现定制的Excel导出模板&#xff0c;已下实现需求及主要逻辑 所需Json数据 对应参数 List<PurQuoteExportDataCrInput> listData [{"ItemName": "电缆VV3*162*10","Spec": "电缆VV3*162*10","Uom":…

CVSS4与CVSS3的不同

CVSS最近出版了新的版本CVSS4.0&#xff0c;新的版本与3.1版本有什么不同吗&#xff1f; 它会带来哪些影响&#xff1f;本片文章主要目的是介绍3.1与4.0版本之间有什么不同&#xff0c;以及会带来什么变化。 再进入比较之前&#xff0c;先简单介绍一下什么是CVSS。 CVSS&…

【返璞归真】-标准化

在第一个维度数值很大、第二个维度数值很小的情况下&#xff0c;为了让聚类和回归等统计方法更有效地处理数据&#xff0c;需要对数据进行预处理&#xff0c;主要考虑以下几方面&#xff1a; 1. 数据标准化或归一化 由于两个维度的数值量级差异很大&#xff0c;直接使用这些数…

linux文件切割

切割&#xff0c;每个文件切割3.5G split -b 3358m file.tar.gz yourfile_part_ 合并 cat yourfile_part_* > test.tar.gz 比对md5sum值&#xff0c;一致

超详细:索引介绍(易懂!)

索引是一种用于快速查询和检索数据的数据结构&#xff0c;其本质可以看成是一种排序好的数据结构。 索引的作用就相当于书的目录。打个比方: 我们在查字典的时候&#xff0c;如果没有目录&#xff0c;那我们就只能一页一页的去找我们需要查的那个字&#xff0c;速度很慢。如果…

智能化护士排班系统的设计与实现(文末附源码)

自动排班-护士(分白班|夜班) 当服务器启动时检测需要自动排班,自动开始排班的算法执行 获得本周的所有日期,例如2023-01-29.....2023-02-04依次对每个科室&#xff0c;从第一天开始,逐天进行排班&#xff0c;分别设置两个二个数组&#xff0c;day[7];night[7]分别记忆一周内每…

C语言指针初步(1)

本期重点&#xff1a;指针的性质、本质和作用 指针是C语言变量的一种&#xff0c;总的来说&#xff0c;它和int或者char之类的变量类型没有什么显著的区别&#xff0c;唯一的重点在于&#xff0c;指针实际上是地址。可以说指针就是地址&#xff0c;它是一个专门用于存放地址的变…

基于STM32的智能语音识别饮水机系统设计

功能描述 1、给饮水机设定称呼&#xff0c;喊出称呼&#xff0c;饮水机回答&#xff1a;我在 2、语音进行加热功能&#xff0c;说&#xff1a;请加热&#xff0c;加热片运行 3、饮水机水位检测&#xff0c;低于阈值播报“水量少&#xff0c;请换水” 4、检测饮水机水温&#xf…

Java项目:校园宿舍管理系统(优质版)(Springboot3+Maven+Mybatis Plus+Vue3+ Element Plus+Mysql)

项目介绍 : Springboot3MavenMybatis PlusVue3 Element PlusMysql 开发的前后端分离的校园宿舍管理系统 项目演示: https://www.bilibili.com/video/BV16UmoYWEVR/ 运行环境: 最好是java jdk 1.8&#xff0c;我们在这个平台上运行的。其他版本理论上也可以。 IDE环境&#x…

【开源项目】数字孪生仓储~经典开源项目数字孪生智慧仓储——开源工程及源码

飞渡科技数字孪生仓储管理平台&#xff0c;基于国产数字孪生引擎平台&#xff0c;整合WMS系统&#xff0c;深度融合5G、大数据、云计算、AI、融合通信等前沿技术应用&#xff0c;将数据、技术、设备与仓储管理需求有机结合&#xff0c;构建多维立体可视窗口&#xff0c;实现仓库…

unity3d————Resources异步加载

知识点一&#xff1a;Resources异步加载是什么&#xff1f; 在Unity中&#xff0c;资源加载可以分为同步加载和异步加载两种方式。同步加载会在主线程中直接进行&#xff0c;如果加载的资源过大&#xff0c;可能会导致程序卡顿&#xff0c;因为从硬盘读取数据到内存并进行处理…

计算机毕业设计Python+CNN卷积神经网络股票预测系统 股票推荐系统 股票可视化 股票数据分析 量化交易系统 股票爬虫 股票K线图 大数据毕业设计 AI

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

这个 AI 懂 Vue 吗?

作者&#xff1a;前端俱乐部 写在前面 最近海外的 AI 编辑器 Cursor 好像挺火的&#xff0c;与此同时&#xff0c;字节跳动也推出了豆包MarsCode编程助手&#xff0c;可以直接生成代码和极限编程。 豆包MarsCode AI 支持网页版编辑器&#xff0c;但我个人更喜欢让它和人气爆棚…

海量数据面试题

目录 前言 什么是海量数据 一、利用位图解决 二、利用布隆过滤器解决 三、利用哈希切割解决 前言 在大数据时代&#xff0c;海量数据处理已成为技术领域中的一项重要课题。无论是企业级应用、互联网平台&#xff0c;还是人工智能和机器学习的实现&#xff0c;都离不开对大规…

复现论文-报错记录dream-ood

复现论文Dream the Impossible: Outlier Imagination with Diffusion Models 过程中出现的问题记录 服务器&#xff1a;NIVIDA2080ti github: 论文&#xff1a; arxiv.org/pdf/2309.13415 1.pytorch使用出现"RuntimeError: An attempt has been made to start a new proc…

LinkedList与链表

目录 一、链表 链表相关练习题 二、LikedList 1、构造方法 2、常用方法 3、LinkedList的遍历 4、ArrayList与LinkedList的区别 一、链表 链表是一种物理存储结构上非连续存储结构&#xff0c;数据元素的逻辑顺序是通过链表中的引用链接次序实现的 链式结构在逻辑上是连…

vulnhub靶机hackxor提示(部分写出)

靶机地址&#xff1a;Hackxor: 1 ~ VulnHub 主机发现 130是靶机 端口扫描 服务扫描 漏洞扫描 Hosts配置&#xff08;这个是需要在网上找的&#xff0c;这个是靶机的缘故搭建不完全所以需要自己写hosts&#xff09; 访问wraithmail:8080 数据包 GET http://utrack/cat.jsp?id1…

录的视频怎么消除杂音?从录制到后期的杂音消除攻略

在录制视频时&#xff0c;杂音往往是一个令人头疼的问题。无论是环境噪音、设备噪音还是电磁干扰&#xff0c;杂音的存在都会极大地影响视频的听觉体验。录的视频怎么消除杂音&#xff1f;通过一些前期准备和后期处理技巧&#xff0c;我们可以有效地消除这些杂音&#xff0c;提…

C++内存模型与并发支持

本文是CppCon23演讲&#xff1a;C Memory Model&#xff1a;from C11 to C 23的笔记&#xff0c;掺杂个人见解以及扩展 内存模型 操作系统的四个特性&#xff1a;虚拟&#xff0c;并发&#xff0c;持久 抽象中很重要的一部分就是内存虚拟。从编程的角度来看&#xff0c;编程就…

机器学习day5-随机森林和线性代数1

十 集成学习方法之随机森林 集成学习的基本思想就是将多个分类器组合&#xff0c;从而实现一个预测效果更好的集成分类器。大致可以分为&#xff1a;Bagging&#xff0c;Boosting 和 Stacking 三大类型。 &#xff08;1&#xff09;每次有放回地从训练集中取出 n 个训练样本&…