背景

RPC大家有一定了解后,在Java开发生态下,动态代理和它有着紧密联系。如果单拎出动态代理,你一定会有一大堆八股文,它和RPC是什么关系?

一、使用场景

在使用RPC的时候,需要服务方提供interface,在调用方编写业务逻辑时,调用接口的方法,拿到结果。为什么?

RPC会为接口生成一个代理类,调用方在使用过程中,实际运行时该接口被调用会被代理类给拦截到,在代理类中具体实现远程调用逻辑。

这里其实就使用到了动态代理技术,场景描述如下,

打开网易新闻 查看精彩图片

通过这种hack手法,用户就不会感知远程调用的细节,实际就和调用本地方法一样。

二、Java动态代理

代码运行环境要求:open Jdk11

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* interface
*/
interface HelloWorld {
String speak();
}
/**
* real object
*/
class RealHello {
public String say() {
return "i'm RealHello";
}
}
/**
* proxy
*/
class JdkProxy implements InvocationHandler {
private final Object target;
JdkProxy(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] paramValues) {
return ((RealHello) target).say();
}
}
/**
* TestProxy
*/
public class TestProxy {
public static void main(String[] args) {
JdkProxy jdkProxy = new JdkProxy(new RealHello());
ClassLoader classLoader = ClassLoader.getSystemClassLoader();
// save proxy class
System.setProperty("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
// generate proxy object
HelloWorld test = (HelloWorld) Proxy.newProxyInstance(classLoader, new Class[] {HelloWorld.class}, jdkProxy);
// invoke proxy
System.out.println(test.speak());
}
}

本段代码的含义:HelloWorld接口生成一个代理类,并调用它的speak方法,返回的数据是RealHello的say方法返回值。

三、额外思考

如果没有动态代理那么如何完成方法调用拦截,实现RPC?

显而易见动态不行,就静态。但是静态有一个不好的地方,那就是需要针对性的为每个实现类创建一个代理类,并且需要写序列化、负载均衡、失败重试等等,上线一个 RPC调用的特性从一天可能就变成一周了。

对于RPC增加或修改接口的情况,动态代理无需修改,自适应协议的变化,而静态代理需要重新生成代码覆盖调用方和服务方。

如果是跨语言的类似GRPC的这种要怎么弄呢?对于动态代理,要么是各种reflect逻辑从服务提供方动态获取解析类型和数据,要么就构建一堆的硬编码,可以理解为通过模板动态生成支持适配协议的相关代码,这两个的代表在GRPC中是envoy和grpc-gateway。

小结

动态代理作为在RPC里面的一种应用,虽然只是具体实现技术,但理解了它才能更好的理解RPC里面是如何做到面向接口编程,帮助用户屏蔽RPC调用细节,达到远程调用。

动态代理本身是一种技术框架,在使用时,我们就需要有一个合理的选型,一般从框架生成代理类的速度、代理类字节码的大小、代理类的执行效率、框架的生态是否繁荣等去选择。

了解更多Java相关资料,请关注微信公众号:Java面试教程

让我们一起,玩转Java面试。

打开网易新闻 查看精彩图片