news 2026/6/16 4:02:53

RPC、动态代理、反向代理与负载均衡全解析

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
RPC、动态代理、反向代理与负载均衡全解析

🔥个人主页:代码不加冰(欢迎来访)
🎬作者简介:java后端学习者
❄️个人专栏:LeetCode刷题日记 , 苍穹外卖日记,SSM框架深入,JavaWeb,
命运的结局尽可永在,不屈的挑战却不可须臾或缺!


前言:

大家好我是代码不加冰,这里给大家分享一下最近在一个项目中学的新知识,也算是自我的一个总结,方便以后复习。


摘要:

本文结合实际项目经验,对 RPC(远程过程调用)的核心原理进行了系统梳理。

从为什么微服务之间不直接使用 HTTP 调用入手,分析了 RPC 在性能、开发体验以及服务治理方面的优势。随后深入讲解了 RPC 的底层调用流程,包括序列化、网络传输、服务执行和结果返回等关键环节,并重点介绍了动态代理在 RPC 框架中的作用,帮助读者理解 Dubbo 如何实现“像调用本地方法一样调用远程服务”。

此外,文章还详细对比了 JDK 动态代理与 CGLIB 动态代理的实现方式,并进一步延伸到 Nginx 反向代理和负载均衡机制,分析其与 Dubbo 客户端负载均衡的区别与协作关系。通过本文,读者可以建立从动态代理、RPC 到微服务通信架构的完整知识体系。

什么是RPC框架:

RPC(Remote Procedure Call),即远程过程调用

  • 本地调用:你的代码在同一个 JVM(同一个进程)里。你写了一个userService.getUserById(1),直接在内存里点过去就执行了。

  • 远程调用:随着业务变大,你把用户模块拆分成了User 服务,把订单模块拆分成了Order 服务,它们分别部署在两台不同的服务器上。此时,Order 服务想要调用User 服务的方法,由于内存不共享,普通的本地调用直接失效了

RPC 的核心目标就是:让调用远程服务的方法时,像调用本地方法一样简单、透明。

1.为什么不用 HTTP / RestTemplate,非要用 RPC

很多刚学完 Spring Boot 的同学会问:“我用 HTTP 请求(比如 RestTemplate 或者是 HttpClient)一样可以跨服务器调用啊,为什么要用 RPC 框架?”

我们可以用一个简单的表格来对比:

特性HTTP (RESTful 常用)RPC (以 Dubbo 为例)
传输协议通常基于 HTTP/1.1,文本传输(JSON)常用底层 TCP(Dubbo协议)或 HTTP/2(Triple协议),二进制传输
性能消息体相对较大,序列化/反序列化慢,开销大消息体小,序列化速度极快,吞吐量极高
开发体验需要手动拼 URL、处理状态码、解析 JSON就像调用本地接口一样,强类型约束,支持代码补全
服务治理需要额外搭配各种组件(如 Ribbon 负载均衡)自带服务发现、负载均衡、容错、流量控制等功能

大白话总结:HTTP 就像邮寄挂号信,格式通用,但包装重、速度慢;RPC 就像内部专用传送带,专门为了高并发、高性能的分布式系统“量身定制”。


2.RPC 的底层工作流程

一个完整的 RPC 框架在底层到底做了什么其实它的核心流程其实就 4 步:

  1. Stub(桩/代理):消费端(Consumer)通过动态代理,生成一个接口的代理对象。你以为你调用的是方法,其实调用的是代理。

  2. Serialize(序列化):代理对象把你的请求(方法名、参数类型、参数值)打包,转成二进制字节流

  3. Transport(网络传输):通过网络(比如 Netty/TCP),将字节流发送到服务提供端(Provider)。

  4. Deserialize(反序列化):提供端收到字节流,还原成 Java 对象,通过反射调用真正的业务方法,然后再把结果按原路返回。


动态代理:

正如前面所说,RPC 的目标是让你像调用本地方法一样调用远程服务。但问题是,消费端(Consumer)手里只有接口(比如UserService.java),并没有实现类(实现类在远端的服务器上)。

在 Java 中,接口是不能直接new出来对象的。那userService.getUserById(1)这行代码为什么能跑通,而且还能把请求发送到网络另一端呢

这就是动态代理施展的地方。

1. 什么是动态代理
  • 静态代理:比如你想买海外的商品,你找了一个专业的代购。这个代购(代理类)和你想买的东西(接口)是一一对应的,代购在代码运行前就已经存在了。

  • 动态代理:你去了一个巨大的万能办事处。你走过去说:“我想调用UserServicegetUserById方法。” 办事处当场给你临时变出一个经纪人(代理对象)。这个经纪人根本不知道getUserById怎么具体实现,但他知道把你的请求打包、通过网络发给远端的真实服务器、再把结果拿回来还给你

