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

RPC 原理:Dubbo为了偷懒而存在的中间商

Dubbo 的核心使命只有一个让程序员在调用远程方法时产生一种“我就在本机内存里调个函数”的错觉。为了实现这个巨大的谎言Dubbo 在底层搞了三场惊天动地的“魔术”。咱们这就钻进 JVM 和网卡的缝隙里看看它到底是怎么忽悠你的。动态代理、序列化和Netty一次“精心包装的跨国快递”。你消费者想从远方的仓库提供者拿东西但你不需要亲自去而是通过一个复杂的物流系统。下面咱们将结合你提到的动态代理、序列化和Netty三大核心技术为你深度拆解 Dubbo 的工作全流程。1. 动态代理看不见的“替身”“只会传话”的假对象你以为你拿到的是真佛真实的 Service 实现类其实你拿到的只是一个开光的牌位代理对象。—— 让你感觉不到是在远程调用当你写代码demoService.sayHello(world)时你以为你在调用本地的对象但实际上这个对象是 Dubbo 给你变出来的“幻影”。Java 是一门死板的语言你不能直接对着空气喊“喂那边的UserService给我查个用户” Java 要求你必须有一个对象才能点它的 method。原理Dubbo 在启动时利用Javassist(直接生成字节码比 JDK 原生反射快)或JDK 动态代理技术为远程接口生成了一本地代理对象Proxy当场捏造了一个实现了接口的“替身”。作用这个代理对象拥有和远程服务一模一样的接口。当你调用代理对象的sayHello方法时它不会执行真正的业务逻辑而是拦截这次调用。它会把你调用的方法名sayHello、参数类型String、参数值world以及版本号等信息打包成一个标准的请求对象RpcInvocation。底层视角这就像你给秘书代理下指令秘书记录下来准备发传真而不是自己去干活。当你调用userService.getUser(1001)时实际上发生了什么拦截这个“替身”根本没有业务逻辑它唯一的任务就是拦截你的调用。打包它迅速把你调用的方法名getUser、参数类型Long、参数值1001甚至连你是谁Request ID都记下来塞进一个叫RpcInvocation的盒子里。潜台词它在你耳边说“老板这活儿我干不了我得把这盒子发给远在千里之外的真正干活的人。”代码视角的真相你以为是user userService.getUser(1001); // 简单优雅实际上是// 伪代码这就是代理内部干的事 RpcInvocation invocation new RpcInvocation(getUser, new Object[]{1001}); Result result invoker.invoke(invocation); // 扔给网络层去跑2. 序列化数据的“压缩与装箱”把对象“挫骨扬灰”那个RpcInvocation盒子还在内存里它是 Java 对象有引用地址有堆内存结构。网线可不认识这些网线只认0和1。—— 把 Java 对象变成二进制流所以必须转换成二进制字节数组。这就是序列化的过程要把内存里的复杂对象图“拍扁”成二进制流默认协议Dubbo 默认使用Hessian2序列化协议。Hessian2 就像一个无情的粉碎机它遍历你的对象把字段名、字段值、类型信息全部转换成字节数组byte[]。Dubbo 协议头光有序列化数据还不够Dubbo 还要在这个字节流前面加个“快递单”。这就是 Dubbo 协议的 Header魔数0xdabb、请求 ID、序列化方式标识等。为什么是 Hessian2体积小比 Java 原生序列化小很多节省带宽。速度快编解码效率高。跨语言虽然 Dubbo 主要是 Java但 Hessian2 支持多语言交互。其他选择Dubbo 也支持 JSON、Protostuff、Kryo、Fastjson2 等你可以根据性能需求切换。流程代理生成的RpcInvocation对象进入序列化器。被转换成二进制的byte[]。加上 Dubbo 协议的魔数、标志位、请求 ID 等头信息封装成完整的数据包。底层真相你的对象在过安检。Hessian2 把它脱光了检查一遍然后压成一个压缩包贴上标签“这是第 10086 号请求去执行getUser”。3. 网络传输 (Netty)高速公路上的“异步飙车”数据包好了怎么发过去如果是传统的 BIOBlocking I/O那就是每来一个请求开一个线程线程等着结果回来。如果并发一高你的服务器线程池瞬间爆炸CPU 全花在上下文切换上啥也别想干—— 高效的数据搬运工。一旦数据打包完成就需要通过网络发送。Dubbo 底层默认使用Netty框架进行 NIO非阻塞 I/O通信。连接模型单一长连接HTTP/1.1 以前每次调用都要 TCP 握手、挥手。就像每次寄信都要重新修一条邮路蠢透了。Dubbo 默认采用单一长连接策略。Consumer 和 Provider 之间建立一条 TCP 连接后就死都不放手会一直保持心跳检测所有的请求都在这条管道里排队发送Pipeline后续的所有请求都通过这条连接发送。心跳检测为了防止这条管子太久没用被防火墙掐断Dubbo 会定期发个“心跳包”Ping/Pong告诉对方“我还活着别杀我。”优势避免了频繁建立和断开 TCP 连接三次握手带来的巨大开销非常适合内部微服务间高频、小数据的调用场景。I/O 模型NIO 异步非阻塞Dubbo 利用 Netty 的 Reactor 模型Boss 线程组负责连接Worker 线程组负责读写。包工头与搬运工BossGroup包工头只负责接客。客户端连进来Boss 说“好嘞你去找 Worker 玩吧。” Boss 不干活只管建立连接TCP 三次握手。WorkerGroup搬运工负责真正的读写。它们通过Selector多路复用器轮询成千上万个连接。关键点一个线程可以管理几万个连接。只有当连接真的有数据要读/写时线程才会介入。其他时间线程在睡觉或处理别的连接绝不空转。异步发送Consumer 发送请求后不会阻塞当前线程傻等结果而是立即返回一个Future对象然后继续处理其他任务。你的线程拿着 Future 继续干活或者挂起等待get()。Netty 线程在后台监听响应。一旦 Provider 的结果回来了Netty 根据 Request ID 找到对应的 Future把结果填进去唤醒你的线程。回调机制当 Provider 处理完返回结果时Netty 会通过回调通知 ConsumerConsumer 再唤醒等待的线程或直接处理结果。底层视角你在餐厅点菜。服务员Netty记下菜单请求给你个号牌Future然后立马去接待下一桌。厨房Provider做好了喊一声服务员再根据你的号牌把菜端给你。你不用站在厨房门口傻等。4. 服务端处理镜像般的逆向过程Provider 端的 Netty Server 接收到二进制数据包后开始逆向操作反序列化Netty 读取字节流利用 Hessian2 将二进制还原为RpcInvocation对象。定位服务根据请求中的接口名、版本、方法名找到对应的真实实现类Invoker。反射调用利用 Java 反射机制调用真实的业务方法sayHello(world)得到结果。响应将结果再次序列化通过 Netty 原路发回给 Consumer。全景流转一次 RPC 的“受难记”让我们把所有环节串起来看看一次调用在底层经历了什么消费者发起调用你调用了demoService.sayHello(dubbo)。代理层拦截Javassist 生成的代理类截获请求封装成RpcInvocation对象。集群容错Dubbo 看了看配置发现你有 3 个提供者。根据负载均衡策略比如随机挑了一个 IP192.168.1.20:20880。序列化Hessian2 把RpcInvocation变成二进制流加上 Dubbo 协议头魔数0xdabb。Netty 发送Netty 的 Channel 获取到这个字节流通过 TCP 长连接异步写入内核缓冲区推送到网卡。网络传输数据包经过交换机、路由器到达 Provider 机器。Provider 接收Provider 的 Netty Server 监听到数据包读取字节流。反序列化Hessian2 把二进制流还原成RpcInvocation。反射调用Dubbo 根据接口名和方法名找到本地真实的DemoServiceImpl利用反射或者生成的优化代码执行sayHello(dubbo)。原路返回结果被封装、序列化、通过网络发回 Consumer。唤醒Consumer 收到响应反序列化更新 Future 状态你的主线程从future.get()醒来拿到结果。全景流程图一次 RPC 的生命周期为了更直观地理解整合成一张全链路图服务提供者是如何启动并暴露服务的服务提供者Provider的启动与暴露本质上是一场在 Spring 容器生命周期内精心编排的“三幕剧”。它的核心目标是将你编写的 Java 接口变成一个可以通过网络被远程调用的服务。整个过程可以概括为Spring 容器启动 - Dubbo 组件扫描与初始化 - 服务参数确定 - 启动网络服务器 - 向注册中心注册。第一幕春雷惊蛰万物萌动 (Spring 容器启动)一切的起点都源于 Spring 容器的初始化。Dubbo 巧妙地利用了 Spring 的生命周期回调机制将自己的启动流程无缝嵌入其中。触发机关当你在 Spring Boot 应用主类上使用EnableDubbo或DubboComponentScan注解时就相当于按下了启动按钮。监听事件Dubbo 会注册一个核心的监听器——DubboDeployApplicationListener。这个监听器就像一个忠实的哨兵时刻等待着 Spring 容器发出的ContextRefreshedEvent事件。大幕拉开一旦 Spring 容器完成所有 Bean 的加载和刷新就会广播ContextRefreshedEvent事件。DubboDeployApplicationListener监听到该事件后便会触发 Dubbo 自身的部署启动器 (DefaultModuleDeployer.start())正式拉开了服务暴露的序幕。第二幕排兵布阵整装待发 (服务配置与封装)在这一阶段Dubbo 的主要任务是“清点人马”即扫描并封装所有需要暴露的服务。扫描服务实现类Dubbo 会根据配置的扫描路径如dubbo.scan.base-packages找到所有被DubboService注解标记的服务实现类。封装服务配置对于每一个找到的服务Dubbo 会创建一个ServiceConfig对象。这个对象是服务的“身份证”和“档案袋”它通过一套优先级规则配置中心 DubboService注解 application.yml配置文件收集并合并所有配置信息最终形成一个包含接口、实现类、版本、分组、超时时间等完整信息的配置对象。生成 Invoker紧接着Dubbo 会通过一个代理工厂 (ProxyFactory)将你的服务实现类和ServiceConfig中的元数据包装成一个Invoker对象。Invoker是 Dubbo 内部对可执行单元的抽象你可以把它理解为一个已经准备好、只待网络请求触发的“本地方法调用器”。第三幕开疆拓土扬名立万 (服务暴露与注册)这是最激动人心的一步服务将从内存中的对象转变为网络上可访问的实体。这个过程由Protocol协议层主导分为本地暴露和远程注册两个关键环节。环节一本地暴露 —— 启动网络服务器职责Protocol接口会调用其具体实现如DubboProtocol的export()方法。行动这个方法的核心任务是启动一个网络服务器来监听指定的端口默认是 20880。由于 Dubbo 默认使用 Netty 作为通信框架所以这里实际上是在启动一个 Netty Server。结果此时你的服务已经在本地 20880 端口上“安营扎寨”准备接收来自网络的二进制数据流了。同时Dubbo 会将之前生成的Invoker和一个代表服务的 URL 关联起来保存在一个本地的注册表 (ProviderConsumerRegTable) 中。这样当网络请求到达时服务器就能根据 URL 找到对应的Invoker来执行业务逻辑。环节二远程注册 —— 向世界宣告存在服务在本地启动后还需要让潜在的调用者Consumer知道它在哪里。这就是注册中心发挥作用的时候。Dubbo 3.0 在此引入了革命性的变化。Dubbo 2.x 的接口级注册方式服务提供者将自己的完整 URL包含 IP、端口、协议、方法等信息直接注册到注册中心如 Zookeeper的特定路径下例如/dubbo/com.example.DemoService/providers。痛点当一个应用提供几十个甚至上百个接口时注册中心会存储海量的节点数据。任何一次服务上下线都会导致大量数据的推送给注册中心带来巨大压力。Dubbo 3.0 的应用级注册 (核心变革)方式服务提供者不再关心自己有多少个接口而是以“应用”为单位进行注册。它会向注册中心注册一个包含应用名、IP、端口等实例信息的ServiceInstance对象存储路径类似于/services/your-application-name。优势无论应用内部有多少个服务接口它在注册中心只对应一个实例节点。这极大地减少了注册中心的数据量和变更频率提升了系统的可扩展性并能更好地与 Kubernetes、Spring Cloud 等生态互通。如何解决“消费者如何发现接口”的问题为了兼容应用级注册Dubbo 3.0 引入了两个配套机制接口-应用映射将接口名 - 应用名的映射关系注册到 Zookeeper 的/dubbo/mapping路径下。消费者通过这个映射就能知道想调用某个接口应该去找哪个应用。元数据中心服务提供者会将自己的详细接口定义方法、参数、返回值等作为元数据存储在独立的元数据中心可以是本地、Zookeeper 或 Nacos 等。消费者获取到应用实例后可以去元数据中心拉取详细的接口配置。双注册模式为了平滑迁移Dubbo 3.0 默认开启了“双注册”模式即同时将服务以接口级和应用级的形式注册到注册中心确保了与旧版本 Dubbo 消费者的兼容性。总结Dubbo 之所以快且强不是因为它发明了新的网络协议而是因为它把这些复杂的网络通信Netty、对象转换Serialization、服务发现Registry封装得滴水不漏让你产生了一种“分布式系统其实很简单”的错觉透明化用动态代理骗过了开发者让远程调用像本地调用一样自然。高效化用Netty NIO 长连接解决了高并发下的网络连接瓶颈。紧凑化用Hessian2 序列化保证了数据传输的体积最小、速度最快。

