美文网首页
初步了解RPC及实现一个简单的RPC

初步了解RPC及实现一个简单的RPC

作者: 一萍之春 | 来源:发表于2019-02-13 18:03 被阅读62次

    什么是RPC

    RPC(全称为 Remote Procedure Call)即远程过程调用
    在维基百科中这样解释

    远程过程调用(英语:Remote Procedure Call,缩写为 RPC)是一个计算机通信协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。

    RPC解决的事情

    • 进程间通讯
    • 提供和本地方法调用一样的调用机制
    • 屏蔽程序员对远程调用的细节实现

    简单的RPC结构

    简单的RPC可以分为五个部分:

    1. client
    2. client-stub
    3. network-service
    4. server-stub
    5. server

    这5个部分的关系及基本流程如下图


    关系及基本流程图

    RPC基本过程

    1. 服务消费方(client)以本地调用方式调用服务;
    2. client stub接收到调用后负责将方法、参数等组装成能够进行网络传输的消息体;
    3. client stub找到服务地址,并将消息发送到服务端;
    4. server stub收到消息后进行解码;
    5. server stub根据解码结果通过反射调用本地的服务;
    6. 本地服务执行并将结果返回给server stub;
    7. server stub将返回结果打包成消息并发送至消费方;
    8. client stub接收到消息,并进行解码;
    9. 服务消费方得到最终结果。

    一般情况下我们的RPC就是对我们的上面过程的2~8进行封装然后,给client的程序员感觉就像调用本地的方法一样。

    简单实现

    首先是Client端的应用层怎么发起RPC,CallerApp

    public class CallerApp {
        public static void main(String[] args) {
            Calculator calculator = new CalculatorRemoteImpl();
            int result = calculator.add(3, 3);
            System.out.println(result);
        }
    }
    

    通过一个CalculatorRemoteImpl,我们把RPC的逻辑封装进去了,客户端调用时感知不到远程调用的麻烦。将上面的2、3、4进行封装

    public class CalculatorRemoteImpl implements Calculator {
        public static final int PORT = 9090;
        private static Logger log = LoggerFactory.getLogger(CalculatorRemoteImpl.class);
    
        public int add(int a, int b) {
            List<String> addressList = lookupProviders("Calculator.add");
            String address = chooseTarget(addressList);
            try {
                Socket socket = new Socket(address, PORT);
    
                // 将请求序列化
                CalculateRpcRequest calculateRpcRequest = generateRequest(a, b);
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
    
                // 将请求发给服务提供方
                objectOutputStream.writeObject(calculateRpcRequest);
    
                // 将响应体反序列化
                ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
                Object response = objectInputStream.readObject();
    
                log.info("response is {}", response);
    
                if (response instanceof Integer) {
                    return (Integer) response;
                } else {
                    throw new InternalError();
                }
    
            } catch (Exception e) {
                log.error("fail", e);
                throw new InternalError();
            }
        }
    
        private CalculateRpcRequest generateRequest(int a, int b) {
            CalculateRpcRequest calculateRpcRequest = new CalculateRpcRequest();
            calculateRpcRequest.setA(a);
            calculateRpcRequest.setB(b);
            calculateRpcRequest.setMethod("add");
            return calculateRpcRequest;
        }
    
        private String chooseTarget(List<String> providers) {
            if (null == providers || providers.size() == 0) {
                throw new IllegalArgumentException();
            }
            return providers.get(0);
        }
    
        public static List<String> lookupProviders(String name) {
            List<String> strings = new ArrayList();
            strings.add("127.0.0.1");
            return strings;
        }
    }
    

    这里用到了Socket来进行远程通讯,同时利用ObjectOutputStream的writeObject和ObjectInputStream的readObject,来实现序列化和反序列化。
    下面是service端的实现

    public class CalleeApp {
        private static Logger log = LoggerFactory.getLogger(CalleeApp.class);
    
        private Calculator calculator = new CalculatorImpl();
    
        public static void main(String[] args) throws IOException {
            new CalleeApp().run();
        }
    
        private void run() throws IOException {
            ServerSocket listener = new ServerSocket(9090);
            try {
                while (true) {
                    Socket socket = listener.accept();
                    try {
                        // 将请求反序列化
                        ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
                        Object object = objectInputStream.readObject();
    
                        log.info("request is {}", object);
    
                        // 调用服务
                        int result = 0;
                        if (object instanceof CalculateRpcRequest) {
                            CalculateRpcRequest calculateRpcRequest = (CalculateRpcRequest) object;
                            if ("add".equals(calculateRpcRequest.getMethod())) {
                                result = calculator.add(calculateRpcRequest.getA(), calculateRpcRequest.getB());
                            } else {
                                throw new UnsupportedOperationException();
                            }
                        }
    
                        // 返回结果
                        ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
                        objectOutputStream.writeObject(new Integer(result));
                    } catch (Exception e) {
                        log.error("fail", e);
                    } finally {
                        socket.close();
                    }
                }
            } finally {
                listener.close();
            }
        }
    
    }
    

    示例代码在GitHub上下载

    相关文章

      网友评论

          本文标题:初步了解RPC及实现一个简单的RPC

          本文链接:https://www.haomeiwen.com/subject/clldeqtx.html