手写rpc框架(1):能运行就行

3 篇文章 0 订阅

举个例子,什么是rpc。
首先,A,B是两个系统相互独立的系统。

现在,B系统需要A系统的数据,一般的做法是A系统提供一个a接口,B系统通过get,post等方式,用http调用,获取A系统的数据。

rpc框架的目的就是要让两个系统的调用变得简单,B系统调用A系统,就像调用本地的类一样快捷。

我们先使用socket进行连接,如果不太熟悉socket通讯,可以看这一段精炼的代码了解一下
socket连接通讯
以上代码运行时先启动server类,再启动client类。

我们开始简单的rpc框架搭建吧。
demo预览如下:
在这里插入图片描述

public class ComsumerApp {

    public static void main(String[] args) {

        Calculator calculator = new CalculatorRemoteImpl();
        int result = calculator.add(1, 2);
        String str = calculator.send("hello rpc");

    }
}
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 socket = new Socket(address, PORT);
            OutputStream outputStream = socket.getOutputStream();//获取一个输出流,向服务端发送信息
            // 将请求序列化
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
            CalculateRpcRequest calculateRpcRequest = generateRequest(a, b);
            // 将请求发给服务提供方
            objectOutputStream.writeObject(calculateRpcRequest);

            // 将响应体反序列化
            ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
            Object response = objectInputStream.readObject();
            System.out.println("response=" + 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;
    }

    @Override
    public String send(String str) {
        List<String> addressList = lookupProviders("Calculator.add");
        String address = chooseTarget(addressList);
        try {
            //创建Socket对象
            Socket socket = new Socket(address, PORT);
            OutputStream outputStream = socket.getOutputStream();//获取一个输出流,向服务端发送信息
            // 将请求序列化
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
            SendRpcRequest sendRpcRequest = generateRequest(str);
            // 将请求发给服务提供方
            objectOutputStream.writeObject(sendRpcRequest);

            // 将响应体反序列化
            ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
            Object response = objectInputStream.readObject();
            System.out.print("response=" + response);

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

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

    private SendRpcRequest generateRequest(String str) {
        SendRpcRequest calculateRpcRequest = new SendRpcRequest();
        calculateRpcRequest.setStr(str);
        calculateRpcRequest.setMethod("send");
        return calculateRpcRequest;
    }
}
public class ProviderApp {
    private static Logger log = LoggerFactory.getLogger(ProviderApp.class);

    private Calculator calculator = new CalculatorImpl();

    public static void main(String[] args) throws IOException {
        new ProviderApp().run();
    }

    private void run() throws IOException {
        //创建ServerSocket对象,绑定并监听端口
        ServerSocket listener = new ServerSocket(9090);
        System.out.println("服务端已启动,等待客户端连接..");
        try {
            while (true) {
                //侦听并接受到此套接字的连接,返回一个Socket对象
                Socket socket = listener.accept();
                try {
                    //得到一个输入流,接收客户端传递的信息
                    InputStream inputStream = socket.getInputStream();
                    // 将请求反序列化
                    ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
                    Object object = objectInputStream.readObject();
                    System.out.print("object=" + object);


                    ObjectOutputStream objectOutputStream = null;
                    // 调用服务
                    int result = 0;
//                      instanceof 严格来说是Java中的一个双目运算符,用来测试一个对象是否为一个类的实例,用法为: boolean result = obj instanceof Class
                    if (object instanceof CalculateRpcRequest) {
                        CalculateRpcRequest calculateRpcRequest = (CalculateRpcRequest) object;
                        if ("add".equals(calculateRpcRequest.getMethod())) {
                            // 返回结果
                            result = calculator.add(calculateRpcRequest.getA(), calculateRpcRequest.getB());
                            objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
                            objectOutputStream.writeObject(new Integer(result));
                        } else {
                            throw new UnsupportedOperationException();
                        }
                    }

                    String result2 = null;
                    if (object instanceof SendRpcRequest) {
                        SendRpcRequest sendRpcRequest = (SendRpcRequest) object;
                        if ("send".equals(sendRpcRequest.getMethod())) {
                            // 返回结果
                            result2 = calculator.send(sendRpcRequest.getStr());
                            objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
                            objectOutputStream.writeObject(new String(result2));
                        } else {
                            throw new UnsupportedOperationException();
                        }
                    }


                } catch (Exception e) {
                    log.error("fail", e);
                } finally {
                    socket.close();
                }
            }
        } finally {
            listener.close();
        }
    }

}
public interface Calculator {
    int add(int a, int b);
    String send(String str);
}

public class CalculatorImpl implements Calculator {
    @Override
    public int add(int a, int b) {
        int c = a + b;
        System.out.print("result=" + c);
        return c;
    }

    @Override
    public String send(String str) {
        System.out.print("接收到的消息" + str);
        return "接收到的消息" + str;
    }
}
@Data
public class SendRpcRequest implements Serializable {
    private static final long serialVersionUID = 7503710091945320739L;

    private String method;
    private String str;

    @Override
    public String toString() {
        return "CalculateRpcRequest{" +
                "method='" + method + '\'' +
                ", str=" + str +
                '}';
    }
}
@Data
public class CalculateRpcRequest implements Serializable {

    private static final long serialVersionUID = 7503710091945320739L;

    private String method;
    private int a;
    private int b;
    @Override
    public String toString() {
        return "CalculateRpcRequest{" +
                "method='" + method + '\'' +
                ", a=" + a +
                ", b=" + b +
                '}';
    }
}

运行时,先启动ProviderApp
在这里插入图片描述

再启动ComsumerApp,可以看到打印结果

在这里插入图片描述

运行成功,现在我们捋一捋原理。
我们首先启动了ProviderApp,该方法通过socket 绑定并监听9090端口,

之后启动了ComsumerApp,该方法 实例化了 Calculator接口,并调用 它的两个方法add()和 send()。

Calculator接口中的两个方法在provider中的实现如下:
在这里插入图片描述

在这里插入图片描述
如果对ssm有经验,这种写法是不是很熟悉。

请注意,ComsumerApp在实例化 Calculator 时,调用的却是自己的CalculatorRemoteImpl() 方法
在这里插入图片描述
进入内部,可以看到这里其实是通过socket来连接provider。

在这里插入图片描述

其中的关键是,comsumer发送了一个 calculateRpcRequest 类给
provider。

将provider设置为断点,启动comsumer,可以看到
在这里插入图片描述
calculateRpcRequest的内容是方法名add,还有add所需的两个参数。
现在ProviderApp知道了方法名和参数,就调用本地的方法获取结果,并将结果返回给comsumer,过程结束。

整体而言,
两个系统通过socket连接,comsumer调用provider的add()方法和send()方法,会在自己的 CalculatorRemoteImpl实现类中,将参数序列化,通过socket发送给 server, server将参数反序列化后,再调用真正的方法进行处理,将结果序列化返回给provider。

总之,这堆demo能跑了,我们来说说这对代码的缺点。

1.provider类只提供了send和add两个方法,如果我们要调用provider的更多方法,难道每个方法都要在 CalculatorRemoteImpl 写一次socket吗?

2.我们通过socket远程连接,有没有更好的方法?netty

3.如果provider类挂了,而cosumer类还在调用,你猜旅长怎么说。

4.我们不仅要调用provider,还要providerB,providerC······多了怎么办?

5.同样,有comsumer,comsumerB,comsumerC,调用provider,顶得住吗?

我们一步一步来解决这些问题~

git 连接
https://github.com/bobly2/rpc_0.1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值