使用 Socket和动态代理以及反射 实现一个简易的 RPC 调用

avatar
作者
筋斗云
阅读量:0

使用 Socket、动态代理、反射 实现一个简易的 RPC 调用


我们前面有一篇 socket 的文章,再之前,还有一篇 java动态代理的文章,本文用到了那两篇文章中的知识点,需要的话可以回顾一下。


下面正文开始:

我们的背景是一个名为cuihua-snack(翠花小吃店)的客户端,要调用cuihua-core(翠花核心厨房)的服务端的接口,要求采用RPC的方式调用。
首先,我们创建两个项目cuihua-snack、cuihua-core
在这里插入图片描述

在这里插入图片描述
其中,cuihua-core 包含两个模块(关于 maven的模块化开发 可以再去了解一下),core-api中定义了客户端与服务端共同需要的接口和参数;core-service 则是服务端的主要逻辑。

我们有这样一张系统调用关系示意图。
在这里插入图片描述
① 服务端启动socket服务;
② 客户端开始调用,通过代理的方式调用;
③ 代理中包含 socket 客户端,与服务端建立连接后,将请求接口的对象信息封装后进行序列化。
④ 服务端接收到 socket 请求后,反序列化拿到请求的对象信息。
⑤ 通过反射,调用接口实现类的方法并返回结果。
⑥ 服务端对执行结果序列化并通过socket 返回给客户端。
⑦ 客户端对服务端返回的数据进行反序列化后,输出。
以上七个步骤,就是整个简易RPC的调用过程。


下面我们来看代码:
首先看 core-api 中的代码:
在这里插入图片描述

/**  * 上菜服务  */ public interface DishService {     String servePickedChineseCabbage(RequestDTO dto); } 
/**  * 上菜requestDTO  */ @Data public class RequestDTO implements Serializable {     /**      * 菜量      */     private String quantityType;//big;medium;small;     /**      * 是否加辣      */     private boolean spicy;     /**      * 冷热      */     private String coldOrHot;//cold/hot } 
/**  * RPC Request  */  @Data public class RpcRequest implements Serializable {     private String className;     private String methodName;     private Object[] args;     private Class[] types;  } 

再来看 core-service的代码:
在这里插入图片描述

public class DishServiceImpl implements DishService {     public String servePickedChineseCabbage(RequestDTO dto) {         System.out.println(dto);         return "Please wait a moment! The dish you want will come soon!";     } } 
public class RpcService {     private DishService service;     public RpcService(DishService service){         this.service = service;     }     public void serverRun() throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {         ServerSocket serverSocket = new ServerSocket(8000);         while(true) {             Socket socket = serverSocket.accept();             handler(socket);         }      }      private void handler(Socket socket) throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {         //读取 socket 传输的数据         ObjectInputStream objectinputstrem = new ObjectInputStream(socket.getInputStream());         RpcRequest rpcRequest = (RpcRequest) objectinputstrem.readObject();         String result = (String) this.invoke(rpcRequest);         //将结果写回 socket         ObjectOutputStream objectOutputStream = new ObjectOutputStream(socket.getOutputStream());         objectOutputStream.writeObject(result);         objectOutputStream.flush();     }      private Object invoke(RpcRequest request) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException {         //通过反射进行服务的调用         Class clazz=Class.forName(request.getClassName());         //找到目标方法         Method method=clazz.getMethod(request.getMethodName(),request.getTypes());         return method.invoke(service,request.getArgs());     } } 
public class App  {     public static void main( String[] args ) throws IOException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, IllegalAccessException {         DishService dishService = new DishServiceImpl();         RpcService rpcService = new RpcService(dishService);         rpcService.serverRun();     } } 

最后,我们来看 cuihua-snack的代码:
在这里插入图片描述

public class CuihuaInvocationHandler implements InvocationHandler {      private String host;     private int port;     public CuihuaInvocationHandler(String host,int port){         this.host = host;         this.port = port;     }      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {         //开启客户端         SocketClient socketClient = new SocketClient(host,port);         //请求参数         RpcRequest rpcRequest = new RpcRequest();         rpcRequest.setArgs(args);         rpcRequest.setTypes(method.getParameterTypes());         rpcRequest.setClassName(method.getDeclaringClass().getName());         rpcRequest.setMethodName(method.getName());         //发送         Object obj = socketClient.transMessage(rpcRequest);          //响应结果         return obj;     } } 
/**  * 代理服务  */ public class ProxyDishService {      public  <T> T  getInstance(Class<T> clazz){         return (T) Proxy.newProxyInstance(clazz.getClassLoader(),new Class<?>[]{clazz},new CuihuaInvocationHandler("localhost",8000));     } } 
public class SocketClient {     private String host;     private int port;      public SocketClient(String host,int port){         this.host = host;         this.port = port;     }      public Socket getSocket() throws IOException {         Socket socket = new Socket(host,port);         return socket;     }      public Object transMessage(RpcRequest request){         ObjectOutputStream objectOutputStream = null;         ObjectInputStream objectInputStream = null;          try {             Socket socket = getSocket();             //将RpcRequest 写入到输出流中,通过socket传递给服务端             objectOutputStream = new ObjectOutputStream(socket.getOutputStream());             objectOutputStream.writeObject(request);             objectOutputStream.flush();              //通过输入流,读取服务端通过socket返回的结果             objectInputStream = new ObjectInputStream(socket.getInputStream());             Object obj = objectInputStream.readObject();             return obj;         }catch(Exception e){             e.printStackTrace();         }finally{             try {                 objectInputStream.close();                 objectOutputStream.close();             } catch (IOException e) {                 e.printStackTrace();             }         }         return null;     } } 
public class App  {     public static void main( String[] args )     {         //调用代理         ProxyDishService proxy = new ProxyDishService();         RequestDTO dto = new RequestDTO();         dto.setColdOrHot("hot");         dto.setSpicy(true);         dto.setQuantityType("big");         String responseStr = proxy.getInstance(DishService.class).servePickedChineseCabbage(dto);         System.out.println("响应结果:"+responseStr);      } } 

执行 cuihua-core 中 core-service 下的 App 的main方法,启动 ServerSocket;
然后,执行cuihua-snack 中 App 的 main方法,socket客户端发起调用。
服务端打印日志如下:

RequestDTO(quantityType=big, spicy=true, coldOrHot=hot) 

客户端打印日志如下:

响应结果:Please wait a moment! The dish you want will come soon! 

以上就是 《使用 Socket和动态代理以及反射 实现一个简易的 RPC 调用》的全部内容,感谢阅读!

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!