您现在的位置是:首页 > 文章详情

就靠这几段代码,带你玩转rpc通信协议,不信你学不明白

日期:2020-09-07点击:550

RPC(Remote Procedure Call Protocol)——远程过程调用协议,它是一种通过网络从远程计算机程序上请求服务,而不需要了解底层网络技术的协议。该协议允许运行于一台计算机的程序调用另一台计算机的子程序,而程序员无需额外地为这个交互作用编程。RPC协议假定某些传输协议的存在,如TCP或UDP,为通信程序之间携带信息数据。在OSI网络通信模型中,RPC跨越了传输层和应用层。RPC使得开发包括网络分布式多程序在内的应用程序更加容易。

2.基本概念

  • RPC(Remote Procedure Call)远程过程调用,简单的理解是一个节点请求另一个节点提供的服务

  • 本地过程调用:如果需要将本地student对象的age+1,可以实现一个addAge()方法,将student对象传入,对年龄进行更新之后返回即可,本地方法调用的函数体通过函数指针来指定。

  • 远程过程调用:上述操作的过程中,如果addAge()这个方法在服务端,执行函数的函数体在远程机器上,如何告诉机器需要调用这个方法呢?


今天,我们就通过一个实例代码进行演示,一步步的查看,rpc的通信是如何进行的,有兴趣的朋友可以把代码进行实现,自己debug一下,查看每一步的传参

1

package com.mashibing.rpc.common;

import java.io.Serializable;

public class User implements Serializable {
   private static final long serialVersionUID = 1L;

   private Integer id;
   private String name;

   public User(Integer id, String name) {
       this.id = id;
       this.name = name;
  }

   public Integer getId() {
       return id;
  }

   public String getName() {
       return name;
  }

   public void setId(Integer id) {
       this.id = id;
  }

   public void setName(String name) {
       this.name = name;
  }

   @Override
   public String toString() {
       return "User{" +
               "id=" + id +
               ", name='" + name + '\'' +
               '}';
  }
}
package com.mashibing.rpc.common;

public interface IUserService {
   public User findUserById(Integer id);
}
package com.mashibing.rpc01;


import com.mashibing.rpc.common.IUserService;
import com.mashibing.rpc.common.User;

public class UserServiceImpl implements IUserService {
   @Override
   public User findUserById(Integer id) {
       return new User(id, "Alice");
  }
}
package com.mashibing.rpc01;

import com.mashibing.rpc.common.User;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;

public class Client {
   public static void main(String[] args) throws Exception {
       Socket s = new Socket("127.0.0.1", 8888);
       ByteArrayOutputStream baos = new ByteArrayOutputStream();
       DataOutputStream dos = new DataOutputStream(baos);
       dos.writeInt(123);

       s.getOutputStream().write(baos.toByteArray());
       s.getOutputStream().flush();

       DataInputStream dis = new DataInputStream(s.getInputStream());
       int id = dis.readInt();
       String name = dis.readUTF();
       User user = new User(id, name);

       System.out.println(user);

       dos.close();
       s.close();
  }
}

2

package com.mashibing.rpc02;

import com.mashibing.rpc.common.User;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.net.Socket;

public class Stub {
   public User findUserById(Integer id) throws Exception {
       Socket s = new Socket("127.0.0.1", 8888);
       ByteArrayOutputStream baos = new ByteArrayOutputStream();
       DataOutputStream dos = new DataOutputStream(baos);
       dos.writeInt(123);

       s.getOutputStream().write(baos.toByteArray());
       s.getOutputStream().flush();

       DataInputStream dis = new DataInputStream(s.getInputStream());
       int receivedId = dis.readInt();
       String name = dis.readUTF();
       User user = new User(id, name);

       dos.close();
       s.close();
       return user;
  }
}

3

package com.mashibing.rpc03;

import com.mashibing.rpc.common.IUserService;
import com.mashibing.rpc.common.User;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;