相关文章:

RPC 原理:Dubbo为了偷懒而存在的中间商

Dubbo 的核心使命只有一个:让程序员在调用远程方法时,产生一种“我就在本机内存里调个函数”的错觉。为了实现这个巨大的谎言,Dubbo 在底层搞了三场惊天动地的“魔术”。咱们这就钻进 JVM 和网卡的缝隙里,看看它到底是怎么忽悠你的…...

强化学习实战:从CartPole到Doom的策略梯度算法

1. 项目概述:当强化学习遇上经典控制问题 最近在复现经典论文时,我重新把玩了下OpenAI Gym里的CartPole环境,顺手用PyTorch实现了Policy Gradient算法。这个看似简单的平衡杆问题,其实包含了强化学习最核心的"试错学习"…...

2026年技术招聘实战:用智在记录语音转文字,搭建可追溯的面试全流程管理体系

一、引言 作为一家互联网公司的技术总监,我每年要带领团队面试近 200 位技术候选人,从校招应届生到资深架构师。在很长一段时间里,我们的技术招聘始终陷入几个无解的困境:面试官既要提问追问,又要手动记笔记&#xff…...

告别javax.servlet:SpringBoot3项目整合knife4j 4.1.0接口文档的完整配置流程

SpringBoot3技术栈迁移实战:从javax.servlet到knife4j 4.1.0的完整升级指南 当SpringBoot3正式发布时,许多开发者发现原先运行良好的Swagger文档突然报出java.lang.ClassNotFoundException: javax.servlet.http.HttpServletRequest错误。这背后是Java EE…...

