本系列之1: 简介
本系列之2: API服务开发
本系列之3: RSON序列化格式
本系列之4: RPC调度原理
本系列之5: NIO实现原理
本系列之6: 客户端动态代理原理👈
本系列之7: 注解处理器(APT)原理
本文介绍Rayson框架客户端使用Java动态代理技术实现RPC远程调用的原理。
Rayson框架自带Java客户端rayson.client,开发人员使用它可以简单、高效地调用使用Rayson框架所开发出来的API服务(由于Rayson框架支持多平台,因此理论上支持其他语言开发的客户端,包括浏览器和Linux命令行curl
)。
正如本系列之2: API服务开发所介绍那样,使用Rayson的Java客户端调用API服务的代码示例如下:
public static void main(final String[] args) throws Exception {
final RaysonServerAddress serverAddr = new RaysonServerAddress("localhost", 8080);
SimpleProtocol simpleProtocol = Rayson.createProxy(serverAddr, SimpleProtocol.class);
try {
String echoMsg = simpleProtocol.echo("Hello World");
System.out.println(echoMsg);
} catch (IOException e) {
System.err.println("Network error occurred");
} catch (RpcException e) {
System.err.println("Invoking RPC got logic error: error_code: " + e.getCode() + " error_message: " + e.getMessage());
}
}
其中,第三行代码SimpleProtocol simpleProtocol = Rayson.createProxy(serverAddr, SimpleProtocol.class);
会创建一个动态代理(dynamic proxy)。动态代理技术是JDK支持的技术,它利用Java的反射技术(Java Reflection),在运行时创建一个实现某些给定接口的动态代理类及其实例(对象)。
使用JDK创建一个动态代理的示例代码如下:
java.lang.reflect.InvocationHandler handler = new MyInvocationHandler();
Foo f = (Foo) java.lang.reflect.Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class[] { Foo.class }, handler);
代码说明:
Foo
是一个接口类型。MyInvocationHandler
是一个实现了接口java.lang.reflect.InvocationHandler
的处理类。- 把
Foo
接口和处理类MyInvocationHandler
的实例作为参数调用java.lang.reflect.Proxy#newProxyInstance
,可以创建一个Foo
接口的动态代理类,该代理是一个动态生成的Foo
接口的实现类。动态代理类的字节码(Byte Codes)是JDK动态生成的。下文我们通过把动态代理类进行反编译的源代码来说明动态代理执行原理。
我们把上文Rayson客户端调用API服务示例代码中所生成的动态代理类进行反编译,其代码如下:
public final class $Proxy0 extends Proxy implements SimpleProtocol, org.rayson.api.client.Proxy {
private static Method m3;
private static Method m4;
static {
try {
m3 = Class.forName("org.rayson.demo.simple.api.SimpleProtocol").getMethod("echo", new Class[] { Class.forName("java.lang.String") });
m4 = Class.forName("org.rayson.demo.simple.api.SimpleProtocol").getMethod("echo2", new Class[] { Class.forName("[B") });
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
} catch (ClassNotFoundException e2) {
throw new NoClassDefFoundError(e2.getMessage());
}
}
public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}
@Override
public final String echo(String str) throws IOException, RpcException {
try {
return (String) this.h.invoke(this, m3, new Object[] { str });
} catch (IOException | Error | RuntimeException | RpcException e) {
throw e;
} catch (Throwable th) {
throw new UndeclaredThrowableException(th);
}
}
@Override
public final byte[] echo2(byte[] bArr) throws IOException, RpcException {
try {
return (byte[]) this.h.invoke(this, m4, new Object[] { bArr });
} catch (IOException | Error | RuntimeException | RpcException e) {
throw e;
} catch (Throwable th) {
throw new UndeclaredThrowableException(th);
}
}
//省略其他接口方法,其代码原理是一样的。
}
代码说明:
- 动态代理类的名字是$Proxy0,名称是动态的。
- 动态代理类实现了要代理的接口
SimpleProtocol
。 - 动态代理类把对接口
SimpleProtocol
的方法的调用–通过把目标方法(Method)和方法调用的参数传递给处理类–转换为对处理器实例InvocationHandler#invoke
方法的调用。 - 这样,我们就可以在处理器类的
InvocationHandler#invoke
方法实现中统一进行动态代理类的业务处理逻辑,看起来就像处理器类“代理了”接口SimpleProtocol
实现的工作,因此这种技术就叫做动态代理技术。
Rayson客户端RPC动态代理的处理类InvocationHandler的实现代码如下:
public class RpcProxy implements InvocationHandler, Proxy {
//省略非主要代码
@Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws RpcException, IOException, ClientFilterException {
final ClientRequestImpl request = new ClientRequestImpl(method, args);
final ClientResponseImpl response = new ClientResponseImpl(getProtocolMirror().getMethod(request.getMethod().getName()));
filterManager.doFilter(request, response);
return response.getResult();
}
}
代码说明:
- 把
InvocationHandler#invoke
方法传递Java方法method
和相应参数args
作为参数,构建一个客户端请求对象request
。 - 同时构建一个客户端响应对象
response
。 - 调用
Filter Manager
实例执行过滤器的过滤逻辑。关于过滤器,请参考本系列之2: API服务开发的“过滤器”部分。 - 等待响应对象
response
返回结果。
这就是Rayson客户端RPC动态代理的工作原理。