笔者之前仅看过RPC这个单词,完全没有了解过,不想终于还是碰上了。起因:这边想提高并发量而去看kafka(最后折中使用了redis),其中kafka需要安装ZooKeeper,而ZooKeeper又与分布式相关,再继续就发现分布式的基础是RPC,于是写下了这篇博文

1. RPC

RPC(Remote Procedure Call)远程过程调用,即通过网络通信来调用远程计算机程序上的服务,而这个调用过程就像调用本地方法一样简单透明,并且不需要了解底层的网络技术协议。RPC采用C/S架构,发出请求的程序是Client,提供服务的则是Server,类似于Http请求与响应。简单总结就是:调用的方法实际在远程,而要像调用本地方法一样简单。

1)对于客户端的我:调用本地的一个方法(存根)就能获得服务。 这个存根是远程服务的一个代理,其底层如何实现,对于我来说是透明的。

2)对于远程服务器:监听是否有连接过来,来了就调用对应的方法并返回(服务器端较易理解)

其结构图如下:

  1. 用户调用一个 “本地” 函数,该函数调用客户句柄(远程服务在本地的存根)
  2. 客户句柄调用网络通信来访问远程程序
  3. 远程程序收到网络通信及相关信息就调用服务句柄
  4. 服务句柄就调用服务函数,函数结束逆序返回结果完成一次远程调用

2. 为什么需要RPC

当我们的业务量越来越庞大,垂直增加服务器的数量对提高性能的作用愈加微乎,此时难免会采用分布式的架构以便更好地提高性能。分布式架构的每个服务都是独立的部分,当需要完成某项业务且依赖不同的服务式时,这些服务就需要互相调用,此时服务之间的调用就需要一种高效的应用程序之间的通讯手段了,这就是PRC出现的原因

3. RPC实现要求

3.1 服务提供方

提供服务:实现所提供的服务

服务暴漏:仅仅实现了服务是不够的,还需要将提供的服务暴漏给外界,让外界知道有何,如何使用服务

3.2 服务调用方

远程代理对象:在调用本地方法时实际调用的是远程的方法,那么势必本地需要一个远程代理对象

总结:为了实现RPC需要有:通信模型(BIO、NIO),服务定位(IP、PORT),远程代理对象(远程服务的本地代理),序列化(网络传输转换成二进制)

4. 简单实现

其主要的对象有:服务端接口、服务端接口实现、服务暴漏、客户端接口(与服务端共享同个接口)、服务的引用

4.1 服务端接口

public interface Service {

	// 提供两个服务,说hello和整数相加
public String hello();
public int sum(int a, int b); }

4.2 服务端接口实现

public class ServiceImpl implements Service {

	@Override
public String hello() {
return "Hello World";
} @Override
public int sum(int a, int b) {
return a + b;
}
}

4.3 服务暴漏

public static void export(Object service, int port) {

	if (service == null || port <= 0 || port > 65535) {
throw new RuntimeException("Arguments error");
} System.out.println(service.getClass().getName() + ": " + port + "服务暴露"); new Thread( () -> { try (ServerSocket server = new ServerSocket(port);) { while(true){
try (
Socket socket = server.accept();
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
) { // 读取方法名
String methodName = in.readUTF(); // 读取参数类型
Class<?>[] parameterTypes = (Class<?>[])in.readObject(); // 读取参数值
Object[] arguments = (Object[])in.readObject(); // 获取方法
Method method = service.getClass().getMethod(methodName, parameterTypes); // 处理结果
Object result = method.invoke(service, arguments); // 写入结果
out.writeObject(result); } catch (Exception e) {
e.printStackTrace();
}
}
} catch (IOException e1) {
e1.printStackTrace();
}
}).start();
}

这个暴露的逻辑是服务端监听特定端口,等客户端发起请求后连接,然后通过Java的IO流获取方法名,参数等相关信息,最后通过反射实现方法的调用并将结果响应给客户端

4.4 客户端接口

public interface ClientService {

	// 提供两个服务,说hello和整数相加
public String hello();
public int sum(int a, int b); }

4.5 服务引用

