手写RPC第一版

2024-06-13 10:32
文章标签 rpc 手写 第一版

本文主要是介绍手写RPC第一版,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

手写RPC第一版

  • 前言
    • 创建项目
    • goods-api
    • goods-provider
    • user-server
    • 最后

前言

通过学习,将自己掌握的的技术记录下来,以便后面学习。

创建项目

创建两个项目,一个用户系统user-server,一个商品系统goods-server。系统goods-server系统有两个Module分别是goods-api(商品系统的接口模块),goods-provider(提供具体的服务)。user-server和goods-server分别部署到不同的服务器上,模拟goods-server服务中提供一个IGoodsService接口,接口中描述两个方法,queryGoodsList(),queryGoodsById(String id)。user-server服务通过socket远程调用GoodsServer中的api。
接下来我们先写goods-server这个服务

goods-api

创建一个RpcRequest类,该类作为user-server和goods-server两个服务间通过Socket远程调用时传输的数据,所以实现Serializable,序列化及反序列化。

import java.io.Serializable;
import java.util.Arrays;/*** 用于socket传输数据,所以要实现Serializable接口。*/
public class RpcRequest implements Serializable {// 类名称private String className;// 方法名称private String methodName;// 方法参数private Object[] args;// 参数类型private Class[] types;public String getClassName() {return className;}public void setClassName(String className) {this.className = className;}public String getMethodName() {return methodName;}public void setMethodName(String methodName) {this.methodName = methodName;}public Object[] getArgs() {return args;}public void setArgs(Object[] args) {this.args = args;}public Class[] getTypes() {return types;}public void setTypes(Class[] types) {this.types = types;}@Overridepublic String toString() {return "RpcRequest{" +"className='" + className + '\'' +", methodName='" + methodName + '\'' +", args=" + Arrays.toString(args) +", types=" + Arrays.toString(types) +'}';}
}

创建一个接口,用于客户端调用。

/*** 用户客户端调用的api*/
public interface IGoodsService {String queryGoodsList();String queryGoodsById(String id);
}

goods-provider

创建一个GoodsServiceImpl,实现IGoodsService中的方法

public class GoodsServiceImpl implements IGoodsService {@Overridepublic String queryGoodsList() {return "EXECUTE QUERY_LIST METHOD";}@Overridepublic String queryGoodsById(String id) {return "EXECUTE QUERY_INFO METHOD";}
}

创建一个RpcProxyServer类,用来提供发布服务的方法publisher(),其中正常的Socket通信,会产生IO阻塞(客户端A调用服务,处于阻塞状态时,客户端B是无法调用服务的),所以第一版本采用线程池的方式,去提高服务端处理多客户端请求的性能。ProcessHandler这个类用来处理服务端接收到的请求以及返回客户端的数据。