DLSS Swapper终极指南:3步轻松升级游戏DLSS版本

DLSS Swapper终极指南:3步轻松升级游戏DLSS版本 【免费下载链接】dlss-swapper 项目地址: https://gitcode.com/GitHub_Trending/dl/dlss-swapper 你是否遇到过这样的情况?新游戏更新后帧率暴跌,或者某个DLSS版本导致画面闪烁&#x…...

如何3秒搞定LaTeX公式转换:Chrome扩展的终极解决方案

如何3秒搞定LaTeX公式转换:Chrome扩展的终极解决方案 【免费下载链接】LaTeX2Word-Equation Copy LaTeX Equations as Word Equations, a Chrome Extension 项目地址: https://gitcode.com/gh_mirrors/la/LaTeX2Word-Equation 还在为学术论文中的数学公式迁移…...

DolphinScheduler告警配置全解析:除了邮件钉钉,这些高级告警策略你试过吗?

DolphinScheduler告警配置全解析:除了邮件钉钉,这些高级告警策略你试过吗? 当你的数据流水线在深夜突然崩溃,而值班人员却因为告警信息淹没在群聊中未能及时响应——这种场景对每个数据工程师来说都是噩梦。DolphinScheduler作为企…...

poi-tl填坑实录:升级到1.10.x后,表格循环和复选框渲染策略变了怎么办?