public static <T>T refer(Class<T> interfaceClass, String host, int port){

	if(interfaceClass == null || !interfaceClass.isInterface() || host == null || port <= 0 || port > 65535){
throw new RuntimeException("Arguments error");
} System.out.println("正在调用远程服务"); @SuppressWarnings("unchecked")
T proxy = (T)Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[] {interfaceClass}, new InvocationHandler() { @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null;
try (
Socket socket = new Socket(host, port);
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
) { out.writeUTF(method.getName()); out.writeObject(method.getParameterTypes()); out.writeObject(args); result = in.readObject(); } catch (Exception e) {
e.printStackTrace();
}
return result;
}
});
return proxy;
}

而引用服务的逻辑是:创建Socket套接字连接,序列化相关请求信息发送给服务端,然后等待响应结果。其中透明调用是使用了动态代理

4.6 测试

public class Test {

	public static void main(String[] args) {

		// 暴露服务
ServiceImpl service = new ServiceImpl();
RPCFramework.export(service, 8080); // 调用服务
Client client = RPCFramework.refer(Client.class, "127.0.0.1", 8080);
int sum = client.sum(1, 2);
String rs = client.hello();
System.out.println("远程响应:" + sum);
System.out.println("远程响应:" + rs);
}
}
RPC.ServiceImpl:8080----- 服务暴露

正在调用远程服务
远程响应:3
远程响应:Hello World

5. 思考

5.1 为什么不用Http

RPC与具体协议无关,可基于Http、TCP,但因为TCP性能相对较好。Http属于应用层协议,TCP属于传输层协议,相对在底层少了一层封装,而且为了可靠传输而选择TCP不选择UDP

5.2 常用的RPC框架

Dubbo(阿里巴巴)、SpringCloud、RMI(JDK内置)

5.3 为什么要使用动态代理

因为要像本地调用一样,对于使用者来说是透明的。

Object result = XXX(String method, String host, int port)

上面这样其实也行,但并不能感觉到是调用本地方法一样,而且如果一个接口有多个方法的话,每调用一次方法就需要发送一次host / port

// 动态代理可以这样使用
ProxyObject.方法1
ProxyObject.方法2 // 没有使用动态代理则不人性化
XXX(String method1, String host, int port)
XXX(String method2, String host, int port)

5.4 为什么参数与参数类型需要分开传

为了方便分辨方法的重载,下面获取方法需要方法名和参数类型

service.getClass().getMethod(methodName, parameterTypes)

6. 优化

6.1 网络通信

上面事例中采用BIO形式,阻塞访问而导致并发量不高,可以用NIO代替

6.2 序列化

这里用了JDK原生方法只能序列化实现了Serializable接口的类,可以使用第三方的类库来提高性能

6.3 服务负载

服务的自动发现,客户端能动态感知服务端的变化,从实现热部署,可用定时轮询的方法,eg:ZooKeeper

6.4 Cluster

集群化,这样便可以提供负载均衡

6.5 请求与响应

请求与响应可以进行编码封装,而不是这样单独一个一个发送

参考Yanyan.He

