侧边栏壁纸
  • 累计撰写 21 篇文章
  • 累计创建 14 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

RPC基础理解

Administrator
2024-01-28 / 0 评论 / 0 点赞 / 6 阅读 / 38678 字


RPC简述

RPC是什么?

RPC (Remote Procedure Call)远程过程调用,这只是一个统称,重点在于方法调用(不支持对象的概念),具体实现甚至可以用RMI, RestFul等去实现,但一般不用,因为RMI不能跨语言,而RestFul效率太低。多用于服务器集群间的通信,因此常使用更加高效短小精悍的传输模式以提高效率。

从单机到分布式-->分布式通信 ,RPC就是分布式通信的一种方式

新建两个两个实现了序列化的基类User,Product:

User.java
import java.io.Serializable;
public class User implements Serializable {
    private static final long serialVersionUID = 1L;
    
    private Integer id;
    private Stringname
    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 +  '\'+
            '}'
   }
}

IUserService.java
public interface IUserService {
    public User  findUserById(Integer id);
}
​

Product.java
import java.io.Serializable;
public class Product implements Serializable{
    private static final long serialVersionUID = 1L;
    
    private Integer id;
    
    private String name;
    
    public Product(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(){}
} 

IProductService.java
public interface IProductService{
    public Product findProductById(Integer id);
}

IUserServiceImpl和IProductServiceImpl.java存放在Server端

IUserServiceImpl
public class IUserServiceImpl implements IUserService {
    @Override
    public User findUserById(Integer id) { 
        return new User(id, "name");
    }
}

IProductServiceImpl.java
public class IProductServiceImpl implements IProductService {
    @Override
    public User findProductById(Integer id) { 
        return new Product(id, "name");
    }
}

目标:

通过简单的七次迭代大概了解RPC底层实现流程

RPC实现方式:

通过动态代理等方式实现多类型多方法的服务器之间的远程调用(ps:了解给定api接口的情况下,服务器之间的通信底层是怎么运行的)

rpc-v0.1

  • 整体流程为Client 发送123到Server,Server端accept到数据进行处理,根据123查询数据库返回对象属性给客户端,客户端再根据属性重新new成对象

  • 弊端:传输过程和业务代码混合,且高度耦合,不容易修改

client.java
public class Client{
    public static void main(String[] args) throws Exception{
        Socket s = new Socket( host:"127.0..1", port: 8888);
        //网络上只能传输二进制,所以要创建一个bytearray 
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream dos = new DataOutputStream(baos):
        dos,writeInt( v: 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.printIn(user);
        dos,close();
        s.close();
    }
}

Server.java
public class Server{
    private static boolean running = true;
    public static void main(String[] args) throws Exception {
        ServerSocket ss = new ServerSocket( port: 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();
        DataInputStream dis = new DataInputStream(in);
        DataOutputStream dos = new DataOutputStream(out);
        
        int id = dis.readInt();
        IUserService service = new UserServiceImpl();
        User user = service.findUserById(id);
        dos.writeInt(user.getId());
        dos,writeUTF(user.getName());
        dos.flush();
    }
} 
​
​

rpc-v0.2

拆分客户端网络连接部分 新建Stub类

Client.java
 public class Client{
     public static void main(String[] args) throws Exception{
         Stub stub = new Stub();
         System.out.println(stub.findUserById(123));
     }
 } 

Stub.java

采用代理模式将网络链接部分写在一个新类里

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(id);
        
        s.getOutputstream().write(baos.toByteArray());
        s.getoutputstream().flush();
        DataInputstream dis = new DataInputstream(s.getInputstream0));
        int receivedId = dis.readIntO;
        String name = dis.readuTFO;
        User user = new User(receivedId, name);
        
        dos.c1ose();
        s.close();
        return user;
    }
}

Server.java
public class Server{
    private static boolean running = true;
    public static void main(String[] args) throws Exception {
        ServerSocket ss = new ServerSocket( port: 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();
        DataInputStream dis = new DataInputStream(in);
        DataOutputStream dos = new DataOutputStream(out);
        
        int id = dis.readInt();
        IUserService service = new UserServiceImpl();
        User user = service.findUserById(id);
        dos.writeInt(user.getId());
        dos,writeUTF(user.getName());
        dos.flush();
    }
} 
​
​

rpc-v0.3

