• 一、前言
  • 二、原理
  • 三、前置基础
  • 四、举例说明
  • 五、总结

一、前言

上一节 手撕RPC系列(1)—最原始的RPC通俗理解 中讲了一个最最简单的rpc思想的例子。那种方法的缺陷太多,平常写代码一般不会那样去写,今天我们在之前的基础上稍微进一步演进,引入stub的概念,stub在rpc里面是代理的意思,是个约定俗成的东西,所以不叫proxy,知道是这么个东西就行了。代理是干嘛的?我要做的事丢给别人去做,那个人就叫代理

二、原理

为了一步一步的理解rpc,我今天不打算实现一个完全标准版的rpc,上图是是标准版,今天要讲的主要区别在红色的部分,只在client端加了stub,如下图

在上一章节的例子中,客户端要实现一堆通信的逻辑,耦合度太高,我能不能客户端只负责调用接口,中间的网络细节不用去管呢?红色部分的stub就是干这活的。

三、前置基础

反射:这个例子会比上一章节进阶一些的是,我们上一章是写死了只有findStudentByid一个方法的调用,假如Client端要调用的Server端接口有很多个呢?Client端可以通过socket把要调用的接口名传给Server端,Server端再通过接口名反射去调用已实现的接口方法。

动态代理:Client已经把调用Server端的具体细节交给了stub,需要stub动态代理生成了一个Client要调用的接口类,通过这个类去实现跟Server端的交互,让Client端实现只负责接口方法的调用,不用去关心一堆巴拉巴拉的网络细节

不具备上面两个基础的童靴不建议往下读

四、举例说明

代码结构:

common类跟上一章节一样:

Student.java #实体类,作为数据的传输和传入的对象

StudentService.java #接口类,定义一个findStudentByid接口,给客户端调用

StudentServiceImpl.java #接口实现类,实现findStudentByid接口,必须在服务端实现

rpc类:

Client.java #客户端类

Server.java #服务端类

Stub.java #代理类

以下是代码:

Student.java

package rpc2.common;public class Student {int id;String name;public Student(int id, String name) {this.id = id;this.name = name;}public int getId() {return id;}public void setId(int id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}@Overridepublic String toString() {return "Student{" +"id=" + id +", name='" + name + '\'' +'}';}
}

StudentService.java

package rpc2.common;public interface StudentService  {public Student findStudentByid(int id);
}

StudentServiceImpl.java

package rpc2.common;public class StudentServiceImpl implements StudentService {@Overridepublic Student findStudentByid(int id) {return new Student(id,"zhangsan");}
}

主要的实现逻辑看下面的代码,对应上面图片描述的3大类,Client,Stub,Server

Client.java

