Dubbo 服务端源码深入分析 (7)

news2024/11/29 9:57:38

目录

1. 前提

2. 认识 Protocol 和 ProxyFactory

Protocal

ProxyFactory

Dubbo服务流程

服务端源码分析

测试代码:

Protocal代理的源码

ProxyFactory源码:

获取invoker对象

具体步骤

1. 我们调用的是ProxyFactory的代理对象的getInvoker方法

2. 我们把断点打到 JavassistProxyFactory 类的对应方法处,进入断点并且封装了一个invoker对象进行返回

服务暴露:

1. 我们调用的是Portocal对象的静态代理类的export方法

 2. 直接把断点打到 RegistryProtocal类的export方法上

3. 重点分析服务暴露doLocalExport方法


1. 前提

本来想着一篇文章分析完dubbo主流程源码的,但是写完Dubbo 基于xml文件分析主流程源码 (4)_chen_yao_kerr的博客-CSDN博客以后发现,源码中存在大量的Dubbo SPI思想,不说清楚Dubbo SPI, 源码是看不下去的。因此,紧接着我又补了2篇Dubbo SPI的博客。

所以有了前3篇博客的铺垫,再次来谈源码,相信会简单很多。

2. 认识 Protocol 和 ProxyFactory

不了解这两个接口,今天的源码分析是走不下去的,它是Dubbo SPI思想运用于Dubbo框架非常重要的实践。

Protocal

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.alibaba.dubbo.rpc;

import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.Adaptive;
import com.alibaba.dubbo.common.extension.SPI;

@SPI("dubbo")
public interface Protocol {
    int getDefaultPort();

    @Adaptive
    <T> Exporter<T> export(Invoker<T> var1) throws RpcException;

    @Adaptive
    <T> Invoker<T> refer(Class<T> var1, URL var2) throws RpcException;

    void destroy();
}

他有很多实现类:

 而这些子类的key-value形式,都是配置在如下路径中:

 先对这些概率有个大概的了解,后面会在代码里面直接提到这个key,希望对照这些图片,能够快速的知道我在说的是什么类。

ProxyFactory


package com.alibaba.dubbo.rpc;

import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.Adaptive;
import com.alibaba.dubbo.common.extension.SPI;

@SPI("javassist")
public interface ProxyFactory {
    @Adaptive({"proxy"})
    <T> T getProxy(Invoker<T> var1) throws RpcException;

    @Adaptive({"proxy"})
    <T> T getProxy(Invoker<T> var1, boolean var2) throws RpcException;

    @Adaptive({"proxy"})
    <T> Invoker<T> getInvoker(T var1, Class<T> var2, URL var3) throws RpcException;
}

它和上面的接口一样,也有很多的子类:

 而这些子类的key-value形式,都是配置在如下路径中:

Dubbo服务流程

1. 需要区分2个URL,第一个是连接注册中心的URL, 注册中心可能是nacos、euraka等等。我使用的是zookeeper。  第二个URL是服务提供方的URL,也就是实际提供服务的机器。注册中心只是一个管理服务提供方的中间件,在注册中心中,我们可以找到这些真正提供服务的机器的ip、应用名、暴露的服务接口、方法等等信息。

2. 我们需要把想要暴露的服务接口的实现类实例化,经过各种封装、最终成为invoker对象,而invoker对象含有 ip、端口号、接口、方法、应用名等信息

3. 通过协议把invoker给暴露出去 (此时,基本的RPC操作就完成了)

4. 获取注册中心对象,把信息交给注册中心去管理。也就是发布、订阅等。 这一步大量的优化了RPC处理集群的复杂过程。

服务端源码分析

测试代码:

package com.enjoy;

import com.alibaba.dubbo.common.Constants;
import com.alibaba.dubbo.common.URL;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
import com.alibaba.dubbo.rpc.Exporter;
import com.alibaba.dubbo.rpc.Invoker;
import com.alibaba.dubbo.rpc.Protocol;
import com.alibaba.dubbo.rpc.ProxyFactory;
import com.enjoy.service.DemoService;
import com.enjoy.service.ZkDemoServiceImpl;
import org.junit.Test;

import java.io.IOException;

/**
 * 注册中心动态服务
 */
public class RpcRegistryProtocolTest {
    ExtensionLoader<Protocol> protocolLoader = ExtensionLoader.getExtensionLoader(Protocol.class);
    ExtensionLoader<ProxyFactory> proxyLoader = ExtensionLoader.getExtensionLoader(ProxyFactory.class);