/**
* 而且Client的调用显得不是很合理(Stub里只有findById的代码),如果有个findByName的新方法,那么就又得重新改进
* 下面这种写法解决了方法增加的问题
*/


public class Stub {
   public static IUserService getStub() {
       InvocationHandler h = new InvocationHandler() {
           @Override
           public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
               Socket s = new Socket("127.0.0.1", 8888);

               ByteArrayOutputStream baos = new ByteArrayOutputStream();
               DataOutputStream dos = new DataOutputStream(baos);
               dos.writeInt(123);

               s.getOutputStream().write(baos.toByteArray());
               s.getOutputStream().flush();

               DataInputStream dis = new DataInputStream(s.getInputStream());
               int id = dis.readInt();
               String name = dis.readUTF();
               User user = new User(id, name);

               dos.close();
               s.close();
               return user;
          }
      };
       Object o = Proxy.newProxyInstance(IUserService.class.getClassLoader(), new Class[] {IUserService.class}, h);
       return (IUserService)o;
  }
}

4

package com.mashibing.rpc04;

import com.mashibing.rpc.common.IUserService;
import com.mashibing.rpc.common.User;

import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;

/**
* 但是这里仅仅实现了findByUserId的方法代理,如果要实现其他方法的代理该怎么做呢?
* 这里就要从协议层做出改进
*
* 服务器端也要做出对应处理
*/


public class Stub {
   public static IUserService getStub() {
       InvocationHandler h = new InvocationHandler() {
           @Override
           public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
               Socket s = new Socket("127.0.0.1", 8888);

               ObjectOutputStream oos = new ObjectOutputStream(s.getOutputStream());

               String methodName = method.getName();
               Class[] parametersTypes = method.getParameterTypes();
               oos.writeUTF(methodName);
               oos.writeObject(parametersTypes);
               oos.writeObject(args);
               oos.flush();


               DataInputStream dis = new DataInputStream(s.getInputStream());
               int id = dis.readInt();
               String name = dis.readUTF();
               User user = new User(id, name);

               oos.close();
               s.close();
               return user;
          }
      };
       Object o = Proxy.newProxyInstance(IUserService.class.getClassLoader(), new Class[] {IUserService.class}, h);
       return (IUserService)o;
  }




}
package com.mashibing.rpc04;

import com.mashibing.rpc.common.IUserService;
import com.mashibing.rpc.common.User;

import java.io.*;
import java.lang.reflect.Method;
import java.net.ServerSocket;
import java.net.Socket;

public class Server {
   private static boolean running = true;
   public static void main(String[] args) throws Exception {
       ServerSocket ss = new ServerSocket(8888);
       while (running) {
           Socket s = ss.accept();
           process(s);
           s.close();
      }
       ss.close();
  }

   private static void process(Socket s) throws Exception {
       InputStream in = s.getInputStream();
       OutputStream out = s.getOutputStream();
       ObjectInputStream oos = new ObjectInputStream(in);
       DataOutputStream dos = new DataOutputStream(out);

       String methodName = oos.readUTF();
       Class[] parameterTypes = (Class[])oos.readObject();
       Object[] args = (Object[])oos.readObject();

       IUserService service = new UserServiceImpl();
       Method method = service.getClass().getMethod(methodName, parameterTypes);
       User user = (User)method.invoke(service, args);



       dos.writeInt(user.getId());
       dos.writeUTF(user.getName());
       dos.flush();
  }
}

5

返回值用Object封装,支持任意类型


就这样,直到最后的server端获取到数据,完成了一个数据传输,不知道大家能不能理解,最后,纸上得来终觉浅,我建议大家还是能够可以自己实现一下

关注我,一个思维跳脱并且有点洒脱的程序员,谢谢

最近在整理我这几年工作以及生活中对我帮助较大的视频以及资料,有需要的,可以去我git仓库中自取

链接在这里:https://gitee.com/biwangsheng/personal.git


本文分享自微信公众号 - Java架构师联盟(msbxq2019)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

原文链接:https://my.oschina.net/u/4472036/blog/4551213
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章