poi-tl 1.10.x升级指南:表格循环与复选框渲染的深度适配方案 最近在重构一个企业级文档生成系统时,我遇到了一个典型的技术债问题——项目使用的poi-tl库长期停留在1.9.1版本,而新版本1.10.x对表格循环和复选框渲染机制做了重大调整。这导致原…...

QFT:颠覆传统文件传输的终极P2P解决方案

QFT:颠覆传统文件传输的终极P2P解决方案 【免费下载链接】qft Quick Peer-To-Peer UDP file transfer 项目地址: https://gitcode.com/gh_mirrors/qf/qft 在当今数据爆炸的时代,文件传输已成为日常工作和生活中不可或缺的一环。然而,传…...

【C++26反射元编程终极指南】:2026年生产级落地的5大核心模式与3个避坑红线

更多请点击: https://intelliparadigm.com 第一章:C26反射元编程的演进脉络与生产就绪定义 C26 正式将反射(Reflection)纳入核心语言特性,标志着元编程从模板元编程(TMP)和 constexpr 编程的“…...

零售店老板看过来:用微信小程序+Beacon信标,5步打造低成本室内导览和优惠券推送系统

零售店低成本智能升级:微信小程序Beacon信标实战指南 走进任何一家现代零售店,你是否注意到那些隐藏在货架角落、看似不起眼的小型设备?它们正悄然改变着顾客的购物体验和商家的运营效率。Beacon信标技术配合微信小程序,正在为中小…...

