RPC学习笔记

1、Remote Procedure Call

  • RPC的主要目标是让构建分布式更容易,在提供强大的远程调用能力时不损失本地调用的语义简洁性。为实现该目标,RPC框架需要提供一种透明的调用机制,让使用者不必显示的区别本地调用和远程调用。
  • RPC不是一个协议或者方法,它只是一个概念。是一个统称,重点在于方法调用(不支持对象的概念),具体实现甚至可以用RMI RestFul等去实现,但一般不用,因为RMI不能跨语言,而RestFul效率太低。
  • RPC多用于服务器集群之间的通信
  • 从单机到分布式->分布式通信->最基本的东西:二进制数据传输
  • 动态代理是RPC的核心之一
  • RPC和序列化是分不开的,因此产生了很多序列化框架
  • RPC不仅可以选序列化框架,网络协议也是可以选择的

1.1 现在有哪些RPC框架?

  • Dubbo:国内最早开源的 RPC 框架,由阿里巴巴公司开发并于 2011 年末对外开源,仅支持 Java 语言。
  • Motan:微博内部使用的 RPC 框架,于 2016 年对外开源,仅支持 Java 语言。
  • Tars:腾讯内部使用的 RPC 框架,于 2017 年对外开源,仅支持 C++ 语言。
  • SpringCloud:国外 Pivotal 公司 2014 年对外开源的 RPC 框架,提供了丰富的生态组件。
  • gRPC:Google 于 2015 年对外开源的跨语言 RPC 框架,支持多种语言。
  • Thrift:最初是由 Facebook 开发的内部系统跨语言的 RPC 框架,2007 年贡献给了 Apache 基金,成为Apache 开源项目之一,支持多种语言。

1.2 RPC具体步骤?

  1. 服务消费者(client客户端)通过本地调用的方式调用服务。
  2. 客户端存根(client stub)接收到请求后负责将方法、入参等信息序列化(组装)成能够进行网络传输的消息
    体。
  3. 客户端存根(client stub)找到远程的服务地址,并且将消息通过网络发送给服务端。
  4. 服务端存根(server stub)收到消息后进行解码(反序列化操作)。
  5. 服务端存根(server stub)根据解码结果调用本地的服务进行相关处理。
  6. 本地服务执行具体业务逻辑并将处理结果返回给服务端存根(server stub)。
  7. 服务端存根(server stub)将返回结果重新打包成消息(序列化)并通过网络发送至消费方。
  8. 客户端存根(client stub)接收到消息,并进行解码(反序列化)。
  9. 服务消费方得到最终结果。
    RPC

2、RPC涉及的技术?

  • 动态代理
    生成Client Stub和Server Stub的时候需要用到java动态代理技术
  • 序列化
    在网络中,所有的数据都会被转换成字节进行传送,需要对这些参数进行序列化和反序列化操作,目前最主流高效的开源序列化框架有:Kryo、FastJson、Hessian、Protobuf等
  • NIO通信
    java提供了NIO的解决方案,可以采用Netty或者mina框架来解决NIO的数据传输问题。开源的RPC框架Dubbo就是采用NIO通信,集成支持netty、mina、grizzly
  • 服务注册中心
    通过服务注册中心,让客户端的连接调用服务端所发布的服务。主流的注册中心组件有:Redis、Zookeeper、Consul、ETcd。Dubbo采用的是Zookeeper提供的服务注册与发现功能。
  • 负载均衡
    在高并发的场景下,需要多个节点或集群来提升整体吞吐能力
  • 健康检查
    健康检查包括:客户端心跳和服务端主动探测两种方式

3、RPC序列化框架的选型

  • Hessian比JDK序列化快、而且序列化之后的长度更短。
  • java自带的序列化方式Serializable接口,只支持Java代码,效率很低且所占空间比较大
  • 因此在设计RPC框架时,可以选用一些其他的序列化框架,如Hessian等

3.1 JDK Serializable

  • JDK Serializable是Java自带的序列化框架,我们只需要实现java.io.Serilizable或java.io.Externalizable接口,就可以使用java自带的序列化机制。
  • 实现序列化接口只是表示该类能够被序列化或反序列化,还需要借助IO操作的ObjectInputStream和ObjectOutputStream对对象进行序列化和反序列化。
  • 缺点:
  1. 只能支持Java语言,不能跨语言使用。
  2. 不好用,语法生硬
  3. 速度慢,序列化的字节数组长度比较长,所占空间大

