什么是RPC框架?

RPC就是远程调用过程,实现各个服务间的通信,像调用本地服务一样。

RPC有什么优点?

- 提高服务的拓展性,解耦。
- 开发人员可以针对模块开发,互不影响。
- 提升系统的可维护性及高可用等。

基于socket思考:

- 怎么建立网络通信?

- 服务端怎么暴露服务并处理客户端请求?

- 客户端怎么去拿到服务并调用?

基于socket的通信的展示图: 流程 1---->2---->3---->4

上面的图形根据自己的理解画的--有些不足,但理解就行。

创建父工程order-server,子order-api,子order-producer,user-consumer的maven工程(也可以自己创建成独立的工程)。

创建过程省略....

 项目工程图如下:

order-api工程:

  • OrderApi类
package com.sqp.example.rpc;
/**
* @author sqp
* @date 2020/7/17 17:38
*/
public interface OrderApi {
String getOrderName(String name);
}
  • RpcRequest类
package com.sqp.example.rpc;
import java.io.Serializable;
import lombok.Getter;
import lombok.Setter;
/**
* @author sqp
* @date 2020/7/17 17:57
*/
@Setter
@Getter
public class RpcRequest implements Serializable { /**
* 方法名
*/
private String methodName; /**
* 方法的入参参数
*/
private Object[] args; /**
* 方法的路径
*/
private String className; /**
* 参数类型
*/
private Class[] types;

order-producer工程:

