写点什么

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

用户头像
小Q
关注
发布于: 2020 年 09 月 07 日

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 仓库中自取

链接在这里:gitee.com/biwangsheng…


发布于: 2020 年 09 月 07 日阅读数: 60
用户头像

小Q

关注

还未添加个人签名 2020.06.30 加入

小Q 公众号:Java架构师联盟 作者多年从事一线互联网Java开发的学习历程技术汇总,旨在为大家提供一个清晰详细的学习教程,侧重点更倾向编写Java核心内容。如果能为您提供帮助,请给予支持(关注、点赞、分享)!

评论

发布
暂无评论
就靠这几段代码,带你玩转rpc通信协议,不信你学不明白