package rpc2.rpc;import rpc2.common.StudentService;public class Client {public static void main(String[] args) {// Client这里不用关注一堆网络交互的细节,直接调用Stub产生的代理对象的方法,既可完成整个链路的调用StudentService service = Stub.getStub();System.out.println(service.findStudentByid(123));}
}

Stub.java

package rpc2.rpc;import rpc2.common.Student;
import rpc2.common.StudentService;import java.io.DataInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;/*** 但是这里仅仅实现了findStudentByid的方法代理,如果要实现其他方法的代理该怎么做呢?* 这里就要从协议层做出改进** 服务器端也要做出对应处理*/public class Stub {public static StudentService getStub() {InvocationHandler h = new InvocationHandler() {/*** proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0,代理了StudentService类* method:我们所要调用某个对象真实的方法的Method对象,也就相当于StudentService的findStudentByid方法* args:指代代理对象方法传递的参数,也就是Client传递的123*/@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Socket socket = new Socket("127.0.0.1", 8888);ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());// 获取Client要调用的方法名String methodName = method.getName();// 获取Client要调用的方法传入的参数类型Class[] parametersTypes = method.getParameterTypes();// 把方法名写出去oos.writeUTF(methodName);// 把参数类型写出去oos.writeObject(parametersTypes);// 把参数写出去oos.writeObject(args);oos.flush();DataInputStream dis = new DataInputStream(socket.getInputStream());int id = dis.readInt();String name = dis.readUTF();Student student = new Student(id, name);oos.close();socket.close();return student;}};// 动态代理产生一个实现了StudentService的接口的代理对象,参数1是类加载器,参数2传入被代理的接口类,参数3是InvocationHandler,被代理时反射调用的方法,也就是Stub给Client端处理的一堆巴拉巴拉的细节Object o = Proxy.newProxyInstance(StudentService.class.getClassLoader(), new Class[]{StudentService.class}, h);// 返回实现StudentService接口类的代理对象return (StudentService)o;}}

Server.java

package rpc2.rpc;
import rpc2.common.Student;
import rpc2.common.StudentService;
import rpc2.common.StudentServiceImpl;import java.io.*;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;public class Server {public static void main(String[] args) throws Exception {ServerSocket serverSocket = new ServerSocket(8888);while (true) {Socket socket = serverSocket.accept();process(socket);socket.close();}}private static void process(Socket socket) throws Exception {InputStream in = socket.getInputStream();OutputStream out = socket.getOutputStream();ObjectInputStream oos = new ObjectInputStream(in);DataOutputStream dos = new DataOutputStream(out);// 读取Client端通过socket传来的方法名String methodName = oos.readUTF();// 读取Client端通过socket传来的参数类型,考虑到同样的方法名但是不同的入参类型的情况,也就是方法的重载Class[] parameterTypes = (Class[])oos.readObject();// 读取Client端通过socket传来的参数值Object[] args = (Object[])oos.readObject();// 实例化StudentServiceImplStudentService service = new StudentServiceImpl();// 考虑到Client端可能调用Server端的多个方法,不仅仅是findStudentByid一个方法的情况,这时可以根据方法名和参数类型获取Method对象供后面反射调用StudentServiceImpl实现的方法Method method = service.getClass().getMethod(methodName, parameterTypes);// 反射调用方法查询出结果Student user = (Student)method.invoke(service, args);// 把结果写回给客户端dos.writeInt(user.getId());dos.writeUTF(user.getName());dos.flush();}
}

最后先运行Server.java,再运行Client.java,输出:

Student{id=123, name='zhangsan'}

五、总结

跟着注释阅读很容易把代码读懂,是不是很简单呢?终于离珠峰又更近了一步,但是这个版本也依旧是个阉割版的rpc。问题一:我们在调用过程中,Student对象里面的所有字段细节,万一增减字段呢?可不可以做到不用关系对象的细节呢?问题二:Server端逻辑依旧很复杂,能不能做到像Client一样也通过Stub去处理细节问题呢?欲知答案,请持续关注本系列。

上一节:手撕RPC系列(1)—最原始的RPC通俗理解

手撕RPC系列(2)—客户端基于stub动态代理的RPC相关推荐

  1. 手撕yolo3系列——详解yolo3整体网络代码(详细注释)

    完整代码百度云直达链接(包含预训练权重)(小白注释) https://pan.baidu.com/s/1US6e93OaCYOghmF21v0UIA 提取码:z8at 参考链接 [注]代码是大神的代码 ...

  2. netcore 中的动态代理与RPC实现(微服务专题)

    一.关于RPC的调用 1. 调用者(客户端Client)以本地调用的方式发起调用: 2. Client stub(客户端存根)收到调用后,负责将被调用的方法名.参数等打包编码成特定格式的能进行网络传输 ...

  3. 两万字吐血总结,代理模式及手写实现动态代理(aop原理,基于jdk动态代理)

    代理模式及手写实现动态代理 一.代理模式 1. 定义 2. 示例 (1)静态代理 (2)动态代理 3. 通用类图 4. 代理模式的优点 二.jdk动态代理实现原理 1. jdk动态代理源码分析(通过该 ...

  4. Java 动态代理及 RPC 框架介绍

    所谓动态代理,指的是语言提供的一种语法,能够将对对象中不同方法的调用重定向到一个统一的处理函数中来. python重写__getattr__函数能够做到这一点,就连世界上最好的语言也提供称为魔术方法的 ...

  5. 手撕算法系列之冒泡排序

    冒泡排序–python def Bubblesort(list1):length = len(list1)for i in length:for j in range(0,length-i-1):if ...

  6. 手撕前端面试题【javascript~ 列表动态渲染、无重复数组、数组排序、新数组、创建数组、深浅拷贝、内存泄露等】

    前端的那些基本标签

  7. 深度学习之手撕深度神经网络DNN代码(基于numpy)

    声明 1)本文仅供学术交流,非商用.所以每一部分具体的参考资料并没有详细对应.如果某部分不小心侵犯了大家的利益,还望海涵,并联系博主删除. 2)博主才疏学浅,文中如有不当之处,请各位指出,共同进步,谢 ...

  8. 手撕系列-AdaBoost

    简介: 上次我们介绍过Adaboost是一种迭代算法,其核心思想是针对同一个训练集训练不同的分类器(弱分类器),然后把这些弱分类器集合起来,构成一个更强的最终分类器(强分类器). 本次将通过一个简单示 ...

  9. 【专栏】RPC系列(实战)-负重前行的“动态代理”

    关注公众号:离心计划,一起离开地球表面  [RPC系列合集] [专栏]RPC系列(理论)-夜的第一章 [专栏]RPC系列(理论)-协议与序列化 [专栏]RPC系列(理论)-动态代理 [专栏]RPC系列 ...

最新文章

  1. 2022-2028年中国二次供水设备行业研究及前瞻分析报告
  2. Python20-Day02
  3. AI一分钟 | 雷军家小米音箱开始控制电视啦,有望成为智能家居中心;国民公公王健林也玩AI,线上线下一起整
  4. 重庆理工大学计算机学院学概率论,重庆理工大学概率论试卷及答案.doc
  5. 基础理论:给定离散概率表求分布函数
  6. 正则表达式获取一个文本域中每一行的值并且去掉前后空格
  7. windbg命令集合
  8. 哈佛推出这款PyTorch工具包,10行代码训练“AI药神”模型
  9. 12.04 ubuntu 安装微软雅黑的字体
  10. 一个循环递归遍历问题
  11. 232串口线或者串口的简单测试
  12. [MSSQL]【SQL SERVER 2005+版本行转列示例】(2012年1-8月水源槑党25强榜单新鲜出炉)
  13. JDK源码解析之HashSet
  14. SQL Server学习笔记6——LTRIM()函数(去掉空格)
  15. 30多种磁芯优缺点对比
  16. Python数据分析培训班介绍
  17. Centos7新开一个终端并在新终端中执行命令
  18. 深圳宝安周边公司出行团建户外一日游
  19. 又1家互联网公司倒闭了,失业来得太突然…
  20. mask_rcnn keras源码跟读3)配置文件

热门文章

  1. 回文数-第14届蓝桥杯Scratch选拔赛真题
  2. Hypermesh前处理+Abaqus有限元分析
  3. 武汉理工大学计算机学院体测通知,谁知道武汉理工大学主考就业技能班 金年的新生是在那报......
  4. 基于javaEE的在线汽车租赁管理系统ssm
  5. Synopsys初体验——软件安装篇
  6. python批量运行cmd_python 之多主机批量执行命令
  7. 独立显卡、集成显卡和CPU核心显卡有什么区别?
  8. 车联网解决方案、汽车CANBUS总线开发、车联网OBD、车载终端车联网平台研发设计、
  9. 讲完社区故事,网易云音乐这次要靠AI上位?
  10. 【ANSYS 学习笔记】Case04_Basic DC Conduction Analysis