当前位置: 首页 > 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;显著提升选拔工作的科学性和公正性。以下是大…...

Java多线程实现之Callable接口深度解析

Java多线程实现之Callable接口深度解析 一、Callable接口概述1.1 接口定义1.2 与Runnable接口的对比1.3 Future接口与FutureTask类 二、Callable接口的基本使用方法2.1 传统方式实现Callable接口2.2 使用Lambda表达式简化Callable实现2.3 使用FutureTask类执行Callable任务 三、…...

【HTML-16】深入理解HTML中的块元素与行内元素

HTML元素根据其显示特性可以分为两大类&#xff1a;块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

浅谈不同二分算法的查找情况

二分算法原理比较简单&#xff0c;但是实际的算法模板却有很多&#xff0c;这一切都源于二分查找问题中的复杂情况和二分算法的边界处理&#xff0c;以下是博主对一些二分算法查找的情况分析。 需要说明的是&#xff0c;以下二分算法都是基于有序序列为升序有序的情况&#xf…...

在WSL2的Ubuntu镜像中安装Docker

Docker官网链接: https://docs.docker.com/engine/install/ubuntu/ 1、运行以下命令卸载所有冲突的软件包&#xff1a; for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done2、设置Docker…...

MySQL账号权限管理指南:安全创建账户与精细授权技巧

在MySQL数据库管理中&#xff0c;合理创建用户账号并分配精确权限是保障数据安全的核心环节。直接使用root账号进行所有操作不仅危险且难以审计操作行为。今天我们来全面解析MySQL账号创建与权限分配的专业方法。 一、为何需要创建独立账号&#xff1f; 最小权限原则&#xf…...

Unsafe Fileupload篇补充-木马的详细教程与木马分享(中国蚁剑方式)

在之前的皮卡丘靶场第九期Unsafe Fileupload篇中我们学习了木马的原理并且学了一个简单的木马文件 本期内容是为了更好的为大家解释木马&#xff08;服务器方面的&#xff09;的原理&#xff0c;连接&#xff0c;以及各种木马及连接工具的分享 文件木马&#xff1a;https://w…...

基于Java+MySQL实现(GUI)客户管理系统

客户资料管理系统的设计与实现 第一章 需求分析 1.1 需求总体介绍 本项目为了方便维护客户信息为了方便维护客户信息&#xff0c;对客户进行统一管理&#xff0c;可以把所有客户信息录入系统&#xff0c;进行维护和统计功能。可通过文件的方式保存相关录入数据&#xff0c;对…...

Linux 中如何提取压缩文件 ?

Linux 是一种流行的开源操作系统&#xff0c;它提供了许多工具来管理、压缩和解压缩文件。压缩文件有助于节省存储空间&#xff0c;使数据传输更快。本指南将向您展示如何在 Linux 中提取不同类型的压缩文件。 1. Unpacking ZIP Files ZIP 文件是非常常见的&#xff0c;要在 …...

Golang——6、指针和结构体

指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...

给网站添加live2d看板娘

给网站添加live2d看板娘 参考文献&#xff1a; stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下&#xff0c;文章也主…...