RPC实战与核心原理

经过一个CalculatorRemoteImpl,咱们把RPC的逻辑封装进去了,客户端调用时感知不到远程调用的麻烦php

download:《极客时间》RPC实战与核心原理java

下面再来看看CalculatorRemoteImpl,代码有些多,可是其实就是把上面的二、三、4几个步骤用代码实现了而已,CalculatorRemoteImpl:
负载均衡

public class CalculatorRemoteImpl implements Calculator {
    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();

            if (response instanceof Integer) {
                return (Integer) response;
            } else {
                throw new InternalError();
            }

        } catch (Exception e) {
            log.error("fail", e);
            throw new InternalError();
        }
    }}

add方法的前面两行,lookupProviders和chooseTarget,可能你们会以为不明觉厉。socket

分布式应用下,一个服务可能有多个实例,好比Service B,可能有ip地址为198.168.1.11和198.168.1.13两个实例,lookupProviders,其实就是在寻找要调用的服务的实例列表。在分布式应用下,一般会有一个服务注册中心,来提供查询实例列表的功能。分布式

查到实例列表以后要调用哪个实例呢,只时候就须要chooseTarget了,其实内部就是一个负载均衡策略。ide

因为咱们这里只是想实现一个简单的RPC,因此暂时不考虑服务注册中心和负载均衡,所以代码里写死了返回ip地址为127.0.0.1。spa

代码继续往下走,咱们这里用到了Socket来进行远程通信,同时利用ObjectOutputStream的writeObject和ObjectInputStream的readObject,来实现序列化和反序列化。ip

最后再来看看Server端的实现,和Client端很是相似,ProviderApp:get

public class ProviderApp {
    private Calculator calculator = new CalculatorImpl();

    public static void main(String[] args) throws IOException {
        new ProviderApp().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();
        }
    }}

Server端主要是经过ServerSocket的accept方法,来接收Client端的请求,接着就是反序列化请求->执行->序列化执行结果,最后将二进制格式的执行结果返回给Client。it

就这样咱们实现了一个简陋而又详细的RPC。  说它简陋,是由于这个实现确实比较挫,在下一小节会说它为何挫。说它详细,是由于它一步一步的演示了一个RPC的执行流程,方便你们了解RPC的内部机制。

相关文章
相关标签/搜索