分布式调用 - 服务间的远程调用RPC

文章目录

  • 导图
  • Pre
  • RPC 概述
  • RPC 调用过程
  • RPC 动态代理
    • 1. 接口定义 (`SeverProvider`)
    • 2. 实现类 (`ServerProviderImpl`)
    • 3. 动态代理类 (`DynamicProxy`)
    • 4. 客户端 (`Client`)
    • 5. 代码工作流程
    • 6. 总结和注意点
    • 7. 结果输出
    • 8. 小结
  • RPC 序列化
  • 协议编码
  • 网络传输

在这里插入图片描述

导图

在这里插入图片描述

服务和应用的调用基于场景的不同会分为几种情况

  • 系统外的客户端调用系统内的服务时需要通过反向代理和负载均衡的方式; ------负载均衡

  • 系统架构内部服务之间的调用需要通过 API 网关; ------API 网关

  • 服务之间的互相感知需要用到服务注册与发现; ------服务注册与发现

  • 服务之间的通信会使用 RPC 架构,RPC 的核心原理以及 Netty 的最佳实践 ------服务间的远程调用


Pre

分布式调用 - 那些关于负载均衡的一二事儿

分布式调用 - API网关和服务注册发现


RPC 概述

无论 API 网关,还是服务注册和发现,都在探讨服务与服务如何发现对方、如何选择正确路径进行调用,描述的是服务之间的关系。厘清关系后,我们再来谈谈服务之间的调用是如何完成的。

在分布式系统中,应用或者服务会被部署到不同的服务器和网络环境中,特别是在有微服务的情况下,应用被拆分为很多个服务,每个服务都有可能依赖其他服务。

假设客户端调用下单服务时,还会调用商品查询服务、扣减库存服务、订单更新服务,如果这三个服务分别对应三个数据库,那么一次客户端请求就会引发 6 次调用,要是这些服务或者数据库都部署在不同的服务器或者网络节点,这 6 次调用就会引发 6 次网络请求。因此,分布式部署方式在提高系统性能和可用性的前提下,对网络调用效率也发起了挑战。

为了面对这种挑战,需要选择合适的网络模型,对传输的数据包进行有效的序列化,调整网络参数优化网络传输性能。为了做到以上几点我们需要引入 RPC,下面就来介绍 RPC 是如何解决服务之间网络传输问题


RPC 调用过程

RPC 是 Remote Procedure Call(远程过程调用)的缩写,该技术可以让一台服务器上的服务通过网络调用另一台服务器上的服务,简单来说就是让不同网络节点上的服务相互调用。因此 RPC 框架会封装网络调用的细节,让调用远程服务看起来像调用本地服务一样简单。

由于微服务架构的兴起,RPC 的概念得到广泛应用,在消息队列、分布式缓存、分布式数据库等多个领域都有用到。可以将 RPC 理解为连接两个城市的高速公路,让车辆能够在城市之间自由通行。由于 RPC 屏蔽了远程调用和本地调用的区别,因此程序开发者无须过多关注网络通信,可以把更多精力放到业务逻辑的开发上.

在这里插入图片描述

上图描述了服务调用的过程,这里涉及左侧的服务调用方和右侧的服务提供方。既然是服务的调用过程,就存在请求过程和响应过程,这两部分用虚线圈出来了。

  • 从图左侧的服务调用方开始,利用“动态代理”方式向服务提供方发起调用,这里会制定服务、接口、方法以及输入的参数;
  • 将这些信息打包好之后进行“序列化”操作,由于 RPC 是基于 TCP 进行传输的,因此在网络传输中使用的数据必须是二进制形式,序列化操作就是将请求数据转换为二进制,以便网络传输;
  • 打好二进制包后,需要对信息进行说明,比如协议标识、数据大小、请求类型等,这个过程叫作“协议编码”,说白了就是对数据包进行描述,并告诉数据接收方数据包有多大、要发送到什么地方去。

至此,数据发送的准备工作就完成了,数据包会通过“网络传输”到达服务提供方

  • 服务提供方接收到数据包以后,先进行“协议解码”,并对解码后的数据“反序列化”,然后通过“反射执行”获取由动态代理封装好的请求
  • 此时随着箭头到了图的最右边,顺着向下的箭头,服务提供方开始“处理请求”,处理完后就要发送响应信息给服务调用方了,之后的发送过程和服务调用方发送请求的过程是一致的,只是方向相反,依次为序列化→协议编码→网络传输→协议解码→反序列化→接收响应”。

