RPC了解

定义

RPC = Remote Procedure Call 远程过程调用

简单来说像调用本地方法一样,调用远程服务器上的方法。

常见的RPC框架

Dubbo 阿里巴巴 国内最流行,功能强大,集成 ZooKeeper
gRPC Google 跨语言,高性能,使用 Protobuf 和 HTTP/2
Thrift Apache Facebook 开源,支持多语言
Spring Cloud OpenFeign Spring 基于 HTTP 的轻量级 RPC

RPC核心组件

组件 作用
动态代理 让调用远程方法像调用本地方法一样
序列化/反序列化 把对象转成字节流在网络上传输(如 JSON、Protobuf、Hessian)
网络通信 使用 TCP/HTTP 传输数据(如 Netty、OkHttp)
服务发现 找到服务端的 IP 和端口(如 ZooKeeper、Nacos)
负载均衡 多个服务实例时,选择哪个调用(如轮询、随机)

为什么需要RPC

在单体应用中,所有代码在一个进程里,方法调用是“本地调用”。

但在微服务架构中:

  • 用户服务、订单服务、支付服务……分布在不同服务器上
  • 它们必须互相调用
  • 如果每次都手动发 HTTP 请求,太麻烦!

实现原理

反射

Java 反射的实现依赖于 JVM 的类加载机制运行时的元数据存储

  1. 类加载过程
  • 当 JVM 加载一个类时,会创建一个对应的 Class 对象。
  • 这个 Class 对象包含了类的所有元信息:字段、方法、构造器、注解、父类、接口等。
  • 这些信息存储在 JVM 的 方法区(Method Area)元空间(Metaspace,JDK 8+)
  1. 反射如何工作
  • Class.forName() 触发类加载,返回 Class 实例。
  • 通过 Class 实例可以获取 FieldMethodConstructor 等对象。
  • 调用 invoke()set() 等方法时,JVM 会通过 JNI(Java Native Interface)调用底层 C++ 代码,动态执行对应的方法或字段操作。

静态代理

image-20251013042305051

一个租房子问题,中介就是这个原理。

缺点代理需要手动创建,简单来说就是房主和中介都要实现租房接口,这个租房接口规定了租房的规范,然后消费者只用调用中介的方法就能实现中介的很多方法并且最终实际租房的时候还是走的房东的租房方法,只是多加了很多中介的额外方法。

但是每次想要租房都得自己手动new一个中介出来。

动态代理

主要了解JDK动态代理,这个动态代理无需导入jar包,然后步骤上还是先定义好租房接口,也就是租房规则,然后房主要实现接口,这里有个区别了,这里的中介是万能中介,能代理任何出租方,出租房屋,汽车什么都行,怎么实现呢?

这里需要有一个动态中介处理器xxxHandler,这个类要有真是的代理对象然后实现InvocationHandler接口实现invoke方法,这样就能反射了,invoke方法里面传入代理对象也就是房主,然后方法,也就是rent方法,然后就是方法调用传入的参数值,然后这个invoke方法里面调用房东的租房方法比如

  1. 定义公共接口:UserService.java
1
2
3
4
5
6
// 用户服务接口(规则)
public interface UserService {
void addUser();
void deleteUser();
void updateUser(String name);
}

  1. 实现类:UserServiceImpl.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 真实的业务对象(被代理的对象)
public class UserServiceImpl implements UserService {

@Override
public void addUser() {
System.out.println("【业务】正在添加用户...");
}

@Override
public void deleteUser() {
System.out.println("【业务】正在删除用户...");
}

@Override
public void updateUser(String name) {
System.out.println("【业务】正在更新用户:" + name);
}
}

  1. 代理处理器:LoggingInvocationHandler.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

// 代理逻辑处理器:负责拦截方法调用并添加额外功能
public class LoggingInvocationHandler implements InvocationHandler {

private Object target; // 被代理的真实对象

public LoggingInvocationHandler(Object target) {
this.target = target;
}

/**
* 拦截所有通过代理对象调用的方法
* @param proxy 生成的代理对象(一般不用)
* @param method 当前调用的方法
* @param args 方法参数
* @return 方法返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1. 方法调用前:记录日志
System.out.println("【代理】方法调用前 - 开始执行: " + method.getName());

// 2. 使用反射调用真实对象的方法
Object result = method.invoke(target, args);

// 3. 方法调用后:记录日志
System.out.println("【代理】方法调用后 - 执行完成: " + method.getName());
System.out.println("---");

return result;
}
}

  1. 工具类:ProxyFactory.java(用于生成代理对象)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import java.lang.reflect.Proxy;

// 代理工厂:封装生成代理对象的逻辑
public class ProxyFactory {

private Object target; // 真实对象

public ProxyFactory(Object target) {
this.target = target;
}

/**
* 创建一个代理对象
* @return 实现了与 target 相同接口的代理实例
*/
public Object getProxy() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 类加载器
target.getClass().getInterfaces(), // 被代理类实现的接口
new LoggingInvocationHandler(target) // 处理器(拦截逻辑)
);
}
}

  1. 测试类:Client.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class Client {
public static void main(String[] args) {
// 1. 创建真实对象
UserService userService = new UserServiceImpl();

// 2. 创建代理工厂,传入真实对象
ProxyFactory factory = new ProxyFactory(userService);

// 3. 获取代理对象(关键!这个对象会触发 invoke)
UserService proxy = (UserService) factory.getProxy();

// 4. 调用方法 —— 实际执行的是 LoggingInvocationHandler.invoke()
proxy.addUser();
proxy.updateUser("张三");
proxy.deleteUser();
}
}

总结

动态代理利用反射的功能,实现了本地的动态调用,这样本地调用别的类的方法的时候就不用每次都new对象然后再去调用对象了,具体实现是首先有个接口来定义好规则,然后被代理对象和使用代理对象的人都要遵守这套规则,然后动态代理需要有个代理工厂类,这个类需要有被代理对象然后实现InvocationHandler这个接口,然后实现invoke方法,然后在invoke方法里面调用被代理对象的方法,这里使用到了反射的技巧,由于JVM加载类的时候会创建Class对象,然后这个对象里面包含了被代理类的所有信息,包括方法,然后我们调用invoke方法的时候就能不用new 被代理对象就能调用它的方法了,然后使用代理对象的类只需要new动态中介,然后通过 Proxy.newProxyInstance(...) 方法生成一个代理对象,这个代理对象实现了与目标对象相同的接口。之后就可以像调用普通对象一样调用这个代理对象的方法,而实际执行时会自动将方法调用转发到 InvocationHandlerinvoke 方法中进行处理。

RPC的实现原理

1、首先是本地发起调用服务中的方法,然后传入参数

2、然后这个接口由于是外部的方法,本地是没有实现类的,那么就利用动态代理获取实现类。

首先,会在运行时生成一个代理对象Stub,然后在动态代理里面封装了请求,调用网络模块发送请求的方法,然后在发送请求的时候会首先序列化参数,然后利用Socket进行TCP连接或者利用Netty等技术来发送请求,最后收到信息最后反序列化。然后服务端知道了你要调用的方法和参数,利用反射来动态的调用方法,然后将结果序列化然后通过网络发送给客户端,最终在客户端通过反序列化得到需要的结果。

image-20251020194015369