手撸RPC框架系列--Java简易版

RPC是Remote Procedure Call(远程过程调用)的简写,即实现调用远程计算机上的方法,就像调用本地方法一样。

分布式环境下各个服务之间的协作,必然会用到RPC的思想。

一般来讲,RPC框架会包含3部分:

  • 服务提供者(ServiceProvider)
  • 注册中心(RegistryCentre)
  • 服务消费者(ServiceComsumer)

RPC整个过程可以概括如下:

  • 定义好统一的请求体(RpcRequest)和返回体(RpcResponse);
  • 定义好服务接口;
  • 服务提供者完成接口的实现,并将接口通过TCP或HTTP的方式暴露出去,同时将服务的元信息提交到注册中心;
  • 服务消费者从注册中心拿到服务提供者的信息,然后提交请求,并获得返回。

考虑到文章篇幅大小,本文仅介绍如何开发服务提供者和服务消费者,服务消费者采用直连服务提供者的方式实现RPC。

请求体(RpcRequest)和返回体(RpcResponse)

请求体(RpcRequest)

public class RpcRequest implements Serializable {

    // 方法名称
    private String methodName;

    // 参数列表
    private Object[] parameters;

    // 参数类型
    private Class<?>[] parameterTypes;

    public String getMethodName() {
        return methodName;
    }

    public void setMethodName(String methodName) {
        this.methodName = methodName;
    }

    public Object[] getParameters() {
        return parameters;
    }

    public void setParameters(Object[] parameters) {
        this.parameters = parameters;
    }

    public Class<?>[] getParameterTypes() {
        return parameterTypes;
    }

    public void setParameterTypes(Class<?>[] parameterTypes) {
        this.parameterTypes = parameterTypes;
    }

    @Override
    public String toString() {
        return "RpcRequest{" +
                "methodName='" + methodName + '\'' +
                ", parameters=" + Arrays.toString(parameters) +
                ", parameterTypes=" + Arrays.toString(parameterTypes) +
                '}';
    }
}

返回体(RpcResponse)

public class RpcResponse implements Serializable {
    public static String SUCCEED = "succeed";
    public static String FAILED = "failed";

    // 响应状态
    private String status = SUCCEED;

    // 响应信息,如异常信息
    private String message;

    // 响应数据
    private Object data;

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    @Override
    public String toString() {
        return "RpcResponse{" +
                "status='" + status + '\'' +
                ", message='" + message + '\'' +
                ", data=" + data +
                '}';
    }
}

服务接口

public interface DemoService {
    String add(Integer a, Integer b);
}

RpcProvider(服务提供者)

RpcProvider本质上就是一个代理类,它持有服务接口的实现类,并根据接收到的RpcRequest,反射调用服务接口实现类的对应方法,并将执行结果封装为RpcResponse返回给RpcConsumer。

public class RpcProvider<T> {
	// 服务提供者持有接口的实现类
    private T ref;
	// 服务接口的Class
    private Class<?> interfaceClass;

    public void setRef(T ref) {
        this.ref = ref;
    }

    public RpcProvider<T> setInterfaceClass(Class<?> interfaceClass) {
        this.interfaceClass = interfaceClass;
        return this;
    }

