1.1 RPC 是什么

定义:RPC(Remote Procedure Call Protocol)——远程过程调用协议 ,RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层应用层RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

我的理解:与其说把RPC 看作是一种协议,倒不如把 它看作是一种 客户机/服务器交互的模式,但是 RPC一定是基于 TCP 或者 其他 通信协议的

下面我们来看一下一个RPC调用的流程涉及哪些通信细节:

  • 服务消费方(client)调用以本地调用方式调用服务;(1)
  • client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;(2)
  • client stub找到服务地址,并将消息发送到服务端;(3)
  • server stub收到消息后进行解码;(4)
  • server stub根据解码结果调用本地的服务;(5)
  • 本地服务执行并将结果返回给server stub;(6)
  • server stub将返回结果打包成消息并发送至消费方;(7)
  • client stub接收到消息,并进行解码;(8)
  • 服务消费方得到最终结果。(9)

RPC的目标就是要2~8这些步骤都封装起来,让用户对这些细节透明。

1.2 手动实现

1.2.1 先做一个空接口实现序列化接口

public interface IRpcService extends Serializable{}

1.2.2 做一个需要被远程调用的接口 以及对应的接口实现类

public interface IHelloService extends IRpcService{String sayHi(String name,String message);
}
public class HelloServiceImpl implements IHelloService{private static final long serialVersionUID = 146468468464364698L;@Overridepublic String sayHi(String name, String message) {return new StringBuilder().append("hi~!").append(",").append(message).toString();}}

1.2.3 需要写一个服务端,主要的作用 是进行服务注册(接口注册) 以及 接收客户端的调用参数 执行调用请求 返回结果

注:这个地方 我没有采用dom4j 解析配置文件的形式 进行接口注册 有时间的朋友可以多加一层

