当前位置: 首页 > news >正文

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();}

至此服务暴漏过程结束。

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

相关文章:

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

一、服务导出 当我们在某个接口的实现类上加上DubboService后&#xff0c;就表示定义了一个Dubbo服务&#xff0c;应用启动时Dubbo只要扫描到了DubboService&#xff0c;就会解析对应的类&#xff0c;得到服务相关的配置信息&#xff0c;比如&#xff1a; 1. 服务的类型&…...

浅谈React的虚拟DOM

React的虚拟DOM&#xff1a;揭秘高效渲染的秘密 在React中&#xff0c;虚拟DOM&#xff08;Virtual DOM&#xff09;是一个核心概念&#xff0c;它是React能够提供高效渲染和更新的关键。虚拟DOM是一个轻量级的JavaScript对象&#xff0c;表示真实的DOM树。通过使用虚拟DOM&am…...

linux上海康SDK安装并设置环境变量

将HCNetSDK下linux部分复制到客户端电脑/usr/lib/HCNetSDK下:sudo cp -r H /usr/lib/HCNetSDK H是我的文件夹&#xff0c;要把这个文件夹的内容复制到/usr/lib/HCNetSDK路径里。 编辑&#xff1a;vi ~/.bashrc 找到export,按 i 插入换行添加 export LD_LIBRARY_PATH$LD_LIB…...

【计算机网络】UDP网络程序

一、服务端 1.udpServer.hpp 此文件负责实现一个udp服务器 #pragma once#include <iostream> #include <string> #include <cstdlib> #include <cstring> #include <functional> #include <strings.h> #include <unistd.h> #incl…...

什么是全域电商?有哪些电商代运营公司能做全域电商代运营?

什么是全域电商&#xff1f;有哪些电商代运营公司能做全域电商代运营&#xff1f; 随着电商行业的迅猛发展&#xff0c;传统的单一平台运营模式已经无法满足品牌多元化发展的需求。在此背景下&#xff0c;全域电商作为一种新兴的运营方式应运而生&#xff0c;成为越来越多品牌在…...

微信小程序上传pdf和显示

引用&#xff1a;https://blog.csdn.net/qq_54027065/article/details/129854339 loadResume(){let that thisuni.showLoading({title:"下载中"})wx.downloadFile({url:url,success:(res)>{console.log(res,"res11111")if (res.statusCode 200){setTi…...

MongoDB分布式集群搭建----副本集----PSS/PSA

MongoDB分布式集群 Replication 复制、Replica Set 复制集/副本集 概念 一、 副本集的相关概念 1.概念 “ A replica set is a group of mongod instances that maintain the same data set. ” 一组MongoDB服务器&#xff08;多个mongod实例&#xff09;&#xff08;有不…...

PDF编辑的好东西

1.Eage浏览器 直接拖到浏览器中就ok了&#xff0c;这样读书的话是非常爽的&#xff0c;然后的话最近&#xff0c;也不知道学啥&#xff0c;vue开发网站&#xff0c;一开始的配置&#xff0c;也是给我难到了&#xff0c;所以没有办法&#xff0c;就随便找点书看看吧&#xff0c…...

块设备的两种访问方法的区别

概述 1.当我们运行类似于“dd if/dev/sdb1ofsdb1.img”的命令把整个/dev/sdb1裸分区复制到sdb1.img的时候&#xff0c;内核走的是def_blk_fops这个file_operations 2.另外一种方法是通过文件系统来访问块设备&#xff0c;file_operations的实现则位于文件系统内&#xff0c;文…...

java 泛型中的 ?

在 Java 泛型中&#xff0c;? 被称为通配符&#xff08;wildcard&#xff09;&#xff0c;它代表了未知的类型。使用通配符可以增加代码的灵活性&#xff0c;允许在不知道具体类型的情况下操作泛型类或接口。通配符主要有以下几种形式&#xff1a; 无界通配符&#xff08;Unbo…...

如何在jupyter notebook切换python环境

目录 参考链接 首先确保conda已经正常安装 conda --version 或者conda -V 以下请将“myenv”替换成自己的命名&#xff01;&#xff01;&#xff01; 1-查看虚拟环境目录 conda env list 2-创建虚拟环境命令 conda create -n myenv 或者 conda create --name myenv 3-激活虚拟环…...

用Python将Word文档转换为Markdown格式

Markdown作为一种轻量级标记语言&#xff0c;以其简洁的语法和广泛的兼容性&#xff0c;特别适合用于博客、技术文档和版本控制系统中的内容管理。而Word文档则因其强大的排版功能&#xff0c;常常成为文档制作的首选。然而&#xff0c;直接使用Word格式在某些平台上可能显得过…...

CSV 文件

CSV&#xff0c;全称为 Comma-Separated Values&#xff09;&#xff08;逗号分隔值&#xff09;&#xff0c;是一种常用的文本文件格式&#xff0c;用于存储表格数据&#xff0c;如电子表格或数据库。它采用纯文本形式&#xff0c;以逗号作为字段之间的分隔符&#xff0c;每行…...

SpringCloud核心组件(五)

文章目录 Gateway一. 概述简介1. Gateway 是什么2. 什么是网关?3.Gateway 和 Nginx 两个网关的区别什么是流量入口&#xff1f; 4.Gateway 能干嘛5.gateway 三大核心概念6.运行方式 二. 入门案例a.创建gateway模块&#xff0c;在pom.xml中引入依赖b.创建启动类GatewayApplicat…...

TCP为什么需要三次握手和四次挥手,有哪些需要注意的地方?

