面试官不讲码德,欺负我一个年轻的开发工程师

面试官不讲码德,欺负我一个年轻的开发工程师,问如果是你怎么设计RPC? RPC也不是很难啊,教你如何使用socket加动态代理与反射实现Rpc

先来解释解释一下rpc,首先很多人以为rpc是一种协议,其实这个就是出错误的,rpc:是远程过程调用;
看他的全程英文Remote Position Control 他其实是一种设计思想而已,解决分布式各个系统之间的调用关系。
我们今天就用socket方式实现一套rpc调用框架,不多说上代码

package rpc.socket;
//先定义一个clinet接口
public interface Clinet<T> {
        T  getService(Class<T> tClass);
        
}

这是我个人写的一个实现类,给位大牛可以尝试实现
这里是采用动态代理。把调用大的过程交给代理对象,这样就可以屏蔽掉底层的网络和整个调用过程,
对于客服而言只用给一个接口的class对象,他会帮你去找到服务端实现类,实现远程调用

package rpc.socket;

import java.io.*;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;
import java.util.Properties;

public class RpcClint implements Clinet {
final static String  RPC_SERVER_HOST="RPC_SERVER_HOST";
final static String  RPC_SERVER_PORT="RPC_SERVER_PORT";
    @Override
    public Object getService(Class aClass) {
       return Proxy.newProxyInstance(aClass.getClassLoader(), new Class[]{aClass}, new InvocationHandler() {
           @Override
           public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
               //读取配置文件
               Properties properties = new Properties();
               InputStream resourceAsStream = aClass.getClassLoader().getResourceAsStream("rpc/RpcConfig.properties");
               properties.load(resourceAsStream);
               String host = properties.getProperty(RPC_SERVER_HOST);
               Integer port =new Integer((String) properties.get(RPC_SERVER_PORT));
               //RPC注册过程
               Socket socket = new Socket(host, port);
               OutputStream in= socket.getOutputStream();
               ObjectOutputStream objectOutputStream = new ObjectOutputStream(in);

               //告诉服务端调用的是哪个类
               objectOutputStream.writeObject(aClass.getName());
               //告诉服务端调用的是哪个方法
               objectOutputStream.writeObject(method.getName());
               //告诉服务端调用方法传入的参数
               objectOutputStream.writeObject(args);
               //告诉服务端调用方法的参数类型
               objectOutputStream.writeObject(method.getParameterTypes());

               //完成序列化,刷新
               objectOutputStream.flush();

               //接受服务端响应结果
               ObjectInputStream  returnObj=new ObjectInputStream(socket.getInputStream());
               Object o = returnObj.readObject();


               //关闭流
               objectOutputStream.close();
               returnObj.close();
               resourceAsStream.close();
               socket.close();
               return o;
           }
       });
    }
}

这是服务端接口

package rpc.socket;

import java.io.IOException;

public interface Server {
    void Handler() throws IOException;
}

这是我实现的服务端rpc,就是采用一个nio接受客服端数据,然后去调用服务端的方法

package rpc.socket;

import rpc.socket.service.GODService;
import rpc.socket.service.GODServiceImpl;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

public class RpcServer implements Server {
    //装用实现类的bena
    static Map<String,Class> RESmap;
    //加载所有需要注册的额服务
    static {
        //这里可以用Aop 加注解的方式来把需要暴露的服务,注册到服务列表里面去
        RESmap=new ConcurrentHashMap<>();

        //把服务注册到注册表
        RESmap.put(GODService.class.getName(),GODServiceImpl.class);

    }

    @Override
    public void Handler() throws IOException {
        ServerSocket serverSocket = new ServerSocket(8080);

        //采取传统的bio处理
        while (true){
            //等待客服端链接
            Socket accept = serverSocket.accept();

            new Thread(()->{
                try {
                    //等待客户端输入
                    ObjectInputStream in= new ObjectInputStream(accept.getInputStream());
                    //获取客户端传过来的类名称
                    String className = (String) in.readObject();

                    //获取客户端客服端传过来的方法名称
                    String methodName=(String) in.readObject();

                    //获取客户端传过来的参数
                    Object[] args = (Object[]) in.readObject();

                    //获取客服的端传过来的参数类型
                    Class[] argsType = (Class[]) in.readObject();

                    //从注册表中获取服务的字节码
                    Class aClass = RESmap.get(className);
                    //通过字节码对象获取构造器
                    Constructor constructor = aClass.getConstructor();
                    constructor.setAccessible(true);
                    //通过反射的方式创建对象并且执行对象的方法
                    Object invoke = aClass.getMethod(methodName,argsType).invoke(constructor.newInstance(), args);

                    //把返回结果写回给客户端
                    ObjectOutputStream returnObject=new ObjectOutputStream(accept.getOutputStream());
                    returnObject.writeObject(invoke);
                    //关闭流
                    in.close();
                    returnObject.close();
                    accept.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }).start();


        }


    }


}

这里我们来建立一个服务端
首先定义一个接口

package rpc.socket.service;

public interface GODService {

    String getGod(String lockMessage,String  offerings);
}

实现类

package rpc.socket.service;

public class GODServiceImpl implements  GODService {
    @Override
    public String getGod(String lockMessage, String  offerings) {
        System.out.println("的供品:"+ offerings);
        System.out.println("你的愿望:"+lockMessage);
        return "年轻人还是少做梦!";
    }
}

写一个服务端的启动类

package rpc.socket.demo;

import rpc.socket.RpcServer;

import java.io.IOException;

public class ServiceStart {
    public static void main(String[] args) throws IOException {
        new RpcServer().Handler();
    }
}

然后是客服端去调用

package rpc.socket.demo;

import rpc.socket.Clinet;
import rpc.socket.RpcClint;
import rpc.socket.service.GODService;

public class clintDemo {
    public static void main(String[] args) {
        Clinet Clint = new RpcClint();
        GODService service = (GODService)Clint.getService(GODService.class);
        String lockReturn = service.getGod("请给让我中彩票吧", "献祭我老板的二十年寿命!");
        System.out.println(lockReturn);


    }
}

我们来看两遍的结果
客服端结果
在这里插入图片描述
我来看服务端结果

在这里插入图片描述
很显然调用成功了
现在我们来画图模拟一下调用过程
在这里插入图片描述你学废了吗

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值