动态代理的核心能力:在程序运行期间,根据你传入的接口,动态地在内存中生成一个代理对象。你对这个接口的所有方法调用,都会被拦截并重定向到一个统一的处理器中。


2. 在 RPC 项目中,动态代理用来干什么

如果没有动态代理,每次想调用远程服务

// 极其痛苦:每次调用都要手动写网络请求 byte[] requestData = serialize("getUserById", 1); byte[] responseData = HttpClient.send("http://192.168.1.10:8080/userService", requestData); User user = deserialize(responseData);

有了动态代理后,框架在底层帮你把这些脏活累活全封装了:

// 1. 动态代理在内存中生成一个 UserService 的实现类对象 UserService userService = (UserService) Proxy.newProxyInstance(...); // 2. 你像调用本地代码一样调用它 User user = userService.getUserById(1);

当你调用userService.getUserById(1)时,JVM 会自动把这个调用转发给动态代理的InvocationHandler(调用处理器)。在这个处理器里面,框架帮你做了 4 件事:

  1. 组装报文:把方法名getUserById、参数类型Long、参数值1封装成一个请求对象(如RpcRequest)。

  2. 寻找地址:去注册中心问一下UserService部署在哪台服务器上(服务发现/负载均衡)。

  3. 网络传输:把请求对象序列化成二进制流,通过 Netty 或 Socket 发送给提供者(Provider)。

  4. 获取结果:等待提供者返回结果,反序列化成User对象,并return给你的业务代码。

对于编写业务代码的你来说,你根本不知道中间发生了一次网络跨越,这就是所谓的“透明化调用”。