从Fritzing画图到Proteus仿真:手把手带你完成一个Arduino光控小项目的完整工作流

从Fritzing到Proteus:Arduino光控项目全流程实战指南 当你第一次尝试将创意转化为实际电路时,是否曾被不同工具间的切换困扰?Fritzing的直观与Proteus的专业如何无缝衔接?本文将带你完整走通从原型设计到仿真验证的全流程&#xf…...

Hitboxer:让键盘变身职业级游戏控制器的终极解决方案

Hitboxer:让键盘变身职业级游戏控制器的终极解决方案 【免费下载链接】socd Key remapper for epic gamers 项目地址: https://gitcode.com/gh_mirrors/so/socd 你是否曾在激烈的游戏对战中,因为键盘按键冲突而错失关键操作?当同时按下…...

从零构建人脸识别系统:OpenCV与dlib实战

1. 项目概述人脸识别系统是计算机视觉领域最具实用价值的技术之一。从手机解锁到机场安检,这项技术已经深入到我们生活的方方面面。但大多数人只把它当作黑箱使用,很少了解背后的实现原理。今天我想分享如何从零开始构建一个基础但完整的人脸识别系统&am…...

ExplorerPatcher终极指南:5个技巧让Windows 11界面回归经典,工作效率翻倍!

ExplorerPatcher终极指南:5个技巧让Windows 11界面回归经典,工作效率翻倍! 【免费下载链接】ExplorerPatcher This project aims to enhance the working environment on Windows 项目地址: https://gitcode.com/GitHub_Trending/ex/Explor…...

如何永久保存微信聊天记录?WeChatMsg终极免费工具完全指南

如何永久保存微信聊天记录?WeChatMsg终极免费工具完全指南 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trending/we/W…...

深度强化学习在游戏AI中的核心技术与实战应用

1. 深度强化学习:游戏AI的进化之路2013年,当DeepMind首次展示AI在雅达利游戏中的表现时,整个科技界都为之震动。那台机器在《打砖块》《太空侵略者》等经典游戏中的表现,不仅超越了人类玩家,更开创了AI研究的新范式。作…...

机器学习求职必备:7大实战项目经验解析

1. 为什么机器学习项目经验是求职关键?2026年的机器学习岗位竞争会比现在更加激烈。根据行业招聘数据显示,超过83%的机器学习岗位要求候选人具备实际项目经验,而不仅仅是理论知识。我在过去五年面试过数百名机器学习工程师,发现那…...

Android Studio布局编辑器偷懒技巧:用Guideline和圆形定位快速实现复杂UI

Android Studio布局编辑器进阶技巧:Guideline与圆形定位实战指南 在移动应用界面设计中,非标准布局往往需要开发者投入大量时间计算坐标位置。传统解决方案要么依赖嵌套视图组导致性能损耗,要么需要手动编写复杂的定位逻辑。ConstraintLayout…...

Python FastAPI 并发请求调度机制