3.2 FST序列化框架

  • FST是完全兼容JDK序列化框架,它在序列化速度上能达到JDK的10倍,序列化结果只有JDK的1/3
  • 语法及其简洁
  • 序列化之后的开销也比JDK少
  • 缺点:

1.FST同样是针对Java开发的序列化框架,因此也不能跨语言使用

3.3 Kryo序列化框架

  • Kryo是一个快速有效的Java序列化框架,它依赖底层ASM库用于字节码的生成,因此有比较好的运行速度
  • Kryo的目标就是提供一个序列化速度快、结果体积小、API简单易用的序列化框架
  • Kryo支持自动深、浅拷贝,它是直接通过对象的深度拷贝,而不是对象->字节->对象的过程
  • 语法比较简洁,API易用
  • 缺点:

1.也是针对Java开发的,不具有跨语言特性

3.4 Protocol buffer

  • Protocol buffer是一种语言中立、平台无关、可扩展的序列化框架,Protocol buffer相较于前面几种序列化框架而言,它是需要预先定义Schema的。
  • ProtocolBuf设计之初的目标就是能够设计一款与语言无关的序列化框架,它目前支持Java/Python/C++/Go/C#等,通用性是很强的
  • Protocol需要使用IDL来定义Schema描述文件,定义完描述文件之后,可以直接使用protoc来生成序列化与反序列代码,因此,只需要简单编写描述文件,就可以使用protobuf了。
  • 缺点:

反序列化性能比Kryo和FST差些

3.5 Thrift序列化框架

  • Thrift是由Facebook实现的一种高效的、支持多种语言的远程服务调用框架,即RPC,后来Facebook将Thrift开源到Apache。
  • Thrift是一个RPC框架,但是由于Thrift提供了多语言之间的RPC服务,所以很多时候被用于序列化中。
  • 使用Thrift实现序列化主要分为3步:创建thrift IDL文件、编译生成Java代码、使用TSerializer和TDeserializer进行序列化和反序列化。
  • Thrift目前支持 C++、Java、Python、PHP、Ruby、 Erlang、Perl、Haskell、C#、Cocoa、JavaScript、Node.js、Smalltalk、OCaml、Delphi等语言
  • Thrift在序列化和反序列化的时间开销总和上和protobuf差不多,protobuf在序列化时间上更占优势,而Thrift在反序列化上有自己的优势

3.6 Hessian序列化框架

  • Hessian是caucho公司开发的轻量级RPC框架,它使用HTTP协议传输,使用Hessian二进制序列化。
  • Hessian支持跨语言、高效的二进制序列化协议,被经常用于序列化框架。
  • Hessian序列化使用简单

3.7 Avro序列化框架

  • Avro是一个数据序列化框架,它是Apache Hadoop下的一个子项目。
  • Avro在设计之初就用于支持数据密集型应用,很适合运城或本地大规模数据交换和存储
  • Avro通过Schema定义数据结构,目前支持Java、C、C++、C#、Python、PHP和Ruby语言
  • Avro对于动态语言无需生成代码,但对于Java这类静态语言,还是需要使用avro-tools.jar来编译生成Java代码。在Schema编写上,感觉相比Thrift、Protobuf更加复杂

3.8 开销的对比图

在这里插入图片描述

4/RPC框架的演进

  • ①第一个版本
    特点:通信代码与真正的业务代码混杂
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());//传输ID给服务器
        s.getOutputStream().flush();
        //读数据
        DataInputStream dis = new DataInputStream(s.getInputStream());
        int id = dis.readInt();
        String name = dis.readUTF();
        User user = new User(name,id);
        System.out.println(user);
        dos.close();
        s.close();

    }
}

--------------------------------------------------------------------------------
package com.demo.rpc01;

import com.demo.IUSerService;
import com.demo.User;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
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();

        DataInputStream dis = new DataInputStream(in);
        DataOutputStream dos = new DataOutputStream(out);

        int id = dis.readInt();//读入ID
        IUSerService serService = new USerServiceImpl();
        User user = serService.getUSerById(id);

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

        dos.flush();
    }
}
-----------------------------------------------------------------------------------
package com.demo.rpc01;

import com.demo.IUSerService;
import com.demo.User;

