1. 程式人生 > 其它 >手寫RPC框架(三)實現服務註冊

手寫RPC框架(三)實現服務註冊

手寫RPC框架(三)實現服務註冊

RPC框架一般由服務端,消費端,註冊中心三部分組成。註冊中心負責持久化服務名稱,IP地址以及埠等。本次只實現簡單的服務註冊功能。

  • 實現服務註冊功能

    public class ServiceRegister {
        //負責儲存服務列表
        private static List<Object> 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<Object> serviceList=null;
        public Provider(Socket client,List<Object> 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> T getService(Class<T> 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);
        }
    

Proxy.newProxyInstance這個函式是用來返回代理物件的,我發現使用ConsumerApp.class.getClassLoader()Calculator.class.getClassLoader()作為第一個引數都是可以的,通過debug發現這兩個類的類載入器一致,

java在執行程式時,並不是一次性載入所有的類,而是把基礎類載入到jvm中,再載入其他的類來節省開銷。類載入是指JVM中載入class檔案的過程(載入→驗證→準備→解析→初始化),所有通過正常雙親委派模式的類載入器載入的classpath下的和ext下的所有類在方法區都是同一個類,堆中的Class例項也是同一個。

JDK 預設提供瞭如下幾種ClassLoader

  • Bootstrp loader
  • ExtClassLoader
  • AppClassLoader