    public void export(int port){
        try {
            ServerSocket listener = new ServerSocket(port);
            while(true){
                Socket socket = listener.accept();
                // 接收数据并进行反序列化
                ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
                // 获取请求对象
                Object object = objectInputStream.readObject();
                if (object instanceof RpcRequest){
                    RpcRequest request = (RpcRequest) object;
                    System.out.println("服务端收到请求: " + request);
					// 处理传入的请求
                    RpcResponse response = handleRequest(request);
                    // 将结果写回客户端
                    ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
                    System.out.println("服务端返回结果: " + response);
                    objectOutputStream.writeObject(response);
                }
                socket.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public RpcResponse handleRequest(RpcRequest request){
        RpcResponse response = new RpcResponse();
        try{
			// 解析请求体,获取请求中调用方法的参数
			// 基于反射获取到接口的对应方法
            Method method = interfaceClass.getMethod(request.getMethodName(), request.getParameterTypes());
            // 反射调用接口实现类的对应方法,并拿到方法结果
			Object data = method.invoke(ref, request.getParameters());
			// 将方法结果set到返回体中
            response.setData(data);
        }catch (Exception e){
            response.setStatus(RpcResponse.FAILED);
            response.setMessage(e.getMessage());
        }
        return response;
    }
}

准备好服务接口的实现类:

public class DemoServiceImpl implements DemoService {

    @Override
    public String add(Integer a, Integer b) {
        return "This is a Simple RPC Service, result: " + (a + b);
    }
}

启动RpcProvider:

public class DemoServiceProvider {
    public static void main(String[] args) {
		// 创建服务接口实现实例
        DemoServiceImpl demoService = new DemoServiceImpl();
        RpcProvider<DemoService> provider = new RpcProvider<>();
		// 将服务接口实现实例传给RpcProvider
        provider.setInterfaceClass(DemoService.class)
                .setRef(demoService);

        provider.export(9092);
    }
}

RpcConsumer

服务消费端仅持有服务的接口,并没有服务各个接口方法的实现,那如何完成方法的调用呢?

基本原理就是,服务消费端会动态创建1个接口的代理类给到用户,用户调用接口的各个方法时,本质上执行的是代理类的对应方法。

代理类根据用户调用的方法名称和入参等构造好RpcRequest,然后提交给RpcProvider,RpcProvider解析RpcRequest并执行完相应方法,将执行结果封装为RpcResponse返回给代理类,代理类解析完RpcResponse并将结果作为接口方法返回给到用户。整个过程中,用户感知不到代理类做的这些事情,就好像接口方法在本地执行一样。

首先我们先定义1个连接RpcProvider的客户端,用于向RpcProvider发送RpcRequest,并接收RpcResponse:

public class RpcClient {
    // 服务接口
    private String address;
    // 服务端口
    private int port;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public RpcResponse send(RpcRequest request) throws Exception {
        Socket socket = new Socket(address, port);
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
        objectOutputStream.writeObject(request);
        ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
        Object object = objectInputStream.readObject();
        if(object instanceof RpcResponse){
            return (RpcResponse) object;
        }
        throw new RuntimeException("Return error");
    }
}

基于Java的Proxy.newProxyInstance动态创建代理类时,需要传入1个InvocationHandler,以定义代理方法的执行过程,即InvocationHandler的invoke方法。

我们定义1个RpcInvocationHandler,在invoke方法中,我们首先根据调用的方法参数构建RpcRequest,然后再通过RpcClient发送RpcRequest,并接收RpcResponse,最后解析RpcResponse拿到远程方法的执行结果。

public class RpcInvocationHandler implements InvocationHandler {
	// 客户端,用于发送RpcRequest
    private RpcClient client;

    public RpcInvocationHandler(RpcClient client) {
        this.client = client;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 构建发送请求
        RpcRequest request = new RpcRequest();
        request.setMethodName(method.getName());
        request.setParameters(args);
        request.setParameterTypes(method.getParameterTypes());
		// 发送请求,并得到返回
        RpcResponse response = client.send(request);

        if(RpcResponse.SUCCEED.equals(response.getStatus())){
			// 解析返回,拿到结果data
            return response.getData();
        }
        throw new RuntimeException(response.getMessage());
    }
}

最后,我们定义一下RpcConsumer:

public class RpcConsumer {
    private String address;
    private int port;

    private Class<?> interfaceClass;

    public RpcConsumer setAddress(String address) {
        this.address = address;
        return this;
    }

    public RpcConsumer setPort(int port) {
        this.port = port;
        return this;
    }

    public RpcConsumer setInterfaceClass(Class<?> interfaceClass) {
        this.interfaceClass = interfaceClass;
        return this;
    }

    public <T> T get(){
        RpcClient client = new RpcClient();
        client.setAddress(address);
        client.setPort(port);
        RpcInvocationHandler handler = new RpcInvocationHandler(client);
		// 创建代理类
        return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, handler);
    }
}

启动RpcConsumer:

public class DemoServiceConsumer {
    public static void main(String[] args) {
        RpcConsumer consumer = new RpcConsumer();
        consumer.setAddress("127.0.0.1");
        consumer.setPort(9092);
        consumer.setInterfaceClass(DemoService.class);

        DemoService service = consumer.get();

        System.out.println(service.add(1, 2));
    }
}

代码执行结果:

This is a Simple RPC Service, result: 3

同时可以观察到RpcProvider端的返回:

服务端收到请求: RpcRequest{methodName='add', parameters=[1, 2], parameterTypes=[class java.lang.Integer, class java.lang.Integer]}
服务端返回结果: RpcResponse{status='succeed', message='null', data=This is a Simple RPC Service, result: 3}

可以发现,RpcConsumer端调用的方法最终是在RpcProvider端执行的,即实现了所谓的远程过程调用

总结

本文实现了1个简易版的RPC框架,旨在带读者过一遍RPC框架的实现过程,但存在以下不足:

  • 缺少注册中心,无法实现服务的注册和发现等
  • 底层TCP通信基于的是Java的BIO,无法应对高并发场景。

后面,我们会引入Zookeeper作为注册中心,使用Netty高性能网络框架来实现底层通信,对现有的RPC实现进行增强。

本文到此结束,感谢阅读!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值