RPC
RPC(remote procedure call)远程过程调用
RPC是为了在分布式应用中,两台主机的Java进程进行通信,当A主机调用B主机的方法时,过程简洁,就像是调用自己进程里的方法一样。
RPC框架的职责就是,封装好底层调用的细节,客户端只要调用方法,就能够获取服务提供者的响应,方便开发者编写代码。
RPC底层使用的是TCP协议,服务端和客户端和点对点通信。
RPC与http
以我写的rpcdemo为例,我在rpcserver端LogonService写了一个checkLogon方法,我想在rpcclient端中直接调用rpcserver端的checkLogon方法。
怎么办?如果是web项目的话本身在tomcat启动了,可以发布一个servlet(http接口,或webservice)接口交互。http接口或webservi都是基于http的,
性能也没rpc高,目前服务器配置等都上来了,springcloud rpc也是基于http协议实现的。
比如主流的dubbo rpc框架,是使用netty启动了一个单独端口的服务,zookeeper作为注册中心,客户端启动后通过nettyclient连接上来,通过netty通信调用
服务器的方法,通过socket传输,,直接走的tcp/ip协议,比http性能更高。
手写rpc框架
技术路线:netty+动态代理+反射+序列化+反序列化
实现原理:
- rpc服务端使用netty发布一个tcp/ip socket服务
- rpc客户端使用连接上netty服务端,建立与服务端的连接
- 客户端通过动态代理生成接口代理类,把要调用的类名,方法,参数类型,参数等序列化后发送到netty服务端
- 服务端收到请求解析类名,方法,参数类型,参数等反序列化后通过反射机制调用该方法,在把响应序列化后返回到客户端
- 客户端收到响应后反序列化得到调用结果
主要涉及以下类
学习使用,客户端,服务器有重复代码,项目上使用的话可以封装成一个maven模块,项目中通过pom引入该rpc模块即可
公共
- RpcRequest,RpcResponse 创建netty服务器数据 数据输入 输出对象
- ByteUtils JsonSerialization 序列化与反序列化工具类 方便对象在netty管道传输
- NettyProtocolHandler netty客户端,服务器通信处理 (客户端发送请求给服务器端,服务端处理完返回数据给客户端)
客户端
4. ClientNetty 连接netty服务端
5. ClientHandler 处理服务器返回的响应
6. ProxyFactory 动态代理工厂,用于生成LogonService接口的动态代理类。
7. RPCInvocationHandler 通过代理对象的invoke方法 把class,method,参数等信息序列化后通过netty客户端发送给netty服务器端处理
8. LogonService接口,和服务端结构一样的LogonService接口,rpc使用的动态代理技术,要求消费者和提供者都必须定义一样的接口类
9. ClientMain 客户端项目启动入口main方法启动
服务端
10. ServerNetty 开启netty服务
11. ServerHandler 处理客户端请求,解析参数得到class,method从concurrentHashMap获取实例后通过反射机制调用方法
12. LogonService接口,和客户端结构一样的LogonService接口,rpc使用的动态代理技术,要求消费者和提供者都必须定义一样的接口类
13. LogonServiceImpl 实现类,因为是服务提供者,需要实现LogonService接口,供服务消费者调用
14. ServerMain 服务端项目启动入口main方法启动
RPCClient项目目录树
RPCServer项目目录树
客户端,服务端pom依赖
客户端,服务端都为普通的maven 工程,只引入了netty和jackson依赖
<dependencies>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.13.0</version>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-transport</artifactId>
<version>4.1.75.Final</version>
</dependency>
</dependencies>
公共类
RpcRequest,RpcResponse
package com.wying.rpc;
import java.util.Arrays;
/**
* description:RpcRequest
* date: 2022/5/3
* author: gaom
* version: 1.0
*/
public class RpcRequest {
private String className;
private String methodName;
private Class[] paramType;
private Object[] args;
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 Class[] getParamType() {
return paramType;
}
public void setParamType(Class[] paramType) {
this.paramType = paramType;
}
public Object[] getArgs() {
return args;
}
public void setArgs(Object[] args) {
this.args = args;
}
@Override
public String toString() {
return "RpcRequest{" +
"className='" + className + '\'' +
", methodName='" + methodName + '\'' +
", paramType=" + Arrays.toString(paramType) +
", args=" + Arrays.toString(args) +
'}';
}
}
package com.wying.rpc;
/**
* description:RpcResponse
* date: 2022/5/3
* author: gaom
* version: 1.0
*/
public class RpcResponse {
private int code;
private Object result;
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public Object getResult() {
return result;
}
public void setResult(Object result) {
this.result = result;
}
@Override
public String toString() {
return "RpcResponse{" +
"code=" + code +
", result=" + result +
'}';
}
}
ByteUtils,JsonSerialization
package com.wying.rpc;
import java.nio.ByteBuffer;
/**
* description:ByteUtils
* date: 2022/5/3
* author: gaom
* version: 1.0
*/
public class ByteUtils {
public static byte[] short2bytes(short v) {
byte[] b = new byte[4];
b[1] = (byte) v;
b[0] = (byte) (v >>> 8);
return b;
}
public static byte[] int2bytes(int v) {
byte[] b = new byte[4];
b[3] = (byte) v;
b[2] = (byte) (v >>> 8);
b[1] = (byte) (v >>> 16);
b[0] = (byte) (v >>> 24);
return b;
}
public static byte[] long2bytes(long v) {
byte[] b = new byte[8];
b[7] = (byte) v;
b[6] = (byte)