通过手写简易版RPC框架理解其原理

什么是RPC框架?

  • RPC:remote procedure call 即:远程过程调用
  • 在分布式架构中离不开服务之间的通信
  • 为了提高服务之间通信的性能:产生了如Dubbo、webservice、Thrift等RPC框架

简易版RPC框架实现

  • 通过实现一个简易版本的RPC框架,去学习其原理
  • 首先我们创建需要一个服务端

服务端实现

  • rpc-server:以quickstart的方式快速创建一个maven工程
image-20200530151137794
  • 以同样的方式创建rpc-server-api和rpc-server-provider
  • 创建api和provider模块的作用是在本机模拟服务之间的调用
    • rpc-server-api:定义客户端需要拿到的一些信息,如接口、传输对象等打成jar包作为依赖使用
    • rpc-server-provider:服务提供方基于api的实现,提供服务供客户端进行调用。
image-20200530151246257
  • 至此我们的服务端基础架构就搭建完成了

API实现

  • 写一个简单的接口
public interface UserService {

    String handToUser(String content);

    String saveUser(User user);

}
image-20200530153353766

Provider实现

  • 依赖api(需要把api打成jar包到本地仓库)
<dependency>
    <groupId>com.self.struggle</groupId>
    <artifactId>rpc-server-api</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>
  • 服务简单实现
public class UserServiceImpl implements UserService {

    @Override
    public String handToUser(String s) {
        return "hi,"+s+".Welcome come!";
    }

    @Override
    public String saveUser(User user) {
        return user.toString();
    }

}
  • 我们的服务端实现是要提供给客户端调用的,所以我们需要暴露出我们的服务实现
  • 提供服务暴露的方法—使用代理的形式暴露服务(基于socket实现)
public class RpcServerProxy {

    /**  线程池回顾
     *   newCached:可缓存地线程池,核心线程数为0,最大线程数为 Integer.MAX_VALUE
     *              在回收时间内,可以对创建好地线程进行复用
     */
    ExecutorService executorService = Executors.newCachedThreadPool();

    public void publish(int port) {
        ServerSocket serverSocket = null;
        try {
            //  port:通过端口暴露服务
            serverSocket = new ServerSocket(port);
            //  不断地接受请求
            while (true) {
                //  建立socket连接:通过ois oos去处理    accept:阻塞
                Socket socket = serverSocket.accept();
                //  利用线程池来处理请求   每一个socket交给一个DealRequest:具体地处理逻辑
                executorService.execute(new DealRequest(socket));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}
  • DealRequest实现
public class DealRequest implements Runnable {

    private Socket socket;

    public DealRequest(Socket socket) {
        this.socket = socket;
    }

    /**
     * 处理socket连接中地流信息
     */
    @Override
    public void run() {
        ObjectInputStream objectInputStream = null;
        ObjectOutputStream objectOutputStream = null;

        try {
            //  获取客户端发送请求中地输入流
            objectInputStream = new ObjectInputStream(socket.getInputStream());

            /*  输入流包含的内容
             *  请求的目标类,方法名称,参数  在 api 中定义一个对象,接收返回信息
             */

            //  反序列化过程
            RpcRequest rpcRequest = (RpcRequest) objectInputStream.readObject();

            //  通过反射  实现调用
            Object result = invoke(rpcRequest);

            //  写入结果
            objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            objectOutputStream.writeObject(result);
            objectOutputStream.flush();

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if(objectInputStream != null) {
                try {
                    objectInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if(objectOutputStream != null) {
                try {
                    objectOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private Object invoke(RpcRequest request) throws Exception {
        // 反射调用
        Object[] params = request.getParams();
        Class<?>[] types = new Class[params.length];
        for (int i = 0; i < params.length; i++) {
            types[i] = params[i].getClass();
        }
        Class<?> clazz = Class.forName(request.getClassName());
        Object obj = clazz.newInstance();
        Method method = clazz.getMethod(request.getMethodName(), types);
        return method.invoke(obj, params);
    }

}

发布服务

  • 在App.java中发布服务
public class App
{
    public static void main( String[] args )
    {
        RpcServerProxy rpcServerProxy = new RpcServerProxy();
        rpcServerProxy.publish(8080);
    }
}
  • 至此,简易版的服务端实现完成

客户端实现

  • 客户端需要去调用服务端
  • 通过远程代理服务调用

代理服务实现

  • 通过反射的方式进行调用,具体实现在 RemoteInvocationHandler 中
public class RpcClientProxy {

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

}
  • RemoteInvocationHandler
  • 在invoke方法中构建传输对象,由RpcNetTransport发送请求
public class RemoteInvocationHandler implements InvocationHandler {

    private String host;
    private int port;

    public RemoteInvocationHandler(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.setParams(args);

        //  远程通信
        RpcNetTransport rpcNetTransport = new RpcNetTransport(host, port);

        return rpcNetTransport.send(rpcRequest);
    }
}
  • RpcNetTransport
public class RpcNetTransport {

    private String host;
    private int port;

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

    public Object send(RpcRequest request) {
        Socket socket = null;
        Object result = null;
        ObjectOutputStream objectOutputStream = null;
        ObjectInputStream objectInputStream = null;

        try {
            //  建立连接
            socket = new Socket(host, port);
            objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            objectOutputStream.writeObject(request);
            objectOutputStream.flush();

            objectInputStream = new ObjectInputStream(socket.getInputStream());
            result = objectInputStream.readObject();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

}

服务调用

public class App
{
    public static void main( String[] args )
    {
        UserService userService = (UserService) new RpcClientProxy().clientProxy(UserService.class, "localhost", 8080);

        String status = userService.handToUser("你好");

        System.out.println("server back:"+status);
    }
}

验证结果

  • 客户端
    在这里插入图片描述

  • 服务端

在这里插入图片描述

流程图

  • nexus代表私服仓库
  • ProxyClient:客户端代理,socket建立连接后,通过动态代理的方式进行远程调用
  • provider:业务逻辑实现,对api进行处理
  • RpcproxyServer:服务端代理,暴露服务,供客户端调用

在这里插入图片描述

源码和升级版本

  • https://gitee.com/joyful_live/rpc.git
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章