public class USerServiceImpl implements IUSerService {
    
    

    @Override
    public User getUSerById(Integer id) {
    
    
        return new User("Alice",id);
    }
}
  • ②第二个版本
    特点:将客户端通信代码封装成Stub,简化了客户端的使用
package com.demo.rpc02;

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

-----------------------------------------------------------------------------------
package com.demo.rpc02;

import com.demo.IUSerService;
import com.demo.User;
import com.demo.rpc01.USerServiceImpl;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
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();

        DataInputStream dis = new DataInputStream(in);
        DataOutputStream dos = new DataOutputStream(out);

        int id = dis.readInt();
        IUSerService serService = new USerServiceImpl();
        User user = serService.getUSerById(id);

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

        dos.flush();
    }
}
-----------------------------------------------------------------------------------
package com.demo.rpc02;

import com.demo.IUSerService;
import com.demo.User;

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

public class Stub {
    
    
    public User getUserById(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(name,id);

        dos.close();
        s.close();
        return user;
    }
}
  • ③第三个版本
    特点:运用动态代理,将客户端通信代码放在代理方法中,但这时接口是写死的,只能处理IUserService的业务请求
package com.demo.rpc03;

import com.demo.IUSerService;

public class Client {
    
    
    public static void main(String[] args) throws Exception
    {
    
    
        IUSerService stub = (IUSerService) Stub.getStub();
        System.out.println(stub.getUSerById(123));

    }
}
--------------------------------------------------------------------------------------------------
package com.demo.rpc03;

import com.demo.IUSerService;
import com.demo.User;
import com.demo.rpc01.USerServiceImpl;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
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();

        DataInputStream dis = new DataInputStream(in);
        DataOutputStream dos = new DataOutputStream(out);

        int id = dis.readInt();
        IUSerService serService = new USerServiceImpl();
        User user = serService.getUSerById(id);

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

        dos.flush();
    }
}
------------------------------------------------------------------------------------------
package com.demo.rpc03;

import com.demo.IUSerService;
import com.demo.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;

public class Stub {
    
    
    public static Object 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(name,id);

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

        Object o = Proxy.newProxyInstance(IUSerService.class.getClassLoader(),new Class[]{
    
    IUSerService.class},h);//生成代理类
        return (IUSerService)o;
    }
}
  • ④第四个版本
    特点:客户端向服务端传入需要调用的方法名,服务端在接收到之后,利用反射机制去调用对应方法,但服务端接口是写死的
package com.demo.rpc04;

import com.demo.IUSerService;

public class Client {
    
    
    public static void main(String[] args) throws Exception
    {
    
    
        Object stub = Stub.getStub();
        System.out.println(((IUSerService)stub).getUSerById(123));

    }
}
--------------------------------------------------------------------------------------
package com.demo.rpc04;

import com.demo.IUSerService;
import com.demo.User;
import com.demo.rpc01.USerServiceImpl;

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();

        DataInputStream dis = new DataInputStream(in);
        DataOutputStream dos = new DataOutputStream(out);

        ObjectInputStream oos = new ObjectInputStream(in);

        String methodName = oos.readUTF();//读入方法名
        Class[] parameterTypes = (Class[]) oos.readObject();//读入方法参数类型
        Object[] args = (Object[]) oos.readObject();//读入方法参数

        IUSerService serService = new USerServiceImpl();
        Method method = serService.getClass().getMethod(methodName,parameterTypes);//调用方法
        User user = (User)method.invoke(serService,args);



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

        dos.flush();
    }
}
-------------------------------------------------------------------------------------------
package com.demo.rpc04;

import com.demo.IUSerService;
import com.demo.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;

public class Stub {
    
    
    public static Object 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[] parameterTypes = method.getParameterTypes();//获取参数类型数组,这是防止重载带来的错误
                oos.writeUTF(methodName);
                oos.writeObject(parameterTypes);
                oos.writeObject(args);//方法参数
                oos.flush();

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

                oos.close();
                s.close();
                return user;

            }
        };

        Object o = Proxy.newProxyInstance(IUSerService.class.getClassLoader(),new Class[]{
    
    IUSerService.class},h);//生成代理类
        return o;
    }
}
  • ⑤第五个版本
    特点:只把Stub的getStub的返回值换成了IUserService,在客户端不用强转了