Python FastAPI 并发请求调度机制解析 在当今高并发的互联网应用中,如何高效处理大量请求成为开发者关注的焦点。Python FastAPI凭借其异步特性和高性能,成为构建现代API的热门选择。其并发请求调度机制尤其值得深入探讨,它能显著提升应用的…...

用《权游》学Prolog:逻辑编程实战指南

1. 项目概述:当逻辑编程遇上奇幻史诗去年冬天重刷《权力的游戏》时,我突发奇想:能不能用这部剧的复杂人物关系作为案例库,边追剧边学习Prolog?这个诞生于1972年的逻辑编程语言,在处理家族谱系、联盟关系这类…...

Windows风扇控制终极方案:3个实用技巧让电脑静音又高效

Windows风扇控制终极方案:3个实用技巧让电脑静音又高效 【免费下载链接】FanControl.Releases This is the release repository for Fan Control, a highly customizable fan controlling software for Windows. 项目地址: https://gitcode.com/GitHub_Trending/f…...

CRMEB商城v5.2.2漏洞实战:手把手教你复现SQL注入(附POC脚本)

CRMEB商城SQL注入漏洞深度解析与实战复现指南 漏洞背景与影响范围 CRMEB作为国内广泛使用的开源电商系统,其5.2.2版本中曝光的SQL注入漏洞(CVE-2024-36837)引起了安全社区的广泛关注。这个漏洞位于ProductController.php文件的getProductList…...

Cadence Virtuoso实战:手把手教你搞定PLL相位噪声的HBnoise仿真与结果解读

Cadence Virtuoso实战:PLL相位噪声HBnoise仿真全流程解析 在射频集成电路设计中,相位噪声是衡量振荡器性能的关键指标之一。对于锁相环(PLL)中的压控振荡器(VCO)模块,准确的相位噪声仿真能够帮助工程师预测系统整体性能,识别潜在…...

告别手速焦虑:用Python自动化脚本轻松搞定大麦网抢票

告别手速焦虑:用Python自动化脚本轻松搞定大麦网抢票 【免费下载链接】Autoticket 大麦网自动抢票工具 项目地址: https://gitcode.com/gh_mirrors/au/Autoticket 你是否也曾经历过这样的场景:心仪的演唱会门票开售瞬间,手指在鼠标上疯…...

金蝶云星空V8.X私有云部署,如何快速自查CommonFileServer任意文件读取漏洞?

金蝶云星空V8.X私有云安全自查指南:CommonFileServer漏洞深度防御 当企业IT团队在凌晨三点收到安全漏洞预警邮件时,心跳加速的不只是值班工程师。作为金蝶云星空系统的守护者,您需要的是可立即执行的精准自查方案,而非泛泛而谈的…...

ZYNQ PS端串口死活收不到数据?先别急着改代码,检查一下BANK电压吧!

ZYNQ PS端串口通信故障排查:从BANK电压到系统级调试思维 调试ZYNQ平台时,PS端串口突然"罢工"只能发送无法接收数据?这种看似软件问题的现象,往往隐藏着硬件配置的玄机。本文将带您深入BANK电压配置的底层逻辑&#xff0…...

如何高效管理个人数字记忆:WeChatMsg聊天记录分析与归档实用指南

如何高效管理个人数字记忆:WeChatMsg聊天记录分析与归档实用指南 【免费下载链接】WeChatMsg 提取微信聊天记录,将其导出成HTML、Word、CSV文档永久保存,对聊天记录进行分析生成年度聊天报告 项目地址: https://gitcode.com/GitHub_Trendin…...

从相机标定到BEV地图:LSS算法在自动驾驶感知中的完整数据处理链路拆解

从相机标定到BEV地图:LSS算法在自动驾驶感知中的完整数据处理链路拆解 当六路环视相机的图像数据涌入自动驾驶系统时,算法需要像人类驾驶员一样理解周围环境的立体空间关系。传统2D感知方案存在视角遮挡、尺度变化等固有缺陷,而BEV&#xff0…...

专利价值量化分析:基于机器学习实现专利权利要求广度评估,提升知识产权管理效率70%

专利价值量化分析:基于机器学习实现专利权利要求广度评估,提升知识产权管理效率70% 【免费下载链接】patents-public-data Patent analysis using the Google Patents Public Datasets on BigQuery 项目地址: https://gitcode.com/gh_mirrors/pa/paten…...