    //注册中心服务--zk的URL
    final URL registryUrl = URL.valueOf("registry://192.168.0.105:2181/com.alibaba.dubbo.registry.RegistryService?registry=zookeeper");

    // 模拟服务器的URL连接,服务端、消费端用的是同一个URL
    // 支持的协议:dubbo,http,hessian,rmi
    URL serviceUrl = URL.valueOf("dubbo://127.0.0.1:9001/com.enjoy.service.DemoService?application=abcd");
    @Test
    public void serverRpc() throws IOException {

        DemoService service = new ZkDemoServiceImpl("peter");
        //代理对象
        Protocol protocol = protocolLoader.getAdaptiveExtension();
        ProxyFactory proxy = proxyLoader.getAdaptiveExtension();

        serviceUrl = serviceUrl.setPort(9001);
        serviceUrl = serviceUrl.addParameter("loadbalance","first");
        serviceUrl = serviceUrl.addParameter("cluster","failsms");
        //连接到注册中心,把模拟的服务器的URL添加进连接注册中心的URL中
        URL newRegistryUrl = registryUrl.addParameter(Constants.EXPORT_KEY, serviceUrl.toFullString());
        //暴露服务
        Invoker<DemoService> serviceInvoker = proxy.getInvoker(service, DemoService.class, newRegistryUrl);
        Exporter<DemoService> exporter = protocol.export(serviceInvoker);
        System.out.println("server 启动协议:"+serviceUrl.getProtocol());
        // 保证服务一直开着
        System.in.read();
        exporter.unexport();
    }
}

下面我就对照服务流程逐步的去拆解代码。

关于静态代理对象,代码是相同的,我直接贴出代码。这不是重点流程,想要深入的可以继续跟:

 那么我就贴出生成的2个静态代理对象的代码:

Protocal代理的源码

package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;

public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {

	public void destroy() {
		throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
	}
	
	public int getDefaultPort() {
		throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!");
	}
	
	public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
		if (arg1 == null) 
			throw new IllegalArgumentException("url == null");
			
		com.alibaba.dubbo.common.URL url = arg1;
		String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
		
		if(extName == null) 
			throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
			
		com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
		return extension.refer(arg0, arg1);
	}
	
	public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
		if (arg0 == null) 
			throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
		if (arg0.getUrl() == null) 
			throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
			
		String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );	//url没有指定处理类,默认用dubbo的。如果url中配置了处理类,则使用url中自带的
		if(extName == null) 
			throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
			
		com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol)ExtensionLoader
															.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);  //又是dubbo spi思想
		
		return extension.export(arg0);	//实际的业务处理类
	}
}

ProxyFactory源码:

package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class ProxyFactory$Adaptive implements com.alibaba.dubbo.rpc.ProxyFactory {

	public java.lang.Object getProxy(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException {
		if (arg0 == null) 
			throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
		if (arg0.getUrl() == null) 
			throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();	
		String extName = url.getParameter("proxy", "javassist");
		if(extName == null)
			throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
			
		com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader
																					.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
		return extension.getProxy(arg0);
	}
	
	public java.lang.Object getProxy(com.alibaba.dubbo.rpc.Invoker arg0, boolean arg1) throws com.alibaba.dubbo.rpc.RpcException {
		if (arg0 == null) 
			throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null");
		if (arg0.getUrl() == null) 
			throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null");com.alibaba.dubbo.common.URL url = arg0.getUrl();
		String extName = url.getParameter("proxy", "javassist");
		if(extName == null) 
			throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
			
		com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader
																				.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
		return extension.getProxy(arg0, arg1);
	}
	
	public com.alibaba.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, com.alibaba.dubbo.common.URL arg2) throws com.alibaba.dubbo.rpc.RpcException {
		if (arg2 == null) 
			throw new IllegalArgumentException("url == null");
		com.alibaba.dubbo.common.URL url = arg2;
		String extName = url.getParameter("proxy", "javassist");  //如果URL中有proxy,就根据URL配置的信息获取处理类。如果没有,那就是用默认的处理类javassist
		if(extName == null) 
			throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
		com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader
													.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName); 	//dubbole spi思路
		return extension.getInvoker(arg0, arg1, arg2);		//调用具体的子类
	}
}

这两个静态代理是通过字符串的形式给拼接出来的,内存中是有的,但是我们无法进行debug操作,只能对照这代码进行分析。

获取invoker对象

 我们知道,proxy是一个静态代理对象,是有字符串拼接出来的并且在内存中是存在的,因此需要接口上方贴出的2个代理对象的源码进行分析。

具体步骤

1. 我们调用的是ProxyFactory的代理对象的getInvoker方法