package com.demo.rpc05;

import com.demo.IUSerService;

public class Client {
    
    
    public static void main(String[] args) throws Exception
    {
    
    
        IUSerService stub = Stub.getStub();
        System.out.println(stub.getUSerById(123));

    }
}
---------------------------------------------------------------------------------------
package com.demo.rpc05;

import com.demo.IUSerService;
import com.demo.User;
import com.demo.rpc01.USerServiceImpl;

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();

        DataInputStream dis = new DataInputStream(in);
        DataOutputStream dos = new DataOutputStream(out);

        ObjectInputStream ois = new ObjectInputStream(in);
        ObjectOutputStream oos = new ObjectOutputStream(out);

        String methodName = ois.readUTF();//读入方法名
        Class[] parameterTypes = (Class[]) ois.readObject();//读入方法参数类型
        Object[] args = (Object[]) ois.readObject();//读入方法参数

        IUSerService serService = new USerServiceImpl();
        Method method = serService.getClass().getMethod(methodName,parameterTypes);//调用方法
        User user = (User)method.invoke(serService,args);


        oos.writeObject(user);

        dos.flush();
    }
}
-----------------------------------------------------------------------------------------------------
package com.demo.rpc05;

import com.demo.IUSerService;
import com.demo.User;

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

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());
                ObjectInputStream ois = new ObjectInputStream(s.getInputStream());

                String methodName = method.getName();
                Class[] parameterTypes = method.getParameterTypes();//获取参数类型数组,这是防止重载带来的错误
                oos.writeUTF(methodName);
                oos.writeObject(parameterTypes);
                oos.writeObject(args);//方法参数
                oos.flush();


                User user = (User)ois.readObject();

                oos.close();
                ois.close();
                s.close();

                return user;

            }
        };

        Object o = Proxy.newProxyInstance(IUSerService.class.getClassLoader(),new Class[]{
    
    IUSerService.class},h);//生成代理类
        return (IUSerService) o;
    }
}
  • ⑥第六个版本
    特点:客户端传入所需业务的接口,服务端从注册表内找到相关类,再根据传入的方法调用业务
package com.demo.rpc06;

import com.demo.IUSerService;

public class Client {
    
    
    public static void main(String[] args) throws Exception
    {
    
    
        IUSerService serService = (IUSerService) Stub.getStub(IUSerService.class);
        System.out.println(serService.getUSerById(123));

    }
}
---------------------------------------------------------------------------------------------------
package com.demo.rpc06;

import com.demo.IUSerService;
import com.demo.User;
import com.demo.rpc01.USerServiceImpl;

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();

        DataInputStream dis = new DataInputStream(in);
        DataOutputStream dos = new DataOutputStream(out);

        ObjectInputStream ois = new ObjectInputStream(in);
        ObjectOutputStream oos = new ObjectOutputStream(out);

        String className = ois.readUTF();//读入类名
        String methodName = ois.readUTF();//读入方法名
        Class[] parameterTypes = (Class[]) ois.readObject();//读入方法参数类型
        Object[] args = (Object[]) ois.readObject();//读入方法参数

        Class clazz = null;

        //从服务注册表找到具体类
        clazz = USerServiceImpl.class;

        Method method = clazz.getMethod(methodName,parameterTypes);//调用方法
        User user = (User)method.invoke(clazz.getDeclaredConstructor().newInstance(),args);


        oos.writeObject(user);

        dos.flush();
    }
}
----------------------------------------------------------------------------------------------------
package com.demo.rpc06;

import com.demo.HessionUtils.HessianUtils;
import com.demo.IUSerService;
import com.demo.User;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;

public class Stub {
    
    
    public static Object getStub(Class clazz){
    
    

        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());
                ObjectInputStream ois = new ObjectInputStream(s.getInputStream());


                String clazzName = clazz.getName();
                String methodName = method.getName();
                Class[] parameterTypes = method.getParameterTypes();//获取参数类型数组,这是防止重载带来的错误

                oos.writeUTF(clazzName);
                oos.writeUTF(methodName);
                oos.writeObject(parameterTypes);
                oos.writeObject(args);//方法参数
                oos.flush();


                Object o = HessianUtils.deserialize(ois.readAllBytes());

                oos.close();
                ois.close();
                s.close();