RPC(简单实现)的更多相关文章

  1. 【转】RPC简单介绍

    RPC简单介绍 RPC 1. RPC是什么 RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络 ...

  2. 分布式系统间通信之RPC简单Demo(七)

    看似终点,回到起点.第一次接触C#,编写的第一个真正的Demo是基于Socket的简单通信,现在JAVA开始的第一个RPC的Demo也是基于Socket.. 下面通过java原生的序列化,Socket ...

  3. Hadoop之RPC简单使用(远程过程调用协议)

    一.RPC概述 RPC是指远程过程调用,也就是说两台不同的服务器(不受操作系统限制),一个应用部署在Linux-A上,一个应用部署在Windows-B或Linux-B上,若A想要调用B上的某个方法me ...

  4. Hadoop RPC简单例子

    jdk中已经提供了一个RPC框架-RMI,但是该PRC框架过于重量级并且可控之处比较少,所以Hadoop RPC实现了自定义的PRC框架. 同其他RPC框架一样,Hadoop RPC分为四个部分: ( ...

  5. 分布式通讯架构RPC简单实现

    什么是RPC: RPC(Remote Procedure Call,远程过程调用),一般用来实现部署在不同机器上的系统之间的方法调用,使得程序能够像访问本地系统资源一样,通过网络传输去访问远端系统资源 ...

  6. Rpc简单入门

    RPC这个概念大家都应该很熟悉了,这里不在累述了:使用场景可以参考这篇,本篇主要分享下Thrift和Grpc在.Net Core环境下使用入门.Thirft或者Grps 都支持跨语言.跨平台的Rpc框 ...

  7. RPC 简单小试

    由于经常被抓取文章内容,在此附上博客文章网址:,偶尔会更新某些出错的数据或文字,建议到我博客地址 :  --> 点击这里 RPC是指远程过程调用,也就是说两台服务器A,B,一个应用部署在A服务器 ...

  8. php实现rpc简单的方法

    rpc是啥这不多解释,php扩展实现rpc yar是鸟哥的写的扩展,实现简单的rpc.比较很好理解 windows安装yar http://pecl.php.net/package/yar/2.0.4 ...

  9. RPC简单设计方案

    服务端: 启动后,等待客户端发来信息,收到信息后进行处理,返回结果. 客户端: 主线程中发起一次RPC,那么就将信息封装成一个任务,提交到线程池,阻塞等待结果. 线程池中工作线程执行任务,发送信息,等 ...

  10. Hadoop RPC简单实例

    1.导入Hadoop-Common-2.6.0.jar导入工程,里面的IPC实现RPC需要的文件. 2.服务器端  (1)服务接口 package com.neu.rpc.server; /** * ...

随机推荐

  1. Selenium的简单使用

    selenium的使用对于新手来说十分友好,因为他避开了如今网络中的异步加载抓取的困扰,使得我们大部分的时间可以用于提取信息和存储中,下面就简单的列一些使用的代码,希望给同样初学的你有一定的参考价值. ...

  2. 官方文档中文版!Spring Cloud Stream 快速入门

    本文内容翻译自官方文档,spring-cloud-stream docs,对 Spring Cloud Stream的应用入门介绍. 一.Spring Cloud Stream 简介 官方定义 Spr ...

  3. Qt Installer Framework翻译(2)

    开始 Qt IFW作为Qt项目的一部分进行开发.该框架自身也使用Qt.然而,它能被用于安装所有类型的应用程序,包括(但不限于)使用Qt编译的. 支持的平台 已在下列平台中进行了测试: > Mic ...

  4. Java入门 - 语言基础 - 04.对象和类

    原文地址:http://www.work100.net/training/java-object-class.html 更多教程:光束云 - 免费课程 对象和类 序号 文内章节 视频 1 概述 2 J ...

  5. [bzoj2115] [洛谷P4151] [Wc2011] Xor

    Description Input 第一行包含两个整数N和 M, 表示该无向图中点的数目与边的数目. 接下来M 行描述 M 条边,每行三个整数Si,Ti ,Di,表示 Si 与Ti之间存在 一条权值为 ...

  6. Python3中的__new__方法以及继承不可变类型类的问题

    最近在学到Python中的__new__方法时被弄懵逼了,一开始实在是很难理解,有很多地方想不通(本人强迫症).最近自己慢慢思索得出了能说服自己的理解: 说__new__方法之前要先提到__init_ ...

  7. jmeter使用—响应断言

    断言的作用:一个HTTP请求发出去,怎么判断执行的任务是否成功呢?通过检查服务器响应数据,是否返回预期想要的数据,如果是,判断任务成功,反之任务失败. 1.添加断言:选中一个取样器,右键->添加 ...

  8. 一次完整的OCR实践记录

    一.任务介绍 这次的任务是对两百余张图片里面特定的编号进行识别,涉及保密的原因,这里就不能粘贴出具体的图片了,下面粘贴出一张类似需要识别的图片. 假如说我的数据源如上图所示,那么我需要做的工作就是将上 ...

  9. 虚拟机 ubuntu系统忘记密码如何进入

    重启 虚拟机 按住shift键 会出现下面的界面 按住‘e’进入下面的界面往下翻 更改红框勾到的字符串为:  rw init=/bin/bash 然后按F10进行引导 然后输入 :”passwd”  ...

  10. 手把手写框架入门(一) | 核心拦截器DispatchFilter实现

    前言 1Filter实现框架拦截 1配置自定义Filter 2创建一个Filter 3创建一个ActionMapping 4创建一个ActionMapper 5创建一个WebExecutor 6创建测 ...