2. 我们把断点打到 JavassistProxyFactory 类的对应方法处,进入断点并且封装了一个invoker对象进行返回

服务暴露:

1. 我们调用的是Portocal对象的静态代理类的export方法

 2. 直接把断点打到 RegistryProtocal类的export方法上

3. 重点分析服务暴露doLocalExport方法

其实,所谓的服务暴露,就是将需要暴露的invoker对象与协议进行绑定,并不是真的将invoker对象写到注册中心去。 

在步骤2中,我们也提及到了注册与发布,其实他们操作的也都是URL信息而已

注册中心,写入的只是服务器的 URL信息,根据不同的URL信息可以找到不同的invoer对象,然后由invoker对象再解析成对应的业务对象与方法进行调用。

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

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

相关文章

Linux线程同步(6)——更高并行性的读写锁

互斥锁或自旋锁要么是加锁状态、要么是不加锁状态&#xff0c;而且一次只有一个线程可以对其加锁。读写锁有 3 种状态&#xff1a;读模式下的加锁状态&#xff08;以下简称读加锁状态&#xff09;、写模式下的加锁状态&#xff08;以下简称写加锁状态&#xff09;和不加锁状态&…

django视图(request请求response返回值)

一、视图函数介绍 视图就是应用中views.py中定义的函数&#xff0c;称为视图函数 def index(request):return HttpResponse("hello world&#xff01;") 1、视图的第一个参数必须为HttpRequest对象&#xff0c;还可能包含下参数如通过正则表达式组获取的位置参数、通…

VBA——01篇(入门篇——简单基础语法)

VBA——01篇&#xff08;入门篇——简单基础语法&#xff09; 1. 语法格式1.1 简单语法1.2 简单例子 2. 变量2.1 常用数据类型2.2 声明变量的常用方式2.3 简单例子 3. 单元格赋值3.1 直接赋值3.2 拷贝单元格 4. 简单的逻辑语法4.1 简单if4.2 简单for循环4.2.1 简单语法例子4.2.…

基于混合整数二阶锥(MISOCP)的配电网重构(附matlab代码)

参考资料&#xff1a;主动配电网网络分析与运行调控 (sciencereading.cn) 配电网重构是指在满足配电网运行基本约束的前提下&#xff0c;通过改变配电网中一个或多个开关的状态对配电网中一个或多个指标进行优化。通过配电网重构&#xff0c;可以在不增加设备投资的情况下&…

注解实现:判空赋值

工作中的小玩意~~ 流程&#xff1a; 注解实现反射工具类 注解定义及实现 注解定义&#xff1a; Documented Target(ElementType.FIELD) Retention(RetentionPolicy.RUNTIME) public interface CheckParam {String value() default "-1"; }简单解释上述其相关注解…

哈工大2023春计算机组成原理真题回忆

仅供同学参考&#xff0c;严禁用作商业用途 如发现将追究责任 2023-5-14 属鼠经历了计算机组成原理考试 现将本人真题回忆如下&#xff1a;欢迎大家补充&#xff0c;并期待大家一起参与这个开源的项目。 致谢:真诚感谢草履虫同学提供的图片 15个选择部分回忆如下 &#xff1a…

【历史上的今天】4 月 13 日:Damn Small Linux 首次发布;谷歌关闭短网址服务;数学先驱出生

整理 | 王启隆 透过「历史上的今天」&#xff0c;从过去看未来&#xff0c;从现在亦可以改变未来。 今天是 2023 年 4 月 13 日&#xff0c;在 2006 年的今天&#xff0c;盛大文学榕树下网站被民营企业收购&#xff1b;原创文学网站榕树下被民营传媒集团欢乐传媒收购&#xff…

hnust 湖南科技大学 2023 软件测试技术 期中考试 复习资料

前言 写的比较匆忙&#xff0c;重点也不明确&#xff0c;没什么参考价值致谢&#xff1a;ly&#xff0c;zxq重点来源&#xff1a;信安※&#xff1a;补充内容★&#xff1a;重点✦&#xff1a;个人推测考点考试范围&#xff1a;1-9章获取最新版本 题型 判断&#xff1a;10简…

AMBER分子动力学模拟之TOP准备-- HIV蛋白酶-抑制剂复合物(1)

AMBER分子动力学模拟之TOP准备-- HIV蛋白酶-抑制剂复合物(1) 我们以HIV蛋白酶-抑制剂复合物为例子&#xff0c;跑Amber动力学模拟 下载1phv 从PBD下载文件&#xff1a;https://www.rcsb.org/ PDB文件预处理 我们以 “protein(water) ligandcomplex” 为例来说一下如何处…

