0 前言 1 总体介绍 2 common模块 2.1 ReflectionUtils.java 3 codec模块 3.1 序列化接口Encoder.java 3.2 json序列化实现JSONEncoder.java 3.3 反序列化接口Decoder.java 3.4 反序列化实现JSONDecoder.java 4 proto模块 4.1 Peer.java 4.2 Request.java 4.3 ServiceDescriptor类 4.4 Response.java类 5 transport网络模块 5.1 TransportClient接口 5.2 HTTPTransportClient.java 5.3 RequsetHandler.java 5.4 TransportServer.java 5.5 HTTPTransportServer.java 6 server模块 6.1 RpcServer.java 6.2 RpcServerConfig.java 6.3 ServiceInstance.java 6.4 ServiceInvoker.java 6.5 ServiceManager.java 7 client模块 7.1 TransportSelector.java 7.2 RandomTransportSelector.java 7.3 RpcClientConfig.java 7.4 RemoteInvoker.java 7.5 RpcClient.java 8 Example 8.1 CalcService.java 8.2 CalcServiceImpl.java 8.3 Client.java 8.4 Server.java 8.5 运行 9 完整工程与代码
0 前言
本文介绍一个RPC的简单实现,RPC的原理可以参考这里:3分钟搞懂RPC原理
涉及的技术:
- 基础知识
JavaCore,Maven,反射
- 动态代理(生成client存根实际调用对象)
Java的动态代理
- 序列化(Java对象与二进制数据互转)
fastjson
序列化:Java对象转为二进制数组
反序列化:二进制数组转为Java对象
- 网络通信(传输序列化后的数据)
jetty,URLConnection
本文只会大致介绍每个模块,以及模块中每个类的成员与方法,完整的工程链接和代码会放在文章结尾的链接中。
1 总体介绍
rpc实现有五个模块:
协议模块:定义了其他模块需要的java bean
序列化模块:定义了序列化和反序列化方法
网络模块:实现底层的网络通信,使用了jetty
server模块:rpc server端
client模块:rpc client端
模块之间的依赖关系如下图所示:
2 common模块 common模块提供了公共的代码,供其他模块使用。
2.1 ReflectionUtils.java rpc的实现中,大量使用了反射方式来获取类对象。ReflectionUtils类提供了获取类对象、执行类方法的函数。
public class ReflectionUtils {
/**
* 根据clazz创建对象
* @param clazz 待创建对象的类
* @param <T> 对象类型
* @return 创建好的对象
*/
public static <T> T newInstance(Class<T> clazz);
/**
* 获取某个class的公有方法
* @param clazz
* @return
*/
public static Method[] getPublicMethods(Class clazz);
/**
* 调用指定对象的指定方法
* @param obj 被调用方法的对象
* @param method 被调用的方法
* @param args 方法的参数
* @return 返回结果
*/
public static Object invoke(Object obj,Method method,Object... args);
}
3 codec模块
序列化模块。数据在网络中进行传输时,都是二进制。所以发送方,需要先将对象转为二进制,这个过程称为序列化。接收方在接收到二进制数据后,将二进制对象转为对象,这个过程称为反序列化。
3.1 序列化接口Encoder.java 将一个对象 序列化为二进制数组
public interface Encoder {
byte[] encode(Object obj);
}
3.2 json序列化实现JSONEncoder.java
将对象转为json形式,然后再转为二进制数组
/**
* 序列化,将json对象转为二进制数组
*/
public byte[] encode(Object obj);
3.3 反序列化接口Decoder.java
将二进制数组转为对象
/**
* 反序列化,将二进制数组转为对象
* @param bytes 二进制数组
* @param clazz 待转的类型
* @param <T> 泛型
* @return 返回的对象
*/
<T>T decode(byte[] bytes,Class<T> clazz);
3.4 反序列化实现JSONDecoder.java 将二进制数组反序列化为json格式,然后将json转为对象
public <T> T decode(byte[] bytes, Class<T> clazz);
4 proto模块
协议模块,定义了其他模块需要的bean类。
4.1 Peer.java 定义了端口和ip信息
public class Peer {
private String host; //ip
private int port; //端口
}
4.2 Request.java
client端向server通信时的请求类
public class Request {
//请求的函数的描述信息
private ServiceDescriptor service;
//请求的参数
private Object[] parameters;
}
4.3 ServiceDescriptor类
请求的描述信息,Server根据这个信息,来找到对应的函数执行。由于这个类之后需要查找与比较,所以需要重写equals方法以及hashCode方法。这个类还提供了一个静态方法from,来根据类名和方法名,来生成一个Service描述对象ServiceDescriptor。
public class ServiceDescriptor {
private String clazz; //类名
private String method; //方法名
private String returnType; //返回类型
private String[] parameterTypes; //参数类型
@Override
public boolean equals(Object o) {
if(this == o)return true;
if(o == null || getClass() != o.getClass()) return false;
ServiceDescriptor that = (ServiceDescriptor)o;
return this.toString().equals(o.toString());
}
@Override
public int hashCode() {
return toString().hashCode();
}
@Override
public String toString() {
return "clazz="+ clazz
+ ",method="+ method
+",returnType="+ returnType
+",parameterTypes="+Arrays.toString(parameterTypes);
}
/**
* 通过clazz 和 method 抽取出方法的输入输出参数,生成ServiceDescript
* @param clazz
* @param method
* @return
*/
public static ServiceDescriptor from(Class clazz, Method method);
}
4.4 Response.java类
rpc调用之后的返回数据类
public class Response {
/**
* 服务返回编码:0-成功 非0失败
*/
private int code =0;
private String message = "ok"; //错误信息
private Object data; //返回数据对象
}
5 transport网络模块
这个模块是rpc最底层的网络通信模块,这里使用http来实现的。
5.1 TransportClient接口 client端的网络模块。在这里用的http短连接来实现的网络通信,在一般的rpc中,其实是用的tcp长连接来实现的 。
/**
* 1. 创建连接
* 2. 发送数据,并且等待响应
* 3. 关闭连接
*/
public interface TransportClient {
//连接,在实现中就是为url变量赋值
void connect(Peer peer);
//写数据
InputStream write(InputStream data);
//关闭连接
void close();
}
5.2 HTTPTransportClient.java
TransportClient实现类,功能见接口类
5.3 RequsetHandler.java server端处理client发过来的请求的类。由server模块调用网络接口时,作为参数传递给TransportServer来处理client的请求。
/**
* 处理网络请求的handler
*/
public interface RequestHandler {
void onRequest(InputStream recive, OutputStream toResp);
}
5.4 TransportServer.java Sever端的网络模块接口
/**
* 1. 启动、监听
* 2. 接受请求
* 3. 关闭监听
* 在servlet中调用handler来处理客户端请求
*/
public interface TransportServer {
/**
* 初始化,设置servlet
* @param port 端口
* @param handler 处理请求的方法
*/
void init(int port,RequestHandler handler);
//开始服务
void start();
//停止服务
void stop();
}
5.5 HTTPTransportServer.java
TransportServer的实现类
6 server模块
server部分执行流程如下所示:
6.1 RpcServer.java server端类
public class RpcServer {
private RpcServerConfig config = new RpcServerConfig(); //server配置类
private TransportServer net; //server网络模块
private Encoder encoder; //序列化类
private Decoder decoder; //反序列化类
private ServiceManager serviceManager; //service管理类
private ServiceInvoker serviceInvoker; //servive执行类
/**
* 注册函数,将一个类注册为rpc service,其中的所有public方法会被注册为rpc service
* 这个函数会调用ServiceManager类的register方法
* @param interfaceClass 注册类的接口
* @param bean 注册类的实现类
* @param <T> 泛型
*/
public <T> void register(Class<T>interfaceClass,T bean);
/**
* 开启网络模块
*/
public void start();
/**
* 停止网络模块
*/
public void stop();
/**
* 处理网络请求的实现类
* 在初始化网络模块的时候,作为参数传入
* 先将input的二进制数据读取出来,然后反序列化成Request类
* 再通过ServiceManager类的lookup函数找到该service
* 然后通过ServiceInvoker类的invoke方法来执行服务
* 然后将结果序列化,返回
*/
private RequestHandler handler = new RequestHandler() {
...
}
}
6.2 RpcServerConfig.java
Server配置类
public class RpcServerConfig {
private Class<? extends TransportServer > transportClass = HTTPTransportServer.class;
private Class<? extends Encoder> encoderClass = JSONEncoder.class;
private Class<? extends Decoder> decoderClass = JSONDecoder.class;
private int port =3000;
}
6.3 ServiceInstance.java
Service实例类
public class ServiceInstance {
private Object target;
private Method method;
}
6.4 ServiceInvoker.java
调用具体服务
public class ServiceInvoker {
public Object invoke(ServiceInstance service, Request request);
}
6.5 ServiceManager.java Service管理类,负责注册服务,查找服务。
public class ServiceManager {
private Map<ServiceDescriptor,ServiceInstance> services;
//注册服务
public <T> void register(Class<T> interfaceClass,T bean);
//查找服务
public ServiceInstance lookup(Request request);
}
7 client模块
client的执行步骤如下图所示:
7.1 TransportSelector.java 网络选择类,client对一个server可以有多个连接,以便负载均衡,所以在进行一次rpc时,需要选择一个连接。
/**
* 表示选择哪个server去连接
*/
public interface TransportSelector {
/**
* 初始化selector
* @param peers 可以连接的server端点信息
* @param count client 与server建立多少个连接
* @param clazz client实现class
*/
void init(List<Peer> peers,int count,Class<? extends TransportClient> clazz);
/**
* 选择一个transport与server做交互
* @return 网络Client
*/
TransportClient select();
/**
* 释放用完的client
* @param client
*/
void release(TransportClient client);
void close();
}
7.2 RandomTransportSelector.java TransportSelector的实现类,实现随机选择一个连接。
7.3 RpcClientConfig.java 配置类,可以对Client进行一些配置
public class RpcClientConfig {
private Class<? extends TransportClient> transportClass = HTTPTransportClient.class;
private Class <? extends Encoder> encoderClass = JSONEncoder.class;
private Class <? extends Decoder> decoderClass = JSONDecoder.class;
private Class<? extends TransportSelector> selectorClass = RandomTransportSelector.class;
private int connectCount =1;
private List<Peer> servers = Arrays.asList(new Peer("127.0.0.1",3000));
}
7.4 RemoteInvoker.java 动态代理的代理类。在Client端,为了实现rpc,通过动态代理,增强原来的类。
/**
* 调用远程服务的代理类
*/
@Slf4j
public class RemoteInvoker implements InvocationHandler {
private Encoder encoder; //序列化
private Class clazz; //需要的服务类
private Decoder decoder; //反序列化
private TransportSelector selector; //网络服务选择类
/**
* 代理类
* proxy:
* method:想调用方法
* args: 方法的参数
* 返回:方法的返回参数
*/
public Object invoke(Object proxy,Method method,Object[] args) throws Throwable;
/**
* 远程执行,被invoke调用
* @param request client请求
* @return 返回 Response类
*/
private Response invokeRemote(Request request);
}
7.5 RpcClient.java Client类,将原有类动态代理为具有rpc功能的类。
public class RpcClient {
private RpcClientConfig config = new RpcClientConfig();
private Encoder encoder;
private Decoder decoder;
private TransportSelector selector;
/**
* 获取 RemoteInvoker 动态代理类
* @param clazz
* @param <T>
* @return
*/
public <T> T getProxy(Class<T> clazz);
}
8 Example 一个实现上述实现的rpc的例子。例子是一个计算器的rpc调用,包括两个函数:加,减。
8.1 CalcService.java 计算类接口,Client端只需要接口类即可rpc。
public interface CalcService {
int add(int a,int b);
int minus(int a,int b);
}
8.2 CalcServiceImpl.java 计算实现类
package com.qzq.gkrpc.example;
public class CalcServiceImpl implements CalcService{
@Override
public int add(int a, int b) {
return a+b;
}
@Override
public int minus(int a, int b) {
return a-b;
}
}
8.3 Client.java rpc的Client端
package com.qzq.gkrpc.example;
import com.qzq.gkrpc.client.RpcClient;
public class Client {
public static void main(String[] args) {
RpcClient client = new RpcClient();
CalcService service = client.getProxy(CalcService.class);
int r1 = service.add(1,2);
int r2 = service.minus(10,8);
System.out.println(r1);
System.out.println(r2);
}
}
8.4 Server.java rpc的Server端
package com.qzq.gkrpc.example;
import com.qzq.gkrpc.server.RpcServer;
public class Server {
public static void main(String[] args) {
RpcServer server = new RpcServer();
server.register(CalcService.class,new CalcServiceImpl());
server.start();
}
}
8.5 运行 先启动server端
然后运行client
成功!
9 完整工程与代码 关注公众号【山人彤】,后台回复【RPC】即可免费领取