public interface Server {//Socket端口int PORT = 8080;//启动服务端void start() throws IOException;//停止服务端void stop();/*** 服务注册* -- serviceInterface 对外暴露接口* -- 内部实现类*/void regist(Class<? extends IRpcService> serviceInterface,Class<? extends IRpcService> impl);}
public class ServerCenter implements Server{/**线程池 接收客户端调用**/private static ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 20, 200, TimeUnit.MILLISECONDS,new ArrayBlockingQueue<Runnable>(10));/**服务注册缓存**/public static final Map<String,Class<?>> serviceRegistry = new HashMap<>();/*** 启动服务*/@Overridepublic void start() throws IOException {ServerSocket server = new ServerSocket();server.bind(new InetSocketAddress(PORT));try {while(true){executor.execute(new ServiceTask(server.accept()));}} finally {server.close();}}/*** 停止服务*/@Overridepublic void stop() {executor.shutdown();}/*** 注册服务*/@Overridepublic void regist(Class<? extends IRpcService> serviceInterface, Class<? extends IRpcService> impl) {serviceRegistry.put(serviceInterface.getName(), impl);}private static class ServiceTask implements Runnable{Socket client = null;public ServiceTask(Socket client) {this.client = client;}@Overridepublic void run() {ObjectInputStream input = null;ObjectOutputStream output = null;try {input = new ObjectInputStream(client.getInputStream());String serviceName = input.readUTF();String methodName = input.readUTF();Class<?>[] parameterTypes = (Class<?>[]) input.readObject();Object[] arguments = (Object[]) input.readObject();Class<?> serviceClass = serviceRegistry.get(serviceName);if(serviceClass == null){throw new ClassNotFoundException(serviceName + "not found");}Method method = serviceClass.getMethod(methodName, parameterTypes);Object result = method.invoke(serviceClass.newInstance(), arguments);//将执行结果反序列化 通过socket返给客户端output = new ObjectOutputStream(client.getOutputStream());output.writeObject(result);} catch (Exception e) {e.printStackTrace();} finally {if(input != null){try {input.close();} catch (IOException e) {e.printStackTrace();}}if(output != null){try {output.close();} catch (IOException e) {e.printStackTrace();}}if(client != null){try {client.close();} catch (IOException e) {e.printStackTrace();}}}}}public static void main(String[] args) throws Exception {ServerCenter center = new ServerCenter();center.regist(IHelloService.class,new HelloServiceImpl().getClass());center.start();}
}

1.2.4 写一个客户端,用动态代理 获取被代理接口的 各种参数 传输给 服务端,接收返回结果,打印到控制台

public class Client {@SuppressWarnings("unchecked")public static <T extends IRpcService>T getRemoteProxyObj(final Class<? extends IRpcService> serviceInterface,final InetSocketAddress addr){return (T) Proxy.newProxyInstance(serviceInterface.getClassLoader(), new Class<?>[]{serviceInterface}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {Socket socket = null;ObjectOutputStream output = null;ObjectInputStream input = null;try {//1.创建Socket客户端,根据指定地址连接远程服务提供者socket = new Socket();socket.connect(addr);//2.将远程服务调用所需的接口类、方法名、参数列表等编码后发送给服务提供者output = new ObjectOutputStream(socket.getOutputStream());output.writeUTF(serviceInterface.getName());output.writeUTF(method.getName());output.writeObject(method.getParameterTypes());output.writeObject(args);//3.同步阻塞等待服务器返回应答 获取应答后返回input = new ObjectInputStream(socket.getInputStream());return input.readObject();} finally{if(socket != null){socket.close();}if(output != null){output.close();}if(input != null){input.close();}}}});}}

1.2.5 测试

注:测试之前 需要开启服务端

public class RpcTest {public static void main(String[] args) throws IOException {IHelloService service  = Client.getRemoteProxyObj(IHelloService.class, new InetSocketAddress(8080));System.out.println(service.sayHi("张三", "新年快乐!"));}
}

就这样我们实现了一个简陋的RPC

本文意在通过实现简单的RPC,去真正意义上对RPC框架的实现原理有初步的了解,而不是人云亦云。

此RPC实现有诸多缺点,但是 我们只要明白RPC的基座 其他的RPC框架只是完善基座以及扩展而已 。

rpc简单实现git代码地址

转载于:https://www.cnblogs.com/dwlovelife/p/10320505.html

手把手教你写一个RPC相关推荐

  1. 手把手教你写一个生成对抗网络

    成对抗网络代码全解析, 详细代码解析(TensorFlow, numpy, matplotlib, scipy) 那么,什么是 GANs? 用 Ian Goodfellow 自己的话来说: " ...

  2. python k线合成_手把手教你写一个Python版的K线合成函数

    手把手教你写一个Python版的K线合成函数 在编写.使用策略时,经常会使用一些不常用的K线周期数据.然而交易所.数据源又没有提供这些周期的数据.只能通过使用已有周期的数据进行合成.合成算法已经有一个 ...

  3. 手把手教你写一个spring IOC容器

    本文分享自华为云社区<手把手教你写一个spring IOC容器>,原文作者:技术火炬手. spring框架的基础核心和起点毫无疑问就是IOC,IOC作为spring容器提供的核心技术,成功 ...

  4. 手把手教你写一个Matlab App(一)

    对于传统工科的学生用的最多的编程软件应该就是matlab,其集成度高,计算能力强,容易上手,颇受大众青睐.今天挖的这个新坑,主要是分享用matlab app designer设计GUI界面的一些方法和 ...

  5. 后端思维篇:手把手教你写一个并行调用模板

    前言 36个设计接口的锦囊中,也提到一个知识点:就是使用并行调用优化接口.所以接下来呢,就快马加鞭写第二篇:手把手教你写一个并行调用模板~ 一个串行调用的例子(App首页信息查询) Completio ...

  6. 从原理到实现丨手把手教你写一个线程池丨源码分析丨线程池内部组成及优化

    人人都能学会的线程池 手写完整版 1. 线程池的使用场景 2. 线程池的内部组成 3. 线程池优化 [项目实战]从原理到实现丨手把手教你写一个线程池丨源码分析丨线程池内部组成及优化 内容包括:C/C+ ...

  7. 手把手教你写一个中文聊天机器人

    本文来自作者 赵英俊(Enjoy) 在 GitChat 上分享 「手把手教你写一个中文聊天机器人」,「阅读原文」查看交流实录. 「文末高能」 编辑 | 哈比 一.前言 发布这篇 Chat 的初衷是想和 ...

  8. 手把手教你写一个没有服务器的颜值打分小程序,可直接上线

    小程序现在可以说非常火爆了,流量入口非常多.尤其是出了流量主功能以后,普通开发者也能在自己的个人小程序里植入官方广告来获取收入.程序员想赚点外快再合适不过了.今天教大家写一个颜值打分的小程序,利用现成 ...

  9. 手把手教你写一个手势密码解锁View(GesturePasswordView)

    相信大家在很多的app肯定看到过手势密码解锁View,但是大家有没有想过怎么实现这样一个View,哈,接下来,小编手把手教大家教写一个GesturePasswordView. 先看一张效果图 要实现这 ...

  10. 手把手教你写一个串口调试助手

    硬件攻城狮 2022-05-20 14:05 发表于广东 果果小师弟. 电子信息工程硕士在读,分享单片机.嵌入式linux.物联网等知识,致力于打造最硬核的嵌入式技术公众号. 摘要:前段时间发布了一个 ...

最新文章

  1. oracle数据库性能awr,常见问题:如何使用AWR报告来诊断数据库性能问题
  2. java保护型数据成员_Java基础知识笔记第四章:类和对象
  3. 分布式事务、分布式锁、分布式session
  4. weboffice 应用
  5. zend studio for Eclipse注册码及汉化
  6. gitHub网站上常见英语翻译
  7. 单目可见光静默活体检测 Binary or Auxiliary Supervision论文解读
  8. NoSQL数据库知识
  9. GitHub+JenKins集成Python自动化测试脚本
  10. 手把手教你处理单张百度热力图
  11. 工作记录 io流写入linux文件
  12. 蓝桥杯2020第二场JAVA C真题
  13. 科学网肖波:海外博后申请的一点经验与看法
  14. 计算机教学在教学方面的作用,浅谈信息技术在教学中的重要作用
  15. Android之调节屏幕亮度
  16. 编译原理 词法分析 算符优先分析法
  17. python print 函数出错
  18. python版飞机大战及码源
  19. HTML转为office WORD方案
  20. 哈密顿算子与梯度、散度、旋度

热门文章

  1. FR的控件CSS样式
  2. python数据分析需要哪些库-用python进行数据分析的五个最常用库
  3. larvel mysql count,Laravel带条件查询多个count
  4. python批量命名文件_用python实现批量重命名文件的代码
  5. 1、win10下的Docker+Redis 的下载及简单使用
  6. REST another WebService???
  7. 《http权威指南》读书笔记 六
  8. C# 传递给C++函数的结构体没有对齐的问题
  9. 【CVPR2005】梯度方向直方图(Histogram of Oriented Gradients,简称HOG)
  10. 使scp不用输入密码