TCP&#xff08;传输控制协议&#xff09;是一种面向连接的、可靠的、基于字节流的传输层通信协议。为了确保数据能够准确无误地从一端发送到另一端&#xff0c;TCP设计了一系列机制来保证通信的可靠性&#xff0c;其中包括连接建立和断开的过程。 三次握手&#xff08;Three-…...

机器学习(基础2)

特征工程 特征工程:就是对特征进行相关的处理 一般使用pandas来进行数据清洗和数据处理、使用sklearn来进行特征工程 特征工程是将任意数据(如文本或图像)转换为可用于机器学习的数字特征,比如:字典特征提取(特征离散化)、文本特征提取、图像特征提取。 特征工程API 实例化…...

Cpolar 内网穿透使用

Cpolar登录地址&#xff1a;cpolar - secure introspectable tunnels to localhost 使用固定公网TCP连接ssh ssh -p端口号 用户名公网地址...

ThreadLocal 提供线程局部变量

ThreadLocal作用 相当于建立一个独立的空间&#xff0c;可以把使用频率高的任何类型的数据放到里面&#xff0c;方便调用用来存取数据&#xff1a;set()/get()使用ThreadLocal存储的数据&#xff0c;线程安全 ThreadLocal工具类 /*** ThreadLocal 工具类*/ SuppressWarnings(…...

MongoDB聚合管道数组操作

数组表达式运算符判断数组中是否包含元素( i n ) 并获取元素索引 ( in)并获取元素索引( in)并获取元素索引(indexOfArray) 一、初始化成员数据 db.persons.insertMany([{ "_id" : "1001", "name" : "张三", "fruits" : [ …...

大数据如何助力干部选拔的公正性

随着社会的发展和进步&#xff0c;干部选拔成为组织管理中至关重要的一环。传统的选拔方式可能存在主观性、不公平性以及效率低下等问题。大数据技术的应用&#xff0c;为干部选拔提供了更加全面、精准、客观的信息支持&#xff0c;显著提升选拔工作的科学性和公正性。以下是大…...

Unity3D中Gfx.WaitForPresent优化方案

前言 在Unity中&#xff0c;Gfx.WaitForPresent占用CPU过高通常表示主线程在等待GPU完成渲染&#xff08;即CPU被阻塞&#xff09;&#xff0c;这表明存在GPU瓶颈或垂直同步/帧率设置问题。以下是系统的优化方案&#xff1a; 对惹&#xff0c;这里有一个游戏开发交流小组&…...

【WiFi帧结构】

文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成&#xff1a;MAC头部frame bodyFCS&#xff0c;其中MAC是固定格式的&#xff0c;frame body是可变长度。 MAC头部有frame control&#xff0c;duration&#xff0c;address1&#xff0c;address2&#xff0c;addre…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

根据万维钢·精英日课6的内容,使用AI(2025)可以参考以下方法:

根据万维钢精英日课6的内容&#xff0c;使用AI&#xff08;2025&#xff09;可以参考以下方法&#xff1a; 四个洞见 模型已经比人聪明&#xff1a;以ChatGPT o3为代表的AI非常强大&#xff0c;能运用高级理论解释道理、引用最新学术论文&#xff0c;生成对顶尖科学家都有用的…...

mysql已经安装,但是通过rpm -q 没有找mysql相关的已安装包

文章目录 现象&#xff1a;mysql已经安装&#xff0c;但是通过rpm -q 没有找mysql相关的已安装包遇到 rpm 命令找不到已经安装的 MySQL 包时&#xff0c;可能是因为以下几个原因&#xff1a;1.MySQL 不是通过 RPM 包安装的2.RPM 数据库损坏3.使用了不同的包名或路径4.使用其他包…...

华硕a豆14 Air香氛版,美学与科技的馨香融合

在快节奏的现代生活中&#xff0c;我们渴望一个能激发创想、愉悦感官的工作与生活伙伴&#xff0c;它不仅是冰冷的科技工具&#xff0c;更能触动我们内心深处的细腻情感。正是在这样的期许下&#xff0c;华硕a豆14 Air香氛版翩然而至&#xff0c;它以一种前所未有的方式&#x…...

RSS 2025|从说明书学习复杂机器人操作任务:NUS邵林团队提出全新机器人装配技能学习框架Manual2Skill

视觉语言模型&#xff08;Vision-Language Models, VLMs&#xff09;&#xff0c;为真实环境中的机器人操作任务提供了极具潜力的解决方案。 尽管 VLMs 取得了显著进展&#xff0c;机器人仍难以胜任复杂的长时程任务&#xff08;如家具装配&#xff09;&#xff0c;主要受限于人…...

【网络安全】开源系统getshell漏洞挖掘

审计过程&#xff1a; 在入口文件admin/index.php中&#xff1a; 用户可以通过m,c,a等参数控制加载的文件和方法&#xff0c;在app/system/entrance.php中存在重点代码&#xff1a; 当M_TYPE system并且M_MODULE include时&#xff0c;会设置常量PATH_OWN_FILE为PATH_APP.M_T…...

【C++】纯虚函数类外可以写实现吗?

1. 答案 先说答案&#xff0c;可以。 2.代码测试 .h头文件 #include <iostream> #include <string>// 抽象基类 class AbstractBase { public:AbstractBase() default;virtual ~AbstractBase() default; // 默认析构函数public:virtual int PureVirtualFunct…...

Spring Security 认证流程——补充

一、认证流程概述 Spring Security 的认证流程基于 过滤器链&#xff08;Filter Chain&#xff09;&#xff0c;核心组件包括 UsernamePasswordAuthenticationFilter、AuthenticationManager、UserDetailsService 等。整个流程可分为以下步骤&#xff1a; 用户提交登录请求拦…...