以上便是整个 RPC 调用的请求、响应流程。

分析上述的 RPC 调用流程后,发现无论是服务调用方发送请求,还是服务提供方发送响应,有几个步骤都是必不可少的,分别为动态代理、序列化、协议编码和网络传输


RPC 动态代理

服务调用方访问服务提供方的过程是一个 RPC 调用。作为服务调用方的客户端通过一个接口访问作为服务提供方的服务端,这个接口决定了访问方法和传入参数,可以告诉客户端如何调用服务端,实际的程序运行也就是接口实现是在客户端进行的。

RPC 会通过动态代理机制,为客户端请求生成一个代理类,在项目中调用接口时绑定对应的代理类,之后当调用接口时,会被代理类拦截,在代理类里加入远程调用逻辑即可调用远程服务端。

来看个例子:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;// 1. 接口命名规范
public interface ServerProvider {  void sayHello(String str);
}// 2. 实现类命名规范
public class ServerProviderImpl implements ServerProvider {@Overridepublic void sayHello(String str) {System.out.println("Hello, " + str);}
}// 3. 动态代理类,遵循标准命名和格式
public class DynamicProxy implements InvocationHandler {private Object realObject;// 构造器传入实际对象public DynamicProxy(Object object) {this.realObject = object;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 这里可以在方法调用前后添加日志或其他操作System.out.println("Before invoking method: " + method.getName());Object result = method.invoke(realObject, args);System.out.println("After invoking method: " + method.getName());return result;}
}// 4. 客户端测试代码
public class Client {public static void main(String[] args) {// 创建真实对象ServerProvider realServerProvider = new ServerProviderImpl();// 创建动态代理处理器InvocationHandler handler = new DynamicProxy(realServerProvider);// 通过Proxy创建代理实例ServerProvider serverProvider = (ServerProvider) Proxy.newProxyInstance(handler.getClass().getClassLoader(),realServerProvider.getClass().getInterfaces(),handler);// 使用代理对象调用方法serverProvider.sayHello("world");}
}

1. 接口定义 (SeverProvider)

public interface ServerProvider {public void sayHello(String str);
}
  • ServerProvider 是一个服务提供者接口,定义了一个 sayHello 方法,接收一个 String 类型的参数 str。这个接口是服务端暴露的远程调用接口。

2. 实现类 (ServerProviderImpl)

public class ServerProviderImpl implements SeverProvider {@Overridepublic void sayHello(String str) {System.out.println("Hello" + str);}
}
  • ServerProviderImplSeverProvider 接口的实现类,实现了 sayHello 方法,并打印出传入的 str 参数。

3. 动态代理类 (DynamicProxy)

public class DynamicProxy implements InvocationHandler {private Object realObject;  public DynamicProxy(Object object) {this.realObject = object;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {method.invoke(realObject, args);return null;}
}
  • DynamicProxy 类实现了 InvocationHandler 接口,后者是 Java 动态代理的核心接口。
  • DynamicProxy 的构造方法接收一个 Object 类型的 realObject 参数,表示需要代理的真实对象(在这里是 ServerProviderImpl)。
  • invoke 方法负责处理所有通过代理对象调用的方法。当代理对象的方法被调用时,invoke 方法会被触发,它通过反射(method.invoke(realObject, args))调用真实对象的方法。

4. 客户端 (Client)

public class Client {public static void main(String[] args) {SeverProvider realSeverProvider = new ServerProviderImpl();InvocationHandler handler = new DynamicProxy(realSeverProvider);SeverProvider severProvider = (SeverProvider)Proxy.newProxyInstance(handler.getClass().getClassLoader(),realSeverProvider.getClass().getInterfaces(),handler);severProvider.sayHello("world");}
}
  • 步骤解析
    • realSeverProvider 创建了真实的服务端对象 ServerProviderImpl,这是一个实现了 ServerProvider 接口的具体实例。
    • handlerDynamicProxy 的实例,它被用来处理代理对象的所有方法调用。
    • 通过 Proxy.newProxyInstance 创建动态代理对象。这个方法需要三个参数:
      1. ClassLoaderhandler.getClass().getClassLoader() 用来加载代理类。
      2. InterfacesrealSeverProvider.getClass().getInterfaces() 获取真实对象实现的接口,这里是 SeverProvider
      3. InvocationHandlerhandler,指定动态代理的处理逻辑。
    • 最后,通过代理对象 severProvider 调用 sayHello("world"),实际上调用的是 DynamicProxy 中的 invoke 方法,间接执行 ServerProviderImplsayHello 方法。

5. 代码工作流程

