动态代理:面向接口编程,屏蔽 RPC 处理流程

简介: 本节讲解动态代理在RPC中的核心作用:通过面向接口编程,利用JDK动态代理技术生成代理类,拦截接口调用并透明嵌入远程通信逻辑,屏蔽底层网络细节,实现“本地调用即远程调用”的无缝体验,提升开发效率与系统解耦能力。

05 | 动态代理:面向接口编程,屏蔽 RPC 处理流程
上一讲我分享了网络通信,其实要理解起来也很简单,RPC 是用来解决两个应用之间的通信,而网络则是两台机器之间的「桥梁」,只有架好了桥梁,我们才能把请求数据从一端传输另外一端。其实关于网络通信,你只要记住一个关键字就行了——可靠的传输。
那么接着上一讲的内容,我们再来聊聊动态代理在 RPC 里面的应用。
如果我问你,你知道动态代理吗? 你可能会如数家珍般地告诉我动态代理的作用以及好处。那我现在接着问你,你在项目中用过动态代理吗?这时候可能有些人就会犹豫了。那我再换一个方式问你,你在项目中有实现过统一拦截的功能吗?比如授权认证、性能统计等等。你可能立马就会想到,我实现过呀,并且我知道可以用 Spring 的 AOP 功能来实现。
没错,进一步再想,在 Spring AOP 里面我们是怎么实现统一拦截的效果呢?并且是在我们不需要改动原有代码的前提下,还能实现非业务逻辑跟业务逻辑的解耦。这里的核心就是采用动态代理技术,通过对字节码进行增强,在方法调用的时候进行拦截,以便于在方法调用前后,增加我们需要的额外处理逻辑。
那话说回来,动态代理跟 RPC 又有什么关系呢?
远程调用的魔法
我说个具体的场景,你可能就明白了。
在项目中,当我们要使用 RPC 的时候,我们一般的做法是先找服务提供方要接口,通过 Maven 或者其他的工具把接口依赖到我们项目中。我们在编写业务逻辑的时候,如果要调用提供方的接口,我们就只需要通过依赖注入的方式把接口注入到项目中就行了,然后在代码里面直接调用接口的方法 。
我们都知道,接口里并不会包含真实的业务逻辑,业务逻辑都在服务提供方应用里,但我们通过调用接口方法,确实拿到了想要的结果,是不是感觉有点神奇呢?想一下,在 RPC 里面,我们是怎么完成这个魔术的。
这里面用到的核心技术就是前面说的动态代理。RPC 会自动给接口生成一个代理类,当我们在项目中注入接口的时候,运行过程中实际绑定的是这个接口生成的代理类。这样在接口方法被调用的时候,它实际上是被生成代理类拦截到了,这样我们就可以在生成的代理类里面,加入远程调用逻辑。
通过这种「偷梁换柱」的手法,就可以帮用户屏蔽远程调用的细节,实现像调用本地一样地调用远程的体验,整体流程如下图所示:
实现原理
动态代理在 RPC 里面的作用,就像是个魔术。现在我不妨给你揭秘一下,我们一起看看这是怎么实现的。之后,学以致用自然就不难了。
我们以 Java 为例,看一个具体例子,代码如下所示:
Java
运行代码
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package cn.mrcode.study.rpc.s05;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**

  • JDK 代理类生成
    */
    public class JDKProxy implements InvocationHandler {
    private Object target;

    JDKProxy(Object target) {

     this.target = target;
    

    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

     return ((RealHello) target).invoke();
    

    }
    }
    Java
    运行代码
    复制代码
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package cn.mrcode.study.rpc.s05;

import java.lang.reflect.Proxy;

/**

  • 测试例子
    */
    public class TestProxy {
    public static void main(String[] args) {

     // 构建代理器
     JDKProxy proxy = new JDKProxy(new RealHello());
    
     final ClassLoader classLoader = ClassLoader.getSystemClassLoader();
    

    // ClassLoader classLoader = ClassLoaderUtils.getCurrentClassLoader();

     // 该 ClassLoaderUtils 类没有找到,不知道是否是其他的工具类
    
     // 把生成的代理类保存到文件
     System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
    
     // 生成代理类
     Hello test = (Hello) Proxy.newProxyInstance(classLoader, new Class[]{Hello.class}, proxy);
     // 方法调用
     System.out.println(test.say());
    

    }
    }
    这段代码想表达的意思就是:给 Hello 接口生成一个动态代理类,并调用接口 say() 方法,但真实返回的值居然是来自 RealHello 里面的 invoke() 方法返回值。你看,短短 50 行的代码,就完成了这个功能,是不是还挺有意思的?
    那既然重点是代理类的生成,那我们就去看下 Proxy.newProxyInstance 里面究竟发生了什么?
    一起看下下面的流程图,具体代码细节你可以对照着 JDK 的源码看(上文中有类和方法,可以直接定位),我是按照 1.7.X 版本梳理的。

