怎么通过Java代码来理解RPC

技术怎么通过Java代码来理解RPC本篇内容主要讲解“怎么通过Java代码来理解RPC”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“怎么通过Java代码来理解RPC”吧!这整

本文主要讲解“如何通过Java代码理解RPC”。感兴趣的朋友不妨看看。本文介绍的方法简单、快速、实用。让边肖学习“如何通过Java代码理解RPC”!

整个过程可以用一句话来概括:机器A通过网络与机器B建立连接,A向B发送一些参数,B执行某个过程,并将结果返回给A。

“在写代码之前,我们先说一个背景。假设我们有一个商品类别:

public class productimplementasserializable { private integerid;privateStringnamepublicProduct(Integerid,Stringname){ this . id=id;this.name=name}//toString()//getset方法}有商品服务接口:

public interface productservice { ProductgetProductById(Integerid);}服务器有商品服务接口的实现类:

public classproductserviceimplessproductservice { @ overrideepublicpproductgetproductbyid(整数ID){//其实我们应该在这里查询数据库获取数据,returnnewProduct(id,‘手机’)简化如下;}}接下来,我们通过客户端向服务器发送一个商品id。服务器获取id后,通过商品服务类获取商品信息,并返回给客户端:

class client { public static void main(string[]args)throwsexception {//Create socketsocket=new socket(' 127 . 0 . 0 . 1 ',8888);//获取输出流byteearrayoptstreams=new ytearrayoptstream();DataOutputStreamdos=new dataoutputstream(baos);//通过网络将商品标识发送到服务器nbs

p;       dos.writeInt(123);          socket.getOutputStream().write(baos.toByteArray());         socket.getOutputStream().flush();          //读取服务端返回的商品信息         DataInputStream dis = new DataInputStream(socket.getInputStream());         Integer id = dis.readInt();     //商品id         String name = dis.readUTF();    //商品名称         Product product = new Product(id, name);//通过服务端返回的商品信息生成商品          System.out.println(product);                  //关闭流资源为了方便阅读,没有做try-catch处理         dos.close();         baos.close();         socket.close();     } }  public class Server {     private static boolean running = true;      public static void main(String[] args) throws Exception {         //建立服务端Socket         ServerSocket ss = new ServerSocket(8888);         //不断监听,处理客户端请求         while (running) {             Socket socket = ss.accept();             process(socket);             socket.close();         }         ss.close();     }      private static void process(Socket socket) throws Exception {         InputStream is = socket.getInputStream();         OutputStream os = socket.getOutputStream();         DataInputStream dis = new DataInputStream(is);         DataOutputStream dos = new DataOutputStream(os);          //读取客户端发过来的id         Integer id = dis.readInt();         //调用服务类生成商品         IProductService service = new ProductServiceImpl();         Product product = service.getProductById(id);         //把商品的信息写回给客户端         dos.writeInt(id);         dos.writeUTF(product.getName());         dos.flush();          dos.close();         dis.close();         os.close();         is.close();     } }

上面的是 RPC  远程调用的原始简易版本,可以看到联网的代码写死在了客户端中,网络部分代码和getProductById()耦合在了一起,如果想把别的方法改为远程调用还要重新写一遍联网的代码,非常麻烦。

怎么通过Java代码来理解RPC

在实际的使用中,我们会编写各种各样的远程调用,打个比方,IProductService接口以后可能会扩展成这样:

public interface IProductService {      Product getProductById(Integer id);          Product getProductByName(String name);          Product getMostExpensiveProduct(); }

我们总不可能为每个方法都编写一段网络连接的代码吧,我们得想到一种办法为所有的方法都嵌入一段共用的网络连接代码。

那具体应该怎样嵌入呢?这里我们可以用到代理模式。

在 Java 中许多优秀的框架都用到了代理模式做代码嵌入,比如说 Mybatis。它把 JDBC 连接部分的代码通过代理模式嵌入到 sql  语句的周围,让我们专注于写 sql。

首先,服务端的代码要进行修改,由于多个方法共用一套联网的代码,我们要在服务端识别调用的是哪一个方法:

public class Server {      private static boolean running = true;      public static void main(String[] args) throws Exception {         //......     }      private static void process(Socket socket) throws Exception {         //获取输入流,输出流         InputStream is = socket.getInputStream();         OutputStream os = socket.getOutputStream();         ObjectInputStream ois = new ObjectInputStream(is);         ObjectOutputStream oos = new ObjectOutputStream(os);         //获取本次远程调用的方法名         String methodName = ois.readUTF();         //获取本次远程调用方法的参数类型         Class[] parameterTypes = (Class[]) ois.readObject();         //获取具体的参数对象         Object[] args = (Object[]) ois.readObject();                  //创建商品服务类实例,(往后这里可以继续优化)         IProductService service = new ProductServiceImpl();         //根据远程获取的方法名和参数,调用相应的方法         Method method = service.getClass().getMethod(methodName, parameterTypes);         Product product = (Product) method.invoke(service, args);         //把结果写回给客户端         oos.writeObject(product);          oos.close();         ois.close();         socket.close();     } }

然后在客户端,我们创建一个新的代理类,对外提供一个 getStub 获取代理类的方法。使用 JDK 的动态代理需要三个参数,一个是类加载器,一个是接口的  class 类,最后一个是InvocationHandler实例。

JDK 动态代理背后的逻辑是这样的:JVM 会根据接口的 class  类动态创建一个代理类对象,这个代理对象实现了传入的接口,也就是说它拥有了接口中所有方法的实现。方法具体的实现可以由用户指定,也就是调用InvocationHandler的invoke方法。

在invoke方法中有三个参数,分别是proxy代理类,method调用的方法,args调用方法的参数。我们可以在invoke方法中对具体的实现方法进行增强,在本案例中就是进行网络调用。
public class Stub {      public static IProductService getStub() {          InvocationHandler h = new InvocationHandler() {             @Override             public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {                 //和服务端建立Socket连接                 Socket socket = new Socket("127.0.0.1", 8888);                 ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());                 //拿到远程调用的方法名                 String methodmethodName = method.getName();                 //拿到远程调用方法的参数类型                 Class[] parametersTypes = method.getParameterTypes();                 //把方法名传递给服务端                 oos.writeUTF(methodName);                 //把方法参数类型传递给服务端                 oos.writeObject(parametersTypes);                 //把方法参数传递给服务端                 oos.writeObject(args);                 oos.flush();                 //获取远程调用的返回结果                 ObjectInputStream ois = new ObjectInputStream(socket.getInputStream());                 Product product = (Product) ois.readObject();                  ois.close();                 oos.close();                 socket.close();                 return product;             }         };         Object o = Proxy.newProxyInstance(IProductService.class.getClassLoader(), new Class[]{IProductService.class}, h);         return (IProductService) o;     } }

这个新版本比第一个版本又美好了一些,但是其实还可以继续优化。现在我们的代理只能够返回IProductService的实现类,得想办法让它返回任意类型的服务实现类。

怎么通过Java代码来理解RPC

思路和远程调用方法相似,在远程调用方法时,我们把方法的名称,参数类型,参数传递给服务端;现在要动态创建服务类,我们可以把服务接口的名字传给服务端。服务端拿到远程接口的名字后,就可以从服务注册表中找到对应服务实现类。

至于服务实现类如何注册到服务注册表,这里提供一个思路:可以考虑使用 Spring 的注解注入。这和我们平时写 spring  代码是相似的,在创建完服务实现类后我们会加上注解 @Service,这样我们就可以在收到远程调用后,遍历使用了  @Service的Bean,找到对应的实现类。

到此,相信大家对“怎么通过Java代码来理解RPC”有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/94344.html

(0)

相关推荐

  • gank是什么意思,王者荣耀gank是什么意思

    技术gank是什么意思,王者荣耀gank是什么意思王者荣耀gank很多网友还在猜测gank是什么意思,这到底是个什么意思,下面给出了详细的解释,我们来看看代表着什么吧,如果你还不知道这是什么意思的话就一起来看看王者荣耀g

    生活 2021年10月23日
  • 英文大小写26个字母,二十六个字母大小写小学英语

    技术英文大小写26个字母,二十六个字母大小写小学英语1、二十六个字母大小写书写顺序英文大小写26个字母:2、26个英文字母及发音音标如下: A a [ei] B b [bi:] C c [si:] D d [di:] E

    生活 2021年10月23日
  • 社保局什么时候上班,社保局几点上班

    技术社保局什么时候上班,社保局几点上班上午11:30和下午5:30 社保局什么时候上班。社会保险 (Social Insurance) 是一种为丧失劳动能力、暂时失去劳动岗位或因健康原因造成损失的人口提供收入或补偿的一种

    生活 2021年10月26日
  • ssh-keygen参数(公钥默认写入ssh目录下的什么文件)

    技术ssh-keygen的公钥格式是什么样的这篇文章给大家介绍ssh-keygen的公钥格式是什么样的,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。ssh-keygen 公钥格式公钥:公钥有2种格式

    攻略 2021年12月24日
  • Linux系统如何安装ST-Link v2烧录环境

    技术Linux系统如何安装ST-Link v2烧录环境这篇文章主要介绍Linux系统如何安装ST-Link v2烧录环境,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!1、首先我们需要安装必要的库和

    攻略 2021年11月18日
  • 抖音粉丝多少钱一个,抖音真人粉多少钱?

    技术抖音粉丝多少钱一个,抖音真人粉多少钱?关于到现在教大家抖音真人粉购买的方法这个话题,相信很多小伙伴都是非常有兴趣了解的吧,因为这个话题也是近期非常火热的,那么既然现在大家都想要知道教大家抖音真人粉购买的方法,小编也是

    测评 2021年11月10日