  1. 客户端 通过 Proxy.newProxyInstance 创建一个代理对象,并指定代理类的 InvocationHandler
  2. 当客户端调用代理对象的方法时,代理对象的 invoke 方法被触发。
  3. invoke 方法中,调用 method.invoke(realObject, args) 来执行真实对象的方法。

6. 总结和注意点

  • 动态代理:动态代理的核心在于 InvocationHandler 接口,代理类不会直接实现接口中的方法,而是通过反射机制调用真实对象的方法,这样就实现了方法调用的“拦截”。
  • 代理机制:这段代码是一个简单的 Java 动态代理实现,通过代理对象使得客户端可以间接调用服务端实现的远程方法。

7. 结果输出

Before invoking method: sayHello
Hello, world
After invoking method: sayHello

这段代码展示了 Java 动态代理的基本使用,它使得客户端能够通过代理对象调用服务端的实际方法,同时提供了灵活的拦截和增强功能。

8. 小结

在客户端和服务端之间加入了一层动态代理,这个代理用来代理服务端接口。客户端只需要知道调用服务端接口的方法名字和输入参数就可以了,并不需要知道具体的实现过程。在实际的 RPC 调用过程中,在客户端生成需要调用的服务端接口实例,将它丢给代理类,当代理类将这些信息传到服务端以后再执行。因此,RPC 动态代理是对客户端的调用和服务端的执行进行了解耦,目的是让客户端像调用本地方法一样调用远程服务。


RPC 序列化


协议编码


网络传输

在这里插入图片描述

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

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

相关文章

vue3项目搭建-4-正式启动项目,git管理

安装插件: npm install vue router npm install eslint 完成目录: 需要添置文件夹: apis -> api接口 composables -> 组合函数 directives -> 全局指令 styles -> 全局样式 utils -> 工具函数 git 管理: …

GPON原理

GPON网络架构 对于OLT来说,它就相当于一个指挥官,它指挥PON口下的ONU在指定的时间段内发送数据以及发起测距过程等 而ONU则是一个士兵,按照OLT的指挥做出相应 而ODN它主要就是提供一个传输通道,主要包括分光器和光纤组成 对于PO…

SJYP 24冬季系列 FROZEN CHARISMA发布

近日,女装品牌SJYP 2024年冬季系列——FROZEN CHARISMA已正式发布,展现了更加干练的法式风格。此次新品发布不仅延续了SJYP一贯的强烈设计风格和个性时尚,更融入了法式风情的干练元素,为消费者带来了一场视觉与穿着的双重盛宴。  …

【H2O2|全栈】JS进阶知识(十一)axios入门

目录 前言 开篇语 准备工作 获取 介绍 使用 结束语 前言 开篇语 本系列博客主要分享JavaScript的进阶语法知识,本期主要对axios进行基本的了解。 与基础部分的语法相比,ES6的语法进行了一些更加严谨的约束和优化,因此,在…

git使用文档手册

创建一个本地代码工作空间,比如这里使用test目录作为工作目录 针对仓库地址 http://192.168.31.125:9557/poxiaoai-crm/project-crm.git。 1. 安装 Git 确保您的系统已经安装了 Git。如果未安装,请根据操作系统访问 Git 官网 下载并安装。 验证安装 …

数据结构——排序算法第二幕(交换排序:冒泡排序、快速排序(三种版本) 归并排序:归并排序(分治))超详细!!!!

文章目录 前言一、交换排序1.1 冒泡排序1.2 快速排序1.2.1 hoare版本 快排1.2.2 挖坑法 快排1.2.3 lomuto前后指针 快排 二、归并排序总结 前言 继上篇学习了排序的前面两个部分:直接插入排序和选择排序 今天我们来学习排序中常用的交换排序以及非常稳定的归并排序 快排可是有多…

11.26深度学习_神经网络-数据处理

一、深度学习概述 1. 什么是深度学习 ​ 人工智能、机器学习和深度学习之间的关系: ​ 机器学习是实现人工智能的一种途径,深度学习是机器学习的子集,区别如下: ​ 传统机器学习算法依赖人工设计特征、提取特征,而深…

数据结构 (13)串的应用举例

前言 数据结构中的串(String),也称为字符串,是一种常见且重要的数据结构,在计算机科学中被广泛应用于各种场景。 一、文本处理 文本编辑器:在文本编辑器中,字符串被用来表示和存储用户输入的文本…

PointNet++论文复现

✨✨ 欢迎大家来访Srlua的博文(づ ̄3 ̄)づ╭❤~✨✨ 🌟🌟 欢迎各位亲爱的读者,感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢,在这里我会分享我的知识和经验。&am…

时间的礼物:如何珍视每一刻

《时间的礼物:如何珍视每一刻》 夫时间者,宇宙之精髓,生命之经纬,悄无声息而流转不息,如织锦之细线,串联古今,贯穿万物。 人生短暂,犹如白驹过隙,倏忽而逝,…

NVIDIA /CUDA 里面的clock rate详细介绍

本文主要介绍: cuda中的时钟频率具体有哪些?clock rate怎么调节? cuda中可以通过nvml 函数或者命令来调整时钟频率(clock rate) 介绍 命令行 nvdia-smi -q -i 0 可以查询device相关参数,可以用后面的命…

扫振牙刷设计思路以及技术解析

市面上目前常见的就两种:扫振牙刷和超声波牙刷 为了防水,表面还涂上了一层防水漆 一开始的电池管理芯片,可以让充电更加均衡。 如TP4056 第一阶段以恒流充电;当电压达到预定值时转入第二阶段进行恒压充电,此时电流逐…

电磁继电器

它的控制原理很简单,当我们给它的线圈接电,这个线圈就有了磁性,它上面的衔铁就会被吸引,这样小灯泡就会点亮 继电器于MOS管的差别在于,继电器可以很轻松的胜任高电压、大电流的场合 我们从外壳上可以看到 30VDC&#x…

【Jenkins】自动化部署 maven 项目笔记

文章目录 前言1. Jenkins 新增 Maven 项目2. Jenkins 配置 Github 信息3. Jenkins 清理 Workspace4. Jenkins 配置 后置Shell脚本后记 前言 目标:自动化部署自己的github项目 过程:jenkins 配置、 shell 脚本积累 相关连接 Jenkins 官方 docker 指导d…

LangGraph中的State管理

本教程将介绍如何使用LangGraph库构建和测试状态图。我们将通过一系列示例代码,逐步解释程序的运行逻辑。 1. 基本状态图构建 首先,我们定义一个状态图的基本结构和节点。 定义状态类 from langgraph.graph import StateGraph, START, END from typi…

Excel的图表使用和导出准备

目的 导出Excel图表是很多软件要求的功能之一,那如何导出Excel图表呢?或者说如何使用Excel图表。 一种方法是软件生成图片,然后把图片写到Excel上,这种方式,因为格式种种原因,导出的图片不漂亮&#xff0c…

vue实现滚动条滑动到底部分页调取后端接口加载数据

一、案例效果 二、前提条件 接口返回数据 三、案例代码 子组件 const $emit defineEmits([cloneItem, updateList]);const props defineProps({rightList: {type: Array,},chartTableData: {type: Array as () > ChartListType[],},deleteChartInfo: {type: Object,}…

Ubuntu中使用多版本的GCC

我的系统中已经安装了GCC11.4,在安装cuda时出现以下错误提示: 意思是当前的GCC版本过高,要在保留GCC11.4的同时安装GCC9并可以切换,可以通过以下步骤实现: 步骤 1: 安装 GCC 9 sudo apt-get update sudo apt-get ins…

【Android】RecyclerView回收复用机制

概述 RecyclerView 是 Android 中用于高效显示大量数据的视图组件&#xff0c;它是 ListView 的升级版本&#xff0c;支持更灵活的布局和功能。 我们创建一个RecyclerView的Adapter&#xff1a; public class MyRecyclerView extends RecyclerView.Adapter<MyRecyclerVie…

Kotlin DSL Gradle 指南

本文是关于 Kotlin DSL Gradle 的指南&#xff08;上篇&#xff09;&#xff0c;介绍了 Gradle 作为 Android 开发构建工具的作用及优势&#xff0c;包括初始配置、生命周期、依赖管理、Task 相关内容。如 Task 的创建、自定义、各种方法和属性&#xff0c;以及文件操作等&…