RPC

Remote Procedure Call 远程过程调用,是一种计算机通信协议,可以实现分布在不同服务器上的应用程序之间的调用,就像调用本地方法那样。

RPC调用流程

  1. 服务消费者 client 以本地调用方式调用服务
  2. client stub 接收调用后负责将方法、参数等序列化成能够进行网络传输的消息体
  3. client stub 将消息进行编码并发送到服务端
  4. server stub 收到消息后进行解码
  5. server stub 根据解码的结果反序列化并调用本地的服务
  6. 本地服务执行并将结果返回给 server stub
  7. server stub 将结果进行编码并发送给消费方 client
  8. client stub 收到消息并进行解码
  9. 服务消费方 client 得到结果

RPC 的作用就是将 2~8 步骤封装起来,用户无需关注这些细节,可以像调用本地方法一样即可完成远程服务调用。

RPC-动态代理

使用 JDK 动态代理的核心是InvocationHandler接口和Proxy类。

  • InvocationHandler 接口
    实现该接口来处理自定义逻辑,当动态代理对象调用一个方法时,这个方法的调用就会被转发到实现了 InvocationHandler 接口类的invoke方法来调用。
    1
    2
    3
    4
    5
    public interface InvocationHandler {
    /**
    * 当你使用代理对象调用方法的时候实际会调用到这个方法
    */
    public Object invoke(Object proxy, Method method, Object[] args)
  • Proxy 类
    使用最多的是 Proxy 类的newProxyInstance()方法,用来生成代理对象。
    1
    2
    3
    public static Object newProxyInstance(ClassLoader loader,
    Class<?>[] interfaces,
    InvocationHandler h) {}

生成代理对象方法的三个参数分别是:
loader:类加载器,用于加载代理对象;
interfaces:被代理类实现的接口;
h:实现了 InvocationHandler 接口的对象。

总的来说,JDK 动态代理的使用步骤:

  1. 定义一个接口及实现类;
  2. 自定义 InvocationHandler 并重写 invoke 方法,在 invoke 方法中通常会调用原生方法并自定义一些处理逻辑;
  3. 通过 Proxy.newProxyInstance() 方法创建代理对象。

在 RPC 中,是如何使用 JDK 动态代理的呢?

定义 JDK 动态代理类,实现核心的 InvocationHandler 接口,重写 invoke 方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class ClientProxy implements InvocationHandler {
// 传入参数service接口的class对象,反射封装成一个request
private String host;
private int port;

// jdk动态代理,每一次代理对象调用方法,都会经过此方法增强(反射获取request对象,socket发送到服务端)
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 通过建造者模式构建RPC请求
RpcRequest request = RpcRequest.builder()
.interfaceName(method.getDeclaringClass().getName())
.methodName(method.getName())
.params(args)
.paramsType(method.getParameterTypes())
.build();
// IOClient.sendRequest 和服务端进行数据传输
RpcResponse response = IOClient.sendRequest(host, port, request);
return response.getData();
}

获取代理对象的工厂类,这里我将工厂类的getProxy()方法定义在动态代理类当中。

1
2
3
4
5
// 创建代理对象
public <T> T getProxy(Class<T> clazz) {
Object o = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{clazz}, this);
return (T) o;
}

其他的,在原生实现类和对应的接口:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class UserServiceImpl implements UserService {
@Override
public User getUserByUserId(Integer id) {
System.out.println("客户端查询了" + id + "的用户");
// 模拟从数据库中取用户的行为
Random random = new Random();
User user = User.builder()
.userName(UUID.randomUUID().toString())
.id(id)
.sex(random.nextBoolean()).build();
return user;
}

@Override
public Integer insertUserId(User user) {
System.out.println("插入数据成功" + user.getUserName());
return user.getId();
}
}