  • 修改rpc02中只代理了一个类的问题,设计到代理模式里面的动态代理,只查看Client的写法,不考虑具体实现。(ps:动态代理是实现RPC的核心关键之一)

Client.java

public class Client {
    public static void main(String[] args) throws Exception{
        //getStub()返回的是一个代理类
        IUserService service = Stub.getStub();
        //service.findUserById调用的是Stub.getstub.InvocationHandTer().invoke(o, findUserById(), 123)
        System.out.printn(service.findUserById(123));
    }
}

Stub.java
public Class Stub{
    public static IUserService getstub() {
            InvocationHandler h = new InvocationHandTer(){
                @override
                public object invoke(object proxy, Method method, object[] args) throws
        Throwable{
                    socket s = new socket("127.0.0.1",8888);
                    ByteArrayOutputstream baos = new ByteArrayoutputstream0);
                    DataOutputstream dos = new DataOutputstream(baos);
                    dos.writeInt(args[0]);
                    
                    s.getoutputstream().write(baostoByteArray());
                    s.getoutputstream().flush();
                                              
                    DataInputstream dis = new DataInputstream(s.getInputstream());
                    int id = disreadInt();
                    String name = dis.readUTF();
                    User user = new User(id, name);
                                              
                    dos.close();
                    s.close();
                    return user;
​
                }
            };
            Object o= Proxy.newProxyInstance(IUserService.class.getclassLoader(),newClass[]{IUserService.class},h);
            //为方便处理,这里输出参数进行调试
            System.out.println(o.getClass().getName());
            System.out.println(o.getClass().getInterface());
            return (IUserService)o;
        }
}

image-20240127222958186

Server.java
public class Server{
    private static boolean running = true;
    public static void main(String[] args) throws Exception {
        ServerSocket ss = new ServerSocket( port: 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();
        DataInputStream dis = new DataInputStream(in);
        DataOutputStream dos = new DataOutputStream(out);
        
        int id = dis.readInt();
        IUserService service = new UserServiceImpl();
        User user = service.findUserById(id);
        dos.writeInt(user.getId());
        dos,writeUTF(user.getName());
        dos.flush();
    }
} 
​
​

rpc-v0.4

  • 修改Stub()只能通过写死的方式实现功能, 通过代理远程调用的方式调用服务器中的方法,同理Server端要新增处理操作

Client.java

public class Client {
    public static void main(String[] args) throws Exception{
        IUserService service = Stub.getStub();
        //findUserById()会调用Stub中的invoke方法
        System.out.printn(service.findUserById(123));
    }
}

Stub.java
public Class Stub{
    public static IUserService getstub() {
            InvocationHandler h = new InvocationHandTer(){
                @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[] parameterTypes = method.getParameterTypes();
                    
                    oos.writeUTF(methodName);
                    oos.writeObject(parameterTypes);
                    oos.writeObject(args);
                    oos.flush;    
                                 
                    DataInputstream dis = new DataInputstream(s.getInputstream());
                    int id = disreadInt();
                    String name = dis.readUTF();
                    User user = new User(id, name);
                                              
                    dos.close();
                    s.close();
                    return user;
​
                }
            };
            Object o= Proxy.newProxyInstance(IUserService.class.getclassLoader(),newClass[]{IUserService.class},h);
            //为方便处理,这里输出参数进行调试
            System.out.println(o.getClass().getName());
            System.out.println(o.getClass().getInterface());
            return (IUserService)o;
        }
}
​

Server.java
public class Server{
    private static boolean running = true;
    public static void main(String[] args) throws Exception {
        ServerSocket ss = new ServerSocket( port: 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();
        DataInputStream ois = new ObjectInputStream(in);
        DataOutputStream dos = new DataOutputStream(out);
        
        String methodName = ois.readUTF();
        Class[] parameterTypes = (Class[])ois.readObject();
        Object[] args = (0bject[])ois.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();
    }
} 

rpc-v0.5

  • 返回值用Object封装,支持任意类型修改Stub 和Server代码

Client.java
public class Client {
    public static void main(String[] args) throws Exception{
        IUserService service = Stub.getStub();
        //findUserById()会调用Stub中的invoke方法
        System.out.printn(service.findUserById(123));
    }
}

Stub.java
public Class Stub{
    public static IUserService getstub() {
            InvocationHandler h = new InvocationHandTer(){
                @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[] parameterTypes = method.getParameterTypes();
                    oos.writeUTF(methodName);
                    oos.writeObject(parameterTypes);
                    oos.writeObject(args);
                    oos.flush;    
                    
                    ObjectInputStream ois = new ObjectInputStream(s.getInputstream());
                    User user = (User)ois.readObject();
                                              
                    oos.close();
                    s.close();
                    return user;
                }
            };
            Object o= Proxy.newProxyInstance(IUserService.class.getclassLoader(),newClass[]{IUserService.class},h);
            return (IUserService)o;
        }
}
​

Server.java
public class Server{
    private static boolean running = true;
    public static void main(String[] args) throws Exception {
        ServerSocket ss = new ServerSocket( port: 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 ois = new ObjectInputStream(in);
        //DataOutputStream dos = new DataOutputStream(out);
        
        String methodName = ois.readUTF();
        Class[] parameterTypes = (Class[])ois.readObject();
        Object[] args = (0bject[])ois.readObject();
        
        IUserService service = new UserServiceImpl();
        Method method = service,getClass().getMethod(methodName,parameterTypes);
        User user = (User)method.invoke(service,args);
        
        ObjectOutputStream oos = new ObjectOutputStream(out);
        oos.writeObject(user);
        oos.flush();
        
    }
} 

rpc-v0.6

