手寫RPC框架(三)實現服務註冊
阿新 • • 發佈:2021-12-01
手寫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