手写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

浙公网安备 33010602011771号