  • 更改只能生成一个类型(IUserService)的代理,帮助生成更多类型的代理

  • 修改Client,Server,Stub (ps: 在设计系统时,首先从Client端进行考虑)

Client.java
public class Client {
    public static void main(String[] args) throws Exception{
        IUserService service = (IUserService)Stub.getStub(IUserService.class);
        //findUserById()会调用Stub中的invoke方法
        System.out.printn(service.findUserById(123));
    }
}

Stub.java
public Class Stub{
    //change IUservice to Object
    public static Object getstub(Class clazz) {
            InvocationHandler h = new InvocationHandTer(){
                @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 clazzName = clazz.getName();
                    //获取方法名
                    String methodName = method.getName();
                    //获取方法参数,用于方法重载
                    Class[] parameterTypes = method.getParameterTypes();
                    
                    //first read first read
                    oos.writeUTF(clazzName);
                    oos.writeUTF(methodName);
                    oos.writeObject(parameterTypes);
                    oos.writeObject(args);
                    oos.flush;    
                    
                    ObjectInputStream ois = new ObjectInputStream(s.getInputstream());
                    User user = (User)ois.readObject();
                                              
                    oos.close();
                    s.close();
                    return user;
                }
            };
            Object o= Proxy.newProxyInstance(IUserService.class.getclassLoader(),newClass[]{IUserService.class},h);
            return (IUserService)o;
        }
}

Server.java
public class Server{
    private static boolean running = true;
    public static void main(String[] args) throws Exception {
        ServerSocket ss = new ServerSocket( port: 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();
        DataInputStream ois = new ObjectInputStream(in);
        
        //first read first read
        String clazzName = ois.readUTF();
        String methodName = ois.readUTF();
        Class[] parameterTypes = (Class[])ois.readObject();
        Object[] args = (0bject[])ois.readObject();
        
        Class clazz = null;
        clazz = UserServiceImpl.class;
  
        Method method = clazz.getMethod(methodName,parameterTypes);
        Object o= (0bject)method.invoke(clazz.newInstance(),args);
        
        ObjectOutputStream oos = new ObjectOutputStream(out);
        oos.writeObject(o);
        oos.flush();
    }
} 
​

rpc-v0.7

  • 测试0.6版本是否生效,Server,Stub 未作修改

Client.java
public class Client {
    public static void main(String[] args) throws Exception{
        IUserService service = (IProductService)Stub.getStub(IProductService.class);
        //findUserById()会调用Stub中的invoke方法
        System.out.printn(service.findProductById(23));
    }
}

rpc-v0.8--v0.9

RPC序列化框架
  • java.io.Serializable(之前的代码都使用的Serializable )

  • Hessian

  • google protobuf

  • facebook Thrift

  • kyro

  • fst

  • json序列化框架

  • Jackson

    • google Gson

    • Ali FastIson

  • xmlrpc(xstream)

  • .........

测试Hessian和Serializable
public class HelloHessian{
    public static void main(String[] args) throws Exception{
        User u = new User( id: 1, name: "zhangsan");  
        System.out.printIn("hessin" + serialize(u).length);
        System.out.printIn("jdk" + jdkSerialize(u).length);
        byte[] bytes = serialize(u);
        System.out.printIn(bytes.length);
        User u1 = (User)deserialize(bytes);
        System.out.println(u1);
    }
    
    public static byte[] serialize(Object o) throws Exception{
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        Hessian20utput output = new Hessian20utput(baos);
        
        output.write0bject(o);
        output,flush();
        byte[] bytes = baos.toByteArray();
        
        baos.close();
        output.close();
        
        return bytes;
    }
    
    public static Object deserialize(byte[] bytes) throws Exception{
        ByteArrayInputStream bais = new ByteArrayInputStream();
        Hessian2Input intput = new Hessian2Input(bais);
        
        Object o = intput.readObject();
        bais.close();
        intput.close();
        return bytes;
    }
    
    public static byte[] jdkSerialize(Object o) throws Exception{
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream output = new ObjectOutputStream(baos);
​
        output.writeObject(o);
        output.flush();
        byte[] bytes = baos.toByteArray();
​
        baos.close();
        output.close();
        return bytes;
    }
    public static Object jdkSerialize(byte[] bytes) throws Exception{
        ByteArrayInputStream bais = new ByteArrayInputStream();
        ObjectInputStream input = new ObjectInputStream(bais);
​
        Object o = intput.readObject();
        bais.close();
        input.close(); 
​
        return o;
    }
​
}

课程来源:https://www.bilibili.com/video/BV1r3411Y7fd , p108-118


0

评论区