手写RPC框架(三)实现服务注册


手写RPC框架(三)实现服务注册

RPC框架一般由服务端,消费端,注册中心三部分组成。注册中心负责持久化服务名称,IP地址以及端口等。本次只实现简单的服务注册功能。

  • 实现服务注册功能

    public class ServiceRegister {
        //负责存储服务列表
        private static List serviceList;
    
        //发布一个rpc服务
        public static void export(int port,Object... service) throws IOException {
            serviceList= Arrays.asList(service);
            ServerSocket server = new ServerSocket(port);
            Socket client=null;
            while (true){
                client=server.accept();
                new Thread(new Provider(client,serviceList)).start();
            }
        }
    }
    
    

    此时消费端获取服务类时直接在serviceList中获取

  • 服务端

        public Object getService(Class servicesClass){
            for(Object obj:serviceList){
                boolean isFather=servicesClass.isAssignableFrom(obj.getClass());
                if(isFather){
                    return obj;
                }
            }
            return null;
        }
    

    将服务端继承Runable接口,在发布一个RPC服务时,直接实例化该线程并启动该线程。

    public class Provider implements Runnable{    
    	private Socket client=null;
        private List serviceList=null;
        public Provider(Socket client,List services){
            this.client=client;
            this.serviceList=services;
        }
        public void run(){
            ObjectInputStream objectInputStream=null;
            ObjectOutputStream objectOutputStream=null;
            try{
                objectInputStream=new ObjectInputStream(client.getInputStream());
                objectOutputStream=new ObjectOutputStream(client.getOutputStream());
                RpcRequest rpcRequest=(RpcRequest)objectInputStream.readObject();
                // 读取类名
                Class serviceClass=(Class) rpcRequest.getServiceClass();
                Object obj=getService(serviceClass);
                if(obj==null){
                    throw new Exception("not service");
                }
                // 读取方法名
                String methodName=rpcRequest.getMethodName();
                // 读取方法入参类型
                Class<?>[] parameterTypes=rpcRequest.getParameterTypes();
                // 读取方法调用入参
                Object[] parameters=rpcRequest.getArguments();
                System.out.println(String.format("收到消费者远程调用请求:类名 = {%s},方法名 = {%s},调用入参 = %s,方法入参列表 = %s",
                        serviceClass, methodName, Arrays.toString(parameters), Arrays.toString(parameterTypes)));
                Method method = obj.getClass().getMethod(methodName,parameterTypes);
                Object invoke = method.invoke(obj, parameters);
                System.out.println("方法调用结果:" + invoke);
                objectOutputStream.writeObject(invoke);
            } catch (IOException | ClassNotFoundException e) {
                e.printStackTrace();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        public Object getService(Class servicesClass){
            for(Object obj:serviceList){
                boolean isFather=servicesClass.isAssignableFrom(obj.getClass());
                if(isFather){
                    return obj;
                }
            }
            return null;
        }
    }
    
    
    
  • 进行服务注册

    public class RpcBootstrap {
        public static void main(String[] args) throws IOException {
            Calculator calculator=new CalculatorImpl();
            ServiceRegister.export(8081,calculator);
        }
    }
    
  • 消费端使用ip,port作为参数

    public class ConsumerProxy implements InvocationHandler {
    
        private String ip;
        private int port;
    
        public ConsumerProxy(String ip, int port) {
            this.ip = ip;
            this.port = port;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Exception {
            Socket socket = new Socket(ip,port);
            ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());
            RpcRequest rpcRequest = new RpcRequest(proxy.getClass().getInterfaces()[0], method.getName(), method.getParameterTypes(), args);
            objectOutputStream.writeObject(rpcRequest);
            ObjectInputStream inputStream = new ObjectInputStream(socket.getInputStream());
            return inputStream.readObject();
        }
    }
    
  • 获取代理对象

        public static T getService(Class clazz,String ip,int port){
            ConsumerProxy consumerProxy=new ConsumerProxy(ip,port);
            return (T)Proxy.newProxyInstance(ConsumerApp.class.getClassLoader(),new Class<?>[] {clazz},consumerProxy);
        }
    
  • 测试程序

        public static void main(String[] args) {
            Calculator calculator= ConsumerApp.getService(Calculator.class,"127.0.0.1",8081);
            int res=calculator.add(100,86);
            System.out.println(res);
        }
    

    image-20211106153640645

  • Proxy.newProxyInstance这个函数是用来返回代理对象的,我发现使用ConsumerApp.class.getClassLoader()Calculator.class.getClassLoader()作为第一个参数都是可以的,通过debug发现这两个类的类加载器一致,

    image-20211106154011361

    java在运行程序时,并不是一次性加载所有的类,而是把基础类加载到jvm中,再加载其他的类来节省开销。类加载是指JVM中加载class文件的过程(加载→验证→准备→解析→初始化),所有通过正常双亲委派模式的类加载器加载的classpath下的和ext下的所有类在方法区都是同一个类,堆中的Class实例也是同一个。

    JDK 默认提供了如下几种ClassLoader

    • Bootstrp loader
    • ExtClassLoader
    • AppClassLoader
    RPC