public class RpcProxyServer {private final ExecutorService executorService = Executors.newCachedThreadPool();public void publisher(Object service, int port) {ServerSocket serverSocket = null;try {serverSocket = new ServerSocket(port);// 阻塞while (true) {// 获得Socket对象Socket socket = serverSocket.accept();// 通过线程池提供服务处理客户端远程调用的能力executorService.execute(new ProcessHandler(service, socket));}} catch (IOException e) {e.printStackTrace();}}
}

ProcessHandler 类在run方法中通过ObjectInputStream获取客户端请求的参数RpcRequest,在通过invoke方法反射调用服务端的服务,在通过ObjectOutputStream将result返回给客户端。

public class ProcessHandler implements Runnable {private Object service;private Socket socket;public ProcessHandler(Object service, Socket socket) {this.service = service;this.socket = socket;}@Overridepublic void run() {ObjectInputStream inputStream = null;ObjectOutputStream outputStream = null;try {// 读取客户端请求过来的数据inputStream = new ObjectInputStream(socket.getInputStream());RpcRequest request = (RpcRequest) inputStream.readObject();// 根据客户端请求参数调用服务端具体的服务Object result = invoke(request);System.out.println("服务端处理的结果:" + result);// 将服务端返回的数据写会给客户端outputStream = new ObjectOutputStream(socket.getOutputStream());outputStream.writeObject(result);outputStream.flush();} catch (Exception 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();}}}}// 根据客户端请求参数调用服务端具体的服务private Object invoke(RpcRequest request) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {// 获取请求的类名String className = request.getClassName();// 获得具体的类Class<?> clazz = Class.forName(className);// 通过方法名称及参数类型,获得具体的方法Method method = clazz.getMethod(request.getMethodName(), request.getTypes());// 通过反射调用方法return method.invoke(service, request.getArgs());}
}

user-server

接下来我们先写user-server这个服务
首先写一个动态代理类,提供clientProxy方法,该方法需要传入客户端Socket连接的Ip,和端口,以及要代理哪个类

public class RpcProxyClient {public <T> T clientProxy(final Class<T> interfaceCls, final String host, final int port) {return (T) Proxy.newProxyInstance(interfaceCls.getClassLoader(), new Class<?>[] {interfaceCls}, new RemoteInvocationHandler(host, port));}
}

代理对象的具体操作在RemoteInvocationHandler中去实现。首先组装RpcRequest参数,然后通过RpcNetTransport远程调用服务端

public class RemoteInvocationHandler implements InvocationHandler {private String host;private int port;public RemoteInvocationHandler(String host, int port) {this.host = host;this.port = port;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {// 组装RpcRequestRpcRequest request = new RpcRequest();request.setClassName(method.getDeclaringClass().getName());request.setMethodName(method.getName());request.setArgs(args);request.setTypes(method.getParameterTypes());// 调用远程服务RpcNetTransport netTransport = new RpcNetTransport(host, port);Object result = netTransport.send(request);return result;}
}

RpcNetTransport 提供一个newSocket()方法,通过host,port获取Socket连接。然后提供send()方法,远程调用服务端。

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 {Socket socket = new Socket(host, port);return socket;}public Object send(RpcRequest request) {ObjectOutputStream outputStream = null;ObjectInputStream inputStream = null;try {// 获得Socket连接Socket socket = newSocket();// 向服务器写数据outputStream = new ObjectOutputStream(socket.getOutputStream());outputStream.writeObject(request);outputStream.flush();// 读取服务器返回数据inputStream = new ObjectInputStream(socket.getInputStream());Object result = inputStream.readObject();return result;} catch (IOException e) {e.printStackTrace();} catch (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;}
}

服务端测试类

public class App {public static void main( String[] args ) {IGoodsService goodsService = new GoodsServiceImpl();RpcProxyServer server = new RpcProxyServer();server.publisher(goodsService, 8080);}
}

客户端测试类

public class App {public static void main( String[] args ) {RpcProxyClient client = new RpcProxyClient();IGoodsService goodsService = client.clientProxy(IGoodsService.class, "localhost", 8080);System.out.println(goodsService.queryGoodsById("test"));System.out.println(goodsService.queryGoodsList());System.out.println(goodsService.queryGoodsList());System.out.println(goodsService.queryGoodsList());System.out.println(goodsService.queryGoodsList());System.out.println(goodsService.queryGoodsList());System.out.println(goodsService.queryGoodsList());System.out.println(goodsService.queryGoodsList());}
}

服务端打印结果:

服务端处理的结果:EXECUTE QUERY_INFO METHOD
服务端处理的结果:EXECUTE QUERY_LIST METHOD
服务端处理的结果:EXECUTE QUERY_LIST METHOD
服务端处理的结果:EXECUTE QUERY_LIST METHOD
服务端处理的结果:EXECUTE QUERY_LIST METHOD
服务端处理的结果:EXECUTE QUERY_LIST METHOD
服务端处理的结果:EXECUTE QUERY_LIST METHOD
服务端处理的结果:EXECUTE QUERY_LIST METHOD

客户端打印结果:

EXECUTE QUERY_INFO METHOD
EXECUTE QUERY_LIST METHOD
EXECUTE QUERY_LIST METHOD
EXECUTE QUERY_LIST METHOD
EXECUTE QUERY_LIST METHOD
EXECUTE QUERY_LIST METHOD
EXECUTE QUERY_LIST METHOD
EXECUTE QUERY_LIST METHOD

最后

本文是自己学习后,变成自己的知识,默写出来的。主要是用来以后自己查看方便使用。下篇文章将基于这个版本实现通过注解的方式调用和发布服务。

这篇关于手写RPC第一版的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1057044

相关文章

stl的sort和手写快排的运行效率哪个比较高?

STL的sort必然要比你自己写的快排要快,因为你自己手写一个这么复杂的sort,那就太闲了。STL的sort是尽量让复杂度维持在O(N log N)的,因此就有了各种的Hybrid sort algorithm。 题主你提到的先quicksort到一定深度之后就转为heapsort,这种是introsort。 每种STL实现使用的算法各有不同,GNU Standard C++ Lib

Matlab实现RPC算法

RPC(Remote Procedure Call,远程过程调用)是一个在计算机网络中常用的技术,允许一个程序调用另一个地址空间(通常位于另一台计算机上)的过程或函数,就像调用本地程序中的函数一样。 下面是一个简化的示例,展示如何使用 Matlab 的 TCP/IP 套接字功能来模拟 RPC 调用。在这个例子中,我们将创建一个简单的服务器(server.m),它监听一个端口并响应客户端(clie

JS手写实现深拷贝

手写深拷贝 一、通过JSON.stringify二、函数库lodash三、递归实现深拷贝基础递归升级版递归---解决环引用爆栈问题最终版递归---解决其余类型拷贝结果 一、通过JSON.stringify JSON.parse(JSON.stringify(obj))是比较常用的深拷贝方法之一 原理:利用JSON.stringify 将JavaScript对象序列化成为JSO

T1打卡——mnist手写数字识别

🍨 本文为🔗365天深度学习训练营中的学习记录博客🍖 原作者:K同学啊 1.定义GPU import tensorflow as tfgpus=tf.config.list_physical_devices("GPU")if gpus:gpu0=gpus[0]tf.config.experimental.set_memort_groth(gpu0,True) #设置GPU现存用量按需

python实现RPC算法

在Python中实现RPC(远程过程调用)算法可以通过多种方式完成,但最常见和简单的方法之一是使用现有的RPC框架,如gRPC(基于Google的Protocol Buffers)或Pyro4。这里将使用Pyro4来演示如何创建一个简单的RPC服务器和客户端。 安装Pyro4 首先,需要安装Pyro4。可以通过pip轻松安装: pip install Pyro4 创建一个RPC服务 接下

【DL--22】实现神经网络算法NeuralNetwork以及手写数字识别

1.NeuralNetwork.py #coding:utf-8import numpy as np#定义双曲函数和他们的导数def tanh(x):return np.tanh(x)def tanh_deriv(x):return 1.0 - np.tanh(x)**2def logistic(x):return 1/(1 + np.exp(-x))def logistic_derivati

【tensorflow CNN】构建cnn网络,识别mnist手写数字识别

#coding:utf8"""构建cnn网络,识别mnistinput conv1 padding max_pool([2,2],strides=[2,2]) conv2 x[-1,28,28,1] 卷积 [5,5,1,32] -> [-1,24,24,32]->[-1,28,

【tensorflow 全连接神经网络】 minist 手写数字识别

主要内容: 使用tensorflow构建一个三层全连接传统神经网络,作为字符识别的多分类器。通过字符图片预测对应的数字,对mnist数据集进行预测。 # coding: utf-8from tensorflow.examples.tutorials.mnist import input_dataimport tensorflow as tfimport matplotlib.pyplot

大数据方向另一个十年开启 |《硬刚系列》第一版完结

《硬刚Presto|Presto原理&调优&面试&实战全面升级版》 《硬刚Apache Iceberg | 技术调研&在各大公司的实践应用大总结》 《硬刚ClickHouse | 4万字长文ClickHouse基础&实践&调优全视角解析》 《硬刚数据仓库|SQL Boy的福音之数据仓库体系建模&实施&注意事项小总结》 《硬刚Hive | 4万字基础调优面试小总结》 《硬刚用户画像(一) | 标

RPC框架-Avro

引言 远程过程调用(RPC, Remote Procedure Call)是一种允许程序调用远程服务器上函数或方法的技术,应用广泛于分布式系统中。在RPC的众多实现中,Apache Avro作为一种数据序列化框架,以其紧凑、高效、跨语言等特性而受到广泛关注。Avro不仅支持数据序列化,还提供了一个简洁的RPC框架,特别适合与Hadoop生态系统集成。 本文将详细探讨Apache Avro框架的