源码模仿之RPC

源码模仿之RPC

RPC - 远程过程调用,概念不多赘述,可自行百度。

场景

  • 统一api接口
  • 生产者(提供远程接口调用方)
  • 使用者(主动调用远程接口)

代码实现

API接口(公共依赖包)

DemoEntity (实体类)

/**
 * 测试对象
 * @author GaoYuan
 */
public class DemoEntity implements Serializable{

    private String id;
    private String name;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

公共接口(需要预先约定好)

public interface IDemoService {
    DemoEntity getDemo(String id);
}
远程调用提供者

DemoService (需要接口的具体实现)

/**
 * 接口具体实现
 * @author GaoYuan
 */
public class DemoService implements IDemoService {

    @Override
    public DemoEntity getDemo(String id) {
        DemoEntity demoEntity = new DemoEntity();
        demoEntity.setId("1");
        demoEntity.setName("gaoyuan");
        return demoEntity;
    }
}

ProviderStarter (提供者启动类)

/**
 * 接口提供者
 * @author GaoYuan
 */
public class ProviderStarter {

    public static void main(String[] args){
        try {
            // 搭建 8080 端口的socket监听服务
            ServerSocket serverSocket = new ServerSocket(8989);
            while (true){
                Socket socket = serverSocket.accept();
                // 获取socket的输入流(即其他服务进行请求的参数,主要是获取调用的类名、方法名、参数等)
                ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());

                String className = objectInputStream.readUTF();
                System.out.println(">>>>>> [接收]:" + className);
                String methodName = objectInputStream.readUTF();
                System.out.println(">>>>>> [接收]:" + methodName);
                Class[] methodParameterTypes = (Class[]) objectInputStream.readObject();
                System.out.println(">>>>>> [接收]:" + methodParameterTypes);
                Object[] methodArgs = (Object[]) objectInputStream.readObject();
                System.out.println(">>>>>> [接收]:" + methodArgs);

                Class myclass = null;

                // 这里主要是解决如何通过接收到的类名为"IDemoService" 转换为 具体的实现"DemoService"
                if(IDemoService.class.getName().equals(className)){
                    // 此时 myclass已经是具体的实现类了
                    myclass = ClassLoader.getSystemClassLoader().loadClass("com.foruo.simple.rpc.provider.DemoService");
                    // myclass = DemoService.class; // 也可以用这种方式获取class
                    System.out.println(">>>>>> [组装]:指定实现类DemoService");
                }

                // 获取具体实现类的方法
                Method method = myclass.getMethod(methodName, methodParameterTypes);
                System.out.println(">>>>>> [组装]:指定方法");
                // 获取实现类方法执行的结果
                Object invoke = method.invoke(myclass.newInstance(), methodArgs);
                System.out.println(">>>>>> [执行]:指定实现类的指定方法");
                System.out.println(">>>>>> [执行]:获取方法执行结果");
                System.out.println(">>>>>> [回执]:将执行结果输出");
                // 将执行结果输出
                ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
                objectOutputStream.writeObject(invoke);
                objectOutputStream.flush();

                // 关闭流
                objectInputStream.close();
                objectOutputStream.close();
                // 关闭socket
                socket.close();
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
}
消费者(使用方)

ConsumerStarter

/**
 * 消费者 - 使用远程接口
 * @author GaoYuan
 */
public class ConsumerStarter {

    public static void main(String[] args){
        // 这里实际上相当于获取了IDemoService的实现
        IDemoService demoService = (IDemoService)simpleRpc(IDemoService.class);
        // 执行getDemo方法时,相当于调用了代理的处理方法
        DemoEntity demoEntity = demoService.getDemo("1");
        System.out.println(demoEntity.getName());
    }

    /**
     * 远程调用具体代码
     * @author GaoYuan
     */
    public static Object simpleRpc(Class myClass){
        return Proxy.newProxyInstance(myClass.getClassLoader(), new Class[]{myClass}, (proxy, method, args) -> {
            // socket连接8080端口
            Socket socket = new Socket("127.0.0.1", 8989);

            // 获取类名 (这里是获取 IDemoService 接口名)
            String className = myClass.getName();
            // 获取方法名
            String methodName = method.getName();
            // 获取方法参数
            Class[] methodParameterTypes = method.getParameterTypes();

            /**
             * 这里主要将 需要调用的目标方法(包含对象类名/接口名)以socket形式传递过去,以调用远程接口
             * 就是告诉远程接口,我想要调用什么方法
             * */
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            objectOutputStream.writeUTF(className);
            System.out.println(">>>>>> [发送]:" + className);
            objectOutputStream.writeUTF(methodName);
            System.out.println(">>>>>> [发送]:" + methodName);
            objectOutputStream.writeObject(methodParameterTypes);
            System.out.println(">>>>>> [发送]:" + methodParameterTypes);
            objectOutputStream.writeObject(args);
            System.out.println(">>>>>> [发送]:" + args);
            objectOutputStream.flush();

            /** 获取远程接口执行结果 */
            ObjectInputStream objectInputStream = new ObjectInputStream(socket.getInputStream());
            Object o = objectInputStream.readObject();
            System.out.println(">>>>>> [接收]:" + o);
            // 关闭流
            objectInputStream.close();
            objectOutputStream.close();
            // 关闭socket
            socket.close();

            return o;
        });
    }
}

执行

先启动 ProviderStarter ,再启动 ConsumerStarter

输出

ProviderStarter

>>>>>> [接收]:com.foruo.simple.rpc.service.IDemoService
>>>>>> [接收]:getDemo
>>>>>> [接收]:[Ljava.lang.Class;@3c5a99da
>>>>>> [接收]:[Ljava.lang.Object;@47f37ef1
>>>>>> [组装]:指定实现类DemoService
>>>>>> [组装]:指定方法
>>>>>> [执行]:指定实现类的指定方法
>>>>>> [执行]:获取方法执行结果
>>>>>> [回执]:将执行结果输出

ConsumerStarter

>>>>>> [发送]:com.foruo.simple.rpc.service.IDemoService
>>>>>> [发送]:getDemo
>>>>>> [发送]:[Ljava.lang.Class;@6a5fc7f7
>>>>>> [发送]:[Ljava.lang.Object;@6fadae5d
>>>>>> [接收]:com.foruo.simple.rpc.entity.DemoEntity@7a5d012c
gaoyuan

可见,最终消费者获取到了远程调用的返回值。

博客

开源中国博客地址

https://my.oschina.net/gmarshal

个人博客地址

http://blog.foruo.top

欢迎关注我的个人微信订阅号:(据说这个头像程序猿专用)

输入图片说明

转载于:https://my.oschina.net/gmarshal/blog/2209176

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值