                return o;

            }
        };

        Object o = Proxy.newProxyInstance(clazz.getClassLoader(),new Class[]{
    
    clazz},h);//生成代理类
        return o;
    }
}
  • ⑦第七个版本
    特点:把Stub的getStub的返回值换成了Object,这样,传入不同的接口,就能调用不同的业务了
package com.demo.rpc07;

import com.demo.IProductService;
import com.demo.IUSerService;
import com.demo.rpc06.Stub;

public class Client {
    
    
    public static void main(String[] args) throws Exception
    {
    
    
        IProductService serService = (IProductService) Stub.getStub(IProductService.class);
        System.out.println(serService.getProById(123));

    }
}
-------------------------------------------------------------------------------------
package com.demo.rpc07;

import com.demo.Product;
import com.demo.User;
import com.demo.rpc01.USerServiceImpl;

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();

        DataInputStream dis = new DataInputStream(in);
        DataOutputStream dos = new DataOutputStream(out);

        ObjectInputStream ois = new ObjectInputStream(in);
        ObjectOutputStream oos = new ObjectOutputStream(out);

        String className = ois.readUTF();//读入类名
        String methodName = ois.readUTF();//读入方法名
        Class[] parameterTypes = (Class[]) ois.readObject();//读入方法参数类型
        Object[] args = (Object[]) ois.readObject();//读入方法参数

        Class clazz = null;

        //从服务注册表找到具体类
        clazz = ProductServiceImpl.class;

        Method method = clazz.getMethod(methodName,parameterTypes);//调用方法
        Product product = (Product)method.invoke(clazz.getDeclaredConstructor().newInstance(),args);


        oos.writeObject(product);

        dos.flush();
    }
}
---------------------------------------------------------------------------------------------------------
package com.demo.rpc07;

import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.Socket;

public class Stub {
    
    
    public static Object getStub(Class clazz){
    
    

        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());
                ObjectInputStream ois = new ObjectInputStream(s.getInputStream());


                String clazzName = clazz.getName();
                String methodName = method.getName();
                Class[] parameterTypes = method.getParameterTypes();//获取参数类型数组,这是防止重载带来的错误

                oos.writeUTF(clazzName);
                oos.writeUTF(methodName);
                oos.writeObject(parameterTypes);
                oos.writeObject(args);//方法参数
                oos.flush();


                Object o = ois.readObject();

                oos.close();
                ois.close();
                s.close();

                return o;

            }
        };

        Object o = Proxy.newProxyInstance(clazz.getClassLoader(),new Class[]{
    
    clazz},h);//生成代理类
        return o;
    }
}
  • ⑧第八个版本
    特点:Hessian的简单用法
package com.demo.rpc08_Hessian01;

import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.demo.User;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.HashMap;

public class HelloHessian {
    
    
    public static void main(String[] args) throws Exception
    {
    
    
        User user = new User("zhangsan",1);
        System.out.println("hession:"+serialize(user).length);
    }

    public static byte[] serialize(Object o) throws Exception
    {
    
    
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        Hessian2Output output = new Hessian2Output(baos);
        output.writeObject(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(bytes);
        Hessian2Input input = new Hessian2Input(bais);
        Object o = input.readObject();
        bais.close();
        input.close();
        return o;

    }

}
  • ⑨第九个版本
    特点:对比JDK序列化和Hessian序列化
package com.demo.rpc09_Hessian02;

import com.caucho.hessian.io.Hessian2Output;
import com.demo.User;

import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.ObjectOutputStream;

public class HessianVsJdk {
    
    
    public static void main(String[] args) throws Exception
    {
    
    
        User user = new User("zhangsan",1);
        System.out.println("Hessian:"+hessianSerial(user).length);
        System.out.println("JDK:"+jdkSerial(user).length);
    }
    public static byte[] hessianSerial(Object o) throws Exception
    {
    
    
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        Hessian2Output output = new Hessian2Output(out);
        output.writeObject(o);
        output.flush();
        byte[] bytes = out.toByteArray();
        out.close();
        output.close();
        return bytes;
    }
    public static byte[] jdkSerial(Object o) throws Exception
    {
    
    
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(out);
        oos.writeObject(o);
        oos.flush();
        byte[] bytes = out.toByteArray();
        out.close();
        oos.close();
        return bytes;
    }
}


猜你喜欢

转载自blog.csdn.net/qq_56919740/article/details/131681296