系统设计基本原理-耦合与内聚

耦合 耦合是模块之间的相互独立性(互相连接的紧密程度)的度量&#xff0c;耦合取决于各个模块之间接口的复杂程度、调用模块的方式以及通过接口的信息类型等。 耦合类型 无直接耦合&#xff1a;指两个模块之间没有直接的关系&#xff0c;它们分别从属于不同模块的控制与调用&…

k8s基础11——安全控制之RBAC用户授权、RBAC用户组授权、SA程序授权

文章目录 一、K8s安全框架1.1 鉴权1.1.1 HTTPS证书认证1.1.2 HTTP Token认证 1.2 授权1.3 准入控制1.4 集群四大角色 二、RBAC给用户授权&#xff08;TLS&#xff09;2.1 签发客户端证书2.2 生成kubeconfig授权文件2.2.1 手动生成2.2.2 脚本生成2.2.3 切换操作集群 2.3 定义RBA…

移动应用开发实验-内容提供者-ContentResolver的使用

文章目录 前言读取通讯录信息要求环境 具体实现主页面布局(activity_main.xml)关于RecyclerView库的相关问题添加RecyclerView库操作 解决报错Item布局(info.xml)添加访问权限编写实体类&#xff08;ContactInfo.java&#xff09;编写适配器&#xff08;MyAdapter.java&#xf…

20 散列表的查找

散列表的查找 简介&#xff1a;散列表&#xff08;也成哈希表&#xff09;是一种高效的数据结构&#xff0c;他可以在平均复杂度为O(1)的情况下实现查找、插入和删除操作。 哈希表的基本思想是根据关键字的值来计算其应存储的位置。这个计算过程就是通过哈希函数来实现的。 根…

计算机视觉——day 91基于双网络的鲁棒特征高光谱目标检测(偏门且很水啊)

基于双网络的鲁棒特征高光谱目标检测 I. INTRODUCTIONII. 提出的方法A. 总体框架B.训练集构建C. Dual Networks III. EXPERIMENTSIV. 结论 I. INTRODUCTION 用于高光谱目标检测的深度网络训练通常面临样本有限的问题&#xff0c;在极端情况下&#xff0c;可能只有一个目标样本…

黑盒测试方法

1 等价类划分 1.1 定义 等价类划分法是一种典型的&#xff0c;并且是最基础的黑盒测试用例设计方法。采用等价类划分法时&#xff0c;完全不用考虑程序内部结构&#xff0c;设计测试用例的唯一依据是软件需求规格说明书。 所谓等价类&#xff0c;是输入条件的一个子集合&…

kali整体版本更新方法,为啥更新?

玩过kali都知道&#xff0c;如果不更新版本&#xff0c;那么安装某个软件总是有很多依赖版本问题&#xff0c;解决起来的确麻烦&#xff0c;这篇文章彻底解决这些问题。 1&#xff0c;更新源 国外源与国内源的选择 kali默认配置的是国外源&#xff0c;但国外源的下载速度非常慢…

基于容器和Kubernetes的应用无限扩容

基于应用负载动态管理CPU、内存等资源的使用是云原生架构的最佳实践之一&#xff0c;Kubernetes通过资源请求和限制以及HPA/VPA等机制&#xff0c;为云原生应用资源管理提供了很好的支持。原文: Infinite Scaling with Containers and Kubernetes[1] 如果没有足够资源让容器完成…

matlab第八章_Simulink简介

目录 Simulink简介 基本知识 Simulink组成 模块库简介 Simulink系统仿真 Stateflow建模与应用 Stateflow的定义 状态图编辑器 Stateflow流程图 Simulink简介 基本知识 Simulink是实现动态系统建模&#xff0c;仿真和分析的软件包&#xff0c;被广泛应用于线性系统&…

土地报征简介

报征概念&#xff1a; 土地报征是指国家为了人民整体利益出发&#xff0c;根据我国相关法律和法规的要求和流程&#xff0c; 将集体土地性质转化为国有土地性质&#xff0c;并给予被征地的对象给予合理的补偿和安置工作。报征4个价段&#xff1a; 1、组卷阶段 &#xff08;1&…

Linux系统之top命令的基本使用

Linux系统之top命令的基本使用 一、top命令介绍二、检查本地环境1. 检查操作系统版本2. 检查系统内核版本 三、top命令的使用帮助1. top命令的选项2. top命令的交换命令 四、top显示信息解释1. top的第一行解释2. top的第二、三行信息解释3. top的第四、五行信息解释4. top的进…