3. Java 中常见的动态代理实现
① JDK 动态代理(Dubbo 2.x 及大部分 RPC 默认首选)
  • 原理:利用 JDK 自带的java.lang.reflect.Proxy类,基于接口生成代理类。

  • 要求:目标类必须实现接口。因为 RPC 本身就是面向接口编程的(消费端只有接口),所以 JDK 动态代理完美契合 RPC 场景。

  • 核心代码结构

    public class RpcProxyHandler implements InvocationHandler { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 1. 在这里拦截到方法调用 System.out.println("准备调用远程方法: " + method.getName()); // 2. 在这里写你的网络发送逻辑 (Socket/Netty) // ... 发送并等待接收结果 ... return rpcResponse.getResult(); // 返回远程执行的结果 } }
② CGLIB / Javassist 动态代理
  • 原理:通过修改字节码,生成目标类的子类来做代理。

  • 要求:不需要实现接口,只要类和方法不是final的就行。

  • 在 Dubbo 中的应用:Dubbo 为了追求极致的性能,并没有单纯使用 JDK 自带的动态代理,而是默认使用了Javassist(或者在新版本中支持 ByteBuddy)来动态生成字节码。因为 Javassist 生成代理类的速度和执行效率比原生 JDK 更快。


Nginx的反向代理:

我们在苍穹外卖中学过一个词叫反向代理,可能大家都快忘了,包括我也是,只知道有这个词,但是具体的原理早就忘记了,这里顺便提一下。对比一下正向代理


它们最核心的区别在于:它们究竟是在给客户端当代理,还是在给服务端当代理。

  • 正向代理:代理的是客户端(传话筒)。服务端不知道真正发起请求的客户端是谁。

  • 反向代理:代理的是服务端(挡箭牌)。客户端不知道真正提供服务的是哪台后端服务器。


💡 正向代理

正向代理位于客户端和目标服务器之间。客户端非常清楚自己要访问谁,但由于某些原因(比如网络限制、隐藏身份),客户端无法直接访问,或者不想直接访问,于是找了一个代理服务器。

  • 经典场景:科学上网:国内上不了某些国外网站,你找了一台能上该网站的国外代理服务器。你把请求发给代理,代理帮你去该网站拿数据,再传回给你。

    • 公司内网行为管理:公司为了防止员工上班摸鱼,让所有人的电脑都通过一个正向代理服务器上网。这个代理服务器可以限制你不能访问游戏网站、购物网站。

  • 特点:

    1. 客户端必须进行配置(比如在浏览器里设置代理 IP 和端口)。

    2. 目标服务器只知道代理服务器的 IP,不知道真正的客户端是谁(保护了客户端隐私)。


💡 反向代理

反向代理同样位于客户端和目标服务器之间,但它是服务端的入口。客户端只知道反向代理服务器的地址(比如nginx.com),直接把请求发过去。反向代理收到后,在后台悄悄把请求分发给真正干活的业务服务器(如 Tomcat)。

  • 经典场景:

    • Nginx 负载均衡:你的网站访问量巨大,一台服务器扛不住。你用 Nginx 作为反向代理,后面挂了 10 台业务服务器。用户统一访问 Nginx,Nginx 自动把流量分发给这 10 台服务器。

    • 安全防护:保护后端服务器不被黑客直接攻击。黑客只能看到反向代理服务器的 IP,根本碰不到内网真正的数据库和核心业务服务器。

  • 特点:

    1. 客户端不需要做任何配置,用户完全感知不到反向代理的存在,觉得这就是普通的网站。

    2. 客户端只知道反向代理的 IP,不知道后端真正提供服务的是哪台服务器(保护了服务端隐私)。

为了方便记忆,我们可以通过下面这张表来做对比:

对比维度正向代理 (Forward Proxy)反向代理 (Reverse Proxy)
代理的对象客户端(替客户端发送请求)服务端(替服务端接收请求)
谁知道真相?客户端知道(它自己配置的代理)服务端知道(它自己架构的 Nginx)
谁被隐瞒了?目标服务端不知道真正的客户端是谁客户端不知道真正的业务服务器是谁
部署位置靠近客户端,通常在局域网内靠近服务端,通常在服务集群前端
核心作用突破访问限制(翻墙)、隐藏客户端、上网行为审计负载均衡、保障内网安全、缓存加速

类比一下:

  • 正向代理(找中介):你想租房,但你不想让房东知道你是谁(可能你是个明星)。于是你找了一个房屋中介(正向代理)替你去和房东谈。房东只看到了中介,合同也是和中介签的,房东不知道真正的租客是谁

  • 反向代理(找前台/物业):你去租某个大集团的公寓,你来到公寓的接待前台(反向代理)。你跟前台说“我要租房”,前台帮你办理了手续,并分给你 302 房间。你自始至终不知道这个公寓背后的老板/管家具体是谁,你只和前台打交道。


负载均衡:

这里想到了负载均衡,也顺便提一下吧,我看的项目也涉及到了:

  • Nginx 的反向代理负载均衡:就像一个商场的大门保安兼导购。外面的顾客(浏览器/App)想进来买东西,统一先找 Nginx,Nginx 看看哪个收银台(Web 服务器)人少,就把顾客带到哪个收银台。它面对的是外部世界

  • Dubbo 的负载均衡:就像商场内部的对讲机调度系统。收银台(Web 服务)在结账时,需要呼叫仓库(Order 服务)或者财务(User 服务)。收银员自己用对讲机问:“仓库 A 和仓库 B 哪个现在有空?”然后直接把请求发过去。它面对的是企业内部服务之间的沟通

深入细节
对比维度Nginx 反向代理负载均衡Dubbo 负载均衡
工作位置系统最前端(网关/边缘)。介于客户端(浏览器)和微服务群之间。系统内部(RPC 层)。介于内部服务 A 和服务 B 之间。
代理对象代理服务器。客户端只知道 Nginx 的 IP,不知道后端真实服务器的 IP。代理接口/方法。通过动态代理,让开发人员感觉在调用本地方法。
传输协议通常是HTTP / HTTPS通常是高性能的TCP 协议(如 Dubbo 协议、Triple 协议)。
负载均衡机制集中式(服务端负载均衡)。请求必须先经过 Nginx 这个“中间商”,由 Nginx 决定转发给谁。客户端负载均衡。消费者(Consumer)本地有服务列表,自己决定调用哪台 Provider,不经过任何中间商,直连过去。
服务发现需要手动在nginx.conf里配置upstream的 IP 列表(或者结合外部组件静态配置)。动态自动发现。通过注册中心(如 Nacos/ZooKeeper),服务上线下线自动感知,无需人工干预。

客户端负载均衡 vs 服务端负载均衡

Nginx:服务端负载均衡 (Server-side LB)

  • 浏览器发送请求给 Nginx。

  • Nginx 作为一个实体服务器架在中间,由它来选择一台后端的 Tomcat,并把请求转发(Forward)过去。

  • 缺点:Nginx 成了系统的单点瓶颈。如果流量巨大,Nginx 可能会扛不住;而且多了一次网络转发,会有微小的延迟。

Dubbo:客户端负载均衡 (Client-side LB)

  • Dubbo 的消费端(Consumer)在启动时,会去注册中心(Nacos)把服务提供者(Provider)的 IP 地址列表下载到本地缓存起来

  • 当代码调用userService.getUserById()时,Dubbo 在本地运行负载均衡算法(比如随机、轮询),直接选出一个 IP(比如192.168.1.5)。

  • 消费端直接发起 Socket 连接调用该 IP,中间没有任何性能损耗和多余的服务器中转

它们在真实架构中如何协同工作

在一套标准的微服务架构中,它们是各司其职的。流量的完整生命周期通常是这样的:

[ 用户的浏览器/手机App ] │ (HTTP 协议) ▼ ┌───────────────┐ │ Nginx 大门 │ <--- 服务端负载均衡:把外网 HTTP 流量分发给前端 Web 服务器 └───────┬───────┘ │ (HTTP 协议) ▼ ┌──────────────────────────────────────┐ │ Spring Boot 业务网关 / 前端应用集群 │ └───────┬──────────────────────────────┘ │ │ (Dubbo 动态代理拦截,在本地做负载均衡,转为 TCP 协议) ▼ ┌──────────────────────────────────────┐ │ 内部微服务 A (Consumer) │ └───────┬──────────────────────────────┘ │ (TCP 直连,不经过中间商) ▼ ┌──────────────────────────────────────┐ │ 内部微服务 B (Provider) │ └──────────────────────────────────────┘
  1. 第一步:用户在国内发起一个 HTTP 请求,首先到达Nginx。Nginx 看眼前有 3 台网关服务器,通过轮询,把请求丢给其中一台(Nginx 实现了外网到内网的分流)。

  2. 第二步:网关处理完基础验证后,需要调用内部的“商品服务”。此时网关作为 Dubbo 的Consumer,利用动态代理拦截了调用,并在本地计算出“商品服务-实例B”目前最空闲。

  3. 第三步:Consumer 直接通过 TCP 协议把请求发给“商品服务-实例B”(Dubbo 实现了内网微服务之间的高性能沟通)。

结语:

如果对你有帮助,请点赞,关注,收藏,你的支持就是我最大的鼓励!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/6/16 4:00:50

Python字符串拼接的工程实践:性能、安全与可读性权衡

1. 为什么“拼字符串”这件事&#xff0c;远比你想象的更值得深挖刚学 Python 的时候&#xff0c;我写的第一行能跑通的代码大概率是print("Hello" " World!")。那时候觉得&#xff0c;字符串拼接&#xff1f;不就是加号一敲&#xff0c;完事。直到我在一…

作者头像 李华
网站建设 2026/6/16 3:56:14

BetterNCM安装器终极指南:5分钟解锁网易云音乐插件系统

BetterNCM安装器终极指南&#xff1a;5分钟解锁网易云音乐插件系统 【免费下载链接】BetterNCM-Installer 一键安装 Better 系软件 项目地址: https://gitcode.com/gh_mirrors/be/BetterNCM-Installer BetterNCM Installer是一款专为网易云音乐PC版设计的跨平台插件管理…

作者头像 李华
网站建设 2026/6/16 3:55:08

误差传播原理与实操:从测量不确定度到g值计算

1. 项目概述&#xff1a;误差传播不是“算错”&#xff0c;而是对测量世界的真实敬畏你手头有一把游标卡尺&#xff0c;精度标称0.02 mm&#xff1b;你用它量了圆柱体的直径三次&#xff0c;读数分别是24.36 mm、24.38 mm、24.34 mm&#xff1b;又用另一台数字秒表测了单摆周期…

作者头像 李华
网站建设 2026/6/16 3:54:53

基于随机森林回归模型的加州房价预测与特征重要性分析

1.作者介绍 孟乐世&#xff0c;男&#xff0c;学院&#xff1a;西安工程大学电子信息学院 年级与身份&#xff1a;2025级研究生 研究方向&#xff1a;机器学习&#xff0c;数据挖掘与人工智能方向 电子邮件&#xff1a;1400980969qq.com 2.关于理论方面的介绍 2.1随机森林回归模…

作者头像 李华
网站建设 2026/6/16 3:45:51

从零构建个人技术IP:单片机内容创作与运营实战指南

1. 项目概述&#xff1a;从“辰哥单片机”看个人技术IP的构建与突围最近在技术圈子里&#xff0c;一个现象挺有意思&#xff1a;不少工程师朋友开始琢磨着打造自己的个人技术品牌&#xff0c;比如“辰哥单片机”。这听起来像是个人的绰号或品牌名&#xff0c;背后其实是一个典型…

作者头像 李华