  • OrderController类
package com.sqp.example.rpc;
/**
* @author sqp
* @date 2020/7/17 17:39
*/
public class OrderController implements OrderApi {
@Override
public String getOrderName(String name) {
return "商品名称【"+name+"】";
}
}
  • ProxySocketServer类
package com.sqp.example.rpc;

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; /**
* 客户端监听
* @author sqp
* @date 2020/7/17 17:41
*/
public class ProxySocketServer { private final ExecutorService executorService = Executors.newCachedThreadPool(); public void publisher(Object service, int port){
ServerSocket serverSocket = null;
try {
// 创建一个serverSocket实例
serverSocket = new ServerSocket(port);
// 一直监听客户端的请求
while(true){
// socket是一个阻塞IO还有连接阻塞
Socket socket =serverSocket.accept();
// 使用线程处理阻塞IO,这样接收消息这边就不需要阻塞 ,但受限于线程
executorService.submit(new ProcessorHandler(socket,service));
}
} catch (IOException e) {
e.printStackTrace();
}finally{
if(serverSocket != null){
try {
serverSocket.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
  • ProcessorHandler类
package com.sqp.example.rpc;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.Socket; /**
* 线程处理类
* @author sqp
* @date 2020/7/17 17:46
*/
public class ProcessorHandler implements Runnable { /**
* 监听返回的socket连接
*/
private Socket socket; /**
* 发布的服务
*/
private Object service; /**
* 构造方法
* @param socket
* @param service
*/
public ProcessorHandler(Socket socket, Object service) {
this.socket =socket;
this.service=service;
} @Override
public void run() {
ObjectInputStream inputStream = null;
ObjectOutputStream outputStream = null;
try{
// 获取接收的数据
inputStream = new ObjectInputStream(socket.getInputStream());
// 反序列化
RpcRequest rpcRequest = (RpcRequest) inputStream.readObject();
// 反射调用
Object res = invoke(rpcRequest); outputStream = new ObjectOutputStream(socket.getOutputStream());
outputStream.writeObject(res);
outputStream.flush();
}catch(Exception e){
e.printStackTrace();
}finally {
if(outputStream!= null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
} /**
* 通过反射进行服务的调用
* @param rpcRequest
* @return*/
private Object invoke(RpcRequest rpcRequest) throws ClassNotFoundException, NoSuchMethodException,
InvocationTargetException, IllegalAccessException {
// 获取class
Class clazz = Class.forName(rpcRequest.getClassName());
// 找到目标的方法, 通过传入方法名称和参数类型
Method method = clazz.getMethod(rpcRequest.getMethodName(), rpcRequest.getTypes());
// 传入服务和参数
return method.invoke(service, rpcRequest.getArgs());
}
}
  • App类
package com.sqp.example.rpc;

public class App {
public static void main( String[] args ) {
// new一个实例
OrderApi orderApi = new OrderController();
ProxySocketServer proxySocketServer = new ProxySocketServer();
// 发布服务
proxySocketServer.publisher(orderApi, 8080);
}
}

user-consumer工程:

  • ProxySocketClient类
package com.sqp.example.rpc;
import java.lang.reflect.Proxy; /**
* @author sqp
* @date 2020/7/17 18:22
*/
public class ProxySocketClient {
   // 动态代理
public <T> T proxyClient(final Class<T> clazz, String host, int port){
return (T)Proxy.newProxyInstance(clazz.getClassLoader(), new Class<?>[] {clazz}, new RemInvocationHandler(host, port));
}
}
  • RemInvocationHandler类
package com.sqp.example.rpc;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method; /**
* @author sqp
* @date 2020/7/17 18:33
*/
public class RemInvocationHandler implements InvocationHandler {
private String host;
private int port; public RemInvocationHandler(String host, int port) {
this.host = host;
this.port = port;
} @Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
RpcNetTransport rpcNetTransport = new RpcNetTransport(host, port);
// 封装请求的参数,方法,类型,类名
RpcRequest rpcRequest = new RpcRequest();
rpcRequest.setMethodName(method.getName());
rpcRequest.setClassName(method.getDeclaringClass().getName());
rpcRequest.setArgs(args);
rpcRequest.setTypes(method.getParameterTypes());
return rpcNetTransport.send(rpcRequest);
}
}
  • RpcNetTransport类
package com.sqp.example.rpc;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket; /**
* @author sqp
* @date 2020/7/17 18:41
*/
public class RpcNetTransport {
private String host;
private int port; public RpcNetTransport(String host, int port) {
this.host = host;
this.port = port;
} public Socket newSocket() throws IOException {
return new Socket(host, port);
} public Object send(RpcRequest rpcRequest) {
ObjectInputStream inputStream = null;
ObjectOutputStream outputStream = null;
try {
// 建立连接
Socket socket = newSocket();
outputStream = new ObjectOutputStream(socket.getOutputStream());
outputStream.writeObject(rpcRequest);
outputStream.flush(); inputStream = new ObjectInputStream(socket.getInputStream());
return inputStream.readObject();
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}finally {
if (inputStream != null){
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(outputStream != null){
try {
outputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
}
  • App类
package com.sqp.example.rpc;

public class App {
public static void main( String[] args ) {
ProxySocketClient proxySocketClient = new ProxySocketClient();
OrderApi orderApi = proxySocketClient.proxyClient(OrderApi.class, "localhost", 8080);
System.out.println(orderApi.getOrderName("文具盒"));
}
}

测试结果:

 总结:

  • 客户端建立通信,实现动态代理去获取代理类,将请求的消息内容组装发送到网络通信中,因为只有通过远程通信才能进行服务的调用。
  • 服务端接收客户端请求连接是在serverSocket.accept(),然后对请求的数据进行处理,反序列化,通过拿到的内容进行反射调用。

  虽然这个简易版的RPC有很多局限性,大家不必过多纠结,但是符合我们RPC的通信原理。

  针对上面的可以改造成用spring的注解方式。下期我再修改,这里我感谢下我的MIC老师。

  写的不好大家别喷,大家多包涵,不过有错误希望大家指出来,一起学习,一起快乐。多手写代码记忆更深刻!

手写简易版RPC框架基于Socket的更多相关文章

  1. Netty核心组件介绍及手写简易版Tomcat

    Netty是什么: 异步事件驱动框架,用于快速开发高i性能服务端和客户端 封装了JDK底层BIO和NIO模型,提供高度可用的API 自带编码解码器解决拆包粘包问题,用户只用关心业务逻辑 精心设计的Re ...

  2. 手写mini版MVC框架

    目录 1, Springmvc基本原理流程 2,注解开发 编写测试代码: 目录结构: 3,编写自定义DispatcherServlet中的初始化流程: 3.1 加载配置文件 3.2 扫描相关的类,扫描 ...

  3. 手写简易版Promise

    实现一个简易版 Promise 在完成符合 Promise/A+ 规范的代码之前,我们可以先来实现一个简易版 Promise,因为在面试中,如果你能实现出一个简易版的 Promise 基本可以过关了. ...

  4. mybatis(八)手写简易版mybatis

    一.画出流程图 二.设计核心类 二.V1.0 的实现 创建一个全新的 maven 工程,命名为 mebatis,引入 mysql 的依赖. <dependency> <groupId ...

  5. C#基于Mongo的官方驱动手撸一个Super简易版MongoDB-ORM框架

    C#基于Mongo的官方驱动手撸一个简易版MongoDB-ORM框架 如题,在GitHub上找了一圈想找一个MongoDB的的ORM框架,未偿所愿,就去翻了翻官网(https://docs.mongo ...

  6. 手写简易SpringMVC

    手写简易SpringMVC 手写系列框架代码基于普通Maven构建,因此在手写SpringMVC的过程中,需要手动的集成Tomcat容器 必备知识: Servlet相关理解和使用,Maven,Java ...

  7. 手写简易的Mybatis

    手写简易的Mybatis 此篇文章用来记录今天花个五个小时写出来的简易版mybatis,主要实现了基于注解方式的增删查改,目前支持List,Object类型的查找,参数都是基于Map集合的,可以先看一 ...

  8. 【教程】手写简易web服务器

    package com.littlepage.testjdbc; import java.io.BufferedReader; import java.io.FileReader; import ja ...

  9. JDK动态代理深入理解分析并手写简易JDK动态代理(下)

    原文同步发表至个人博客[夜月归途] 原文链接:http://www.guitu18.com/se/java/2019-01-05/27.html 作者:夜月归途 出处:http://www.guitu ...

随机推荐

  1. MiniUI日期选择框MonthPicker英文修改为中文

    一.详细内容及解决方案 正常MIniUI的MonthPicker中的月份和星期默认是英文的,我百度搜索关于这个问题的博客少之又少,下面说下解决办法,非常简单. <input id="d ...

  2. C和指针课后练习题3

    1.在你的机器上,字符的范围有多大?有那些不同的整数类型以及他们的范围? C语言中数据输入输出格式: %d 有符号10进制整数%i 有符号10进制整数%o 无符号8进制整数%u 无符号10进制整数%x ...

  3. ccpc赛前记

    距离ccpc比赛还不到一个小时了,有些紧张又有些兴奋 作为留学选手参加国内的比赛感觉好像很奇怪?谁能想到一个疫情会让我拿ccpc结束自己的acm生涯(也许,谁知道呢) cf上蓝了 该准备gre了,目标 ...

  4. spring java config配置搭建工程资料收集(网文)

    https://blog.csdn.net/poorcoder_/article/details/70231779 https://github.com/lovelyCoder/springsecur ...

  5. moviepy音视频剪辑:视频变换处理与内容相关的变换函数headblur、mask_and/or、mirror_x/y、rotate、painting、scroll介绍

    一.引言 在<moviepy音视频剪辑:moviepy中的剪辑基类Clip详解>介绍了剪辑基类的fl.fl_time.fx方法,在<moviepy音视频剪辑:视频剪辑基类VideoC ...

  6. MapReduce简单执行过程及Wordcount案例

    MapReducer运行过程 以单词统计为案例. 假如现在文件中存在如下内容: aa bb aa cc dd aa 当然,这是小文件,如果文件大小较大时会将文件进行 "切片" ,此 ...

  7. sails框架结合mocha的测试环境搭建

    一.环境结构 1.首先最底层是操作系统 2.其次在操作系统之上是Node.js的运行环境,和Database 3.再之上就是sail和mocha框架 二.环境搭建 1.首先需要Node.js的运行环境 ...

  8. js监测页面是否切换到后台

    最近做个弹幕,用的是第三方的插件,在浏览器页面切换到后台,返回后发现数据有堆叠卡死的情况,如何解决这个问题?网上参考了些demo,大致可以实现 1.document.hidden( Boolean值, ...

  9. shell 编程 -- 条件判断

    1.按照文件类型进行判断(常用的)-b 判断该文件是否存在-d 判断是否存在,并且是否为目录(是目录就为真)-e 判断该文件是否存在(存在为真)-f 判断文件是否存在,并且是否为普通文件(是普通文件为 ...

  10. C++ 纯虚函数与抽象类——virtual 和纯说明符 “=0”

    什么时候使用纯虚函数 某些类,在现实角度和项目角度都不需要实例化(不需要创建它的对象),这个类中定义的某些成员函数只是为了提供一个形式上的接口,准备上子类来做具体的实现.此时这个方法就可以定义为&qu ...