相关文章
|
1天前
|
NoSQL 中间件 关系型数据库
开箱即用的 GoWind Admin|风行,企业级前后端一体中后台框架:如何进行Docker部署后端
GoWind Admin风行是一款企业级中后台框架,支持Docker一键部署。通过Makefile封装构建流程,提供docker-compose全量部署与docker run单服务部署两种模式,适配开发、生产多场景。支持服务增减灵活配置,助力高效容器化落地。
31 1
|
1天前
|
搜索推荐 算法 UED
15 | 最近邻检索(上):如何用局部敏感哈希快速过滤相似文章?
在搜索引擎与推荐系统中,相似文章去重至关重要。本文介绍如何利用向量空间模型将文章转化为高维向量,并通过局部敏感哈希(如SimHash)实现高效近似最近邻检索,结合抽屉原理优化索引,快速找出内容相似的文章,提升用户体验。该技术广泛应用于网页去重、图像识别等场景。
|
1天前
|
存储 算法 搜索推荐
线性结构检索:从数组和链表的原理初窥检索本质
本节深入解析数组与链表的存储特性及其对检索效率的影响。数组支持随机访问,适合二分查找,检索效率为O(log n);链表虽检索较慢,但插入删除高效,适用于频繁动态调整场景。通过改造链表结构,如结合数组提升检索性能,揭示了数据组织方式对检索的核心作用,帮助理解“快速缩小查询范围”这一检索本质。
|
1天前
|
存储 Java 索引
单/双链表代码实现
本文详解单/双链表的代码实现,涵盖增删查改操作。重点解析三大技巧:1)同时持有头尾节点引用以优化插入删除效率;2)使用虚拟头尾节点简化边界处理;3)避免内存泄漏的良好编程习惯。适合掌握链表基础后深入学习。
|
1天前
|
Ubuntu Shell Linux
Docker
本文介绍了Docker的常用操作命令,包括启动、停止、重启、查看状态及设置开机自启等基础操作,涵盖镜像的搜索、下载、删除、空间查看及虚悬镜像处理,并详细说明了如何配置命令自动补全功能。同时提供了后台运行Linux镜像及使用Docker下载RPM依赖的方法,适用于日常开发与运维场景。
|
1天前
|
存储 Java API
数组(顺序存储)基本原理
本章讲解数组的底层原理,区分静态数组与动态数组。静态数组是连续内存空间,支持O(1)随机访问,但增删效率低,需搬移数据;通过手动实现动态数组,理解其扩容、插入、删除等操作的实现逻辑与时间复杂度,为后续数据结构打下基础。
|
1天前
|
SQL 算法 关系型数据库
熔断限流:业务如何实现自我保护?
本讲介绍RPC框架中业务的自我保护机制。面对高并发,服务端通过限流(如令牌桶、滑动窗口)防止过载,支持应用级、IP级配置,并可结合注册中心动态调整阈值;调用端则通过熔断机制避免因下游故障引发雪崩,熔断器在动态代理层拦截请求,实现快速失败与恢复,保障系统稳定性。
|
1天前
|
存储 数据采集 搜索推荐
状态检索:如何快速判断一个用户是否存在?
本文探讨如何高效判断用户是否存在,对比有序数组、二分查找树和哈希表后,引出更优方案:位图与布隆过滤器。位图以bit为单位存储,大幅节省空间;布隆过滤器通过多哈希函数降低冲突概率,虽有一定误判率,但查询效率达O(1),适用于注册去重、爬虫去重等场景,是提升系统性能的关键技术。
|
1天前
|
负载均衡 算法 网络协议
负载均衡:节点负载差距这么大,为什么收到的流量还一样?
本文探讨RPC框架中的自适应负载均衡机制。针对传统权重调节滞后问题,提出通过实时采集节点CPU、内存、请求耗时等指标,结合权重算法动态打分,自动调整节点最终权重,实现流量智能分配,提升系统稳定性与响应效率。
|
1天前
|
存储 关系型数据库 MySQL
数据库检索
本文探讨如何用B+树为海量磁盘数据建立高效索引。由于磁盘访问远慢于内存,关键在于减少磁盘I/O次数。B+树通过多路平衡查找、节点大小匹配磁盘块、顺序访问优化等方式,显著提升磁盘数据检索效率,广泛应用于MySQL等数据库系统。