基于Socket手写实现RPC远程通信

什么是RPC

RPC(Remote Procedure Call)远程过程调用,简单来说是一个服务请求另一个服务提供的数据。它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。

大概的流程图
在这里插入图片描述

代码实现

1. 新建rpc_server模块,在server模块下面创建server_api和server_provider

api提供一些公共的方法,provider主要是服务端的一些处理逻辑
在这里插入图片描述

2. 编写api模块

添加一个公共方法接口和一个RPC请求参数对象

package com.xjp.rpc;

/**
 * @Description
 * @Author XJP
 * @Date 2020-04-24 21:53
 */
public interface IHelloServer {

    String sayHello(String s);

}

package com.xjp.rpc;

import java.io.Serializable;
import java.util.Arrays;

/**
 * @Description
 * @Author XJP
 * @Date 2020-04-25 9:29
 */
public class RpcRequest implements Serializable {

    private String className;
    private String methodName;
    private Object[] parameters;
    private Class[] types;

	//添加get和set,toString方法
}

4. 编写provider

1. 实现IHelloServer接口

加入api的pom依赖

		<dependency>
            <groupId>org.example</groupId>
            <artifactId>rpc_server_api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
package com.xjp.impl;

import com.xjp.rpc.IHelloServer;

/**
 * @Description
 * @Author XJP
 * @Date 2020-04-24 21:55
 */
public class HelloServerImpl implements IHelloServer {
    @Override
    public String sayHello(String s) {
        System.out.println("Rpc Start");
        return "hello: " + s;
    }
}

2. rpc服务端代理

创建RpcProxyServer 类,通过一个线程池,通过线程池异步处理请求

package com.xjp;

import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @Description
 * @Author XJP
 * @Date 2020-04-25 9:05
 */
public class RpcProxyServer {

    public void publisher(Object service, int port){
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        ServerSocket serverSocket = null;
        try {
            //监听端口
            serverSocket = new ServerSocket(8080);
            //收取客户端请求,没有请求的话处于阻塞状态
            while (true) {
                Socket socket = serverSocket.accept();
                //socket.getInputStream();//获得客户端输入流
                //改为线程实现,不用阻塞
                executorService.execute(new Process(socket,service));
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}

要线程具体执行的代码,Socket获取输入输出流,通过反射获取被调用方法的方法名,类型,invoke方法得到调用结果返回。

package com.xjp;

import com.xjp.rpc.RpcRequest;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.net.Socket;

/**
 * @Description
 * @Author XJP
 * @Date 2020-04-25 9:18
 */
public class Process implements Runnable{

    private Socket socket;
    private Object service;

    public Process(Socket socket, Object service) {
        this.socket = socket;
        this.service = service;
    }

    @Override
    public void run() {
        //说明接收了一个客户端请求
        try {
            ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            //读取客户端数据
            RpcRequest rpcRequest = (RpcRequest) objectInputStream.readObject();
            Object invoke = invoke(rpcRequest);
            objectOutputStream.writeObject(invoke);
            objectOutputStream.flush();
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //反射过程调用
    public Object invoke(RpcRequest rpcRequest) throws Exception{
        Object[] args = rpcRequest.getParameters();
        Class clazz = Class.forName(rpcRequest.getClassName());
        Method method = clazz.getMethod(rpcRequest.getMethodName(), rpcRequest.getTypes());
        Object invoke = method.invoke(service, args);
        return invoke;
    }
}

创建Test测试类,发布接口服务。

package com.xjp;

import com.xjp.impl.HelloServerImpl;
import com.xjp.rpc.IHelloServer;

/**
 * @Description
 * @Author XJP
 * @Date 2020-04-25 10:07
 */
public class Test {

    public static void main(String[] args) {
        IHelloServer helloServer = new HelloServerImpl();
        RpcProxyServer proxyServer = new RpcProxyServer();
        proxyServer.publisher(helloServer,8080);
    }
}

服务端代码已经完成。

5. 客户端代码

导入api依赖

创建客户端对目标对象的一个代理

package com.xjp.rpc.proxy;

import java.lang.reflect.Proxy;

/**
 * @Description
 * @Author XJP
 * @Date 2020-04-24 22:05
 */
public class RpcClientProxy {

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

host是调用的目标地址,port是端口

package com.xjp.rpc.proxy;

import com.xjp.rpc.RpcRequest;
import com.xjp.rpc.RpcTranspost;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

/**
 * @Description
 * @Author XJP
 * @Date 2020-04-24 22:12
 */
public class RemoteInvoHander implements InvocationHandler {

    private String host;
    private int port;

    public RemoteInvoHander(String host, int port) {
        this.host = host;
        this.port = port;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        RpcRequest rpcRequest = new RpcRequest();
        rpcRequest.setClassName(method.getDeclaringClass().getName());
        rpcRequest.setMethodName(method.getName());
        rpcRequest.setParameters(args);
        rpcRequest.setTypes(method.getParameterTypes());

        RpcTranspost rpcTranspost = new RpcTranspost(host,port);
        return rpcTranspost.sendRequest(rpcRequest);
    }
}

创建rpc发送请求的类

package com.xjp.rpc;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;

/**
 * @Description
 * @Author XJP
 * @Date 2020-04-25 9:38
 */
public class RpcTranspost {

    private String host;
    private int port;

    public RpcTranspost(String host, int port) {
        this.host = host;
        this.port = port;
    }

    public Object sendRequest(RpcRequest rpcRequest) throws IOException {
        Socket socket = null;
        ObjectOutputStream objectOutputStream = null;
        ObjectInputStream objectInputStream = null;
        try {
            socket = new Socket(host,port);
            objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            objectOutputStream.writeObject(rpcRequest);
            objectOutputStream.flush();//清空缓冲区
            objectInputStream = new ObjectInputStream(socket.getInputStream());
            Object o = objectInputStream.readObject();
            return o;
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            objectInputStream.close();
            objectOutputStream.close();
        }
        return null;
    }
}

客户端调用服务端接口测试代码

package com.xjp.rpc;

import com.xjp.rpc.proxy.RpcClientProxy;

/**
 * @Description
 * @Author XJP
 * @Date 2020-04-24 22:14
 */
public class RpcClient {

    public static void main(String[] args) {
        RpcClientProxy rpcClientProxy = new RpcClientProxy();
        IHelloServer helloServer = rpcClientProxy.clientProxy(IHelloServer.class, "localhost", 8080);
        System.out.println(helloServer.sayHello("测试RPC通信"));
    }
}

6. 启动测试

启动服务端的Test测试类
在这里插入图片描述

启动客户端的测试代码
在这里插入图片描述
在这里插入图片描述
可以看到客户端的请求已经通过网络传输到了服务端
在这里插入图片描述
因为这种是demo演示,所以socket是短连接,一次通信完成之后连接就断掉了,其他RPC框架用的是基于长连接的通信。

主要的技术点还是在客户端对目标对象的代理,网络IO流的传输以及序列化,然后服务端的反序列化,对客户端的方法进行反射调用,RPC主流框架有dubbo,grpc,Thrift,jsonrpc等,框架封装了底层调用的逻辑,使得开发者不需要过多关注底层原理,就能调用本地方法一样的去调用远程方法。

小白一个,还在继续学习中。。。。。。

发布了6 篇原创文章 · 获赞 2 · 访问量 282

猜你喜欢

转载自blog.csdn.net/qq_42145271/article/details/105746275
今日推荐