背景
现在的java后端服务都是类似微服务这种,面向接口编程,不管是开源的,dubbo, spring cloud ,还是各家公司自研的,基本上都是以rpc协议这种
思考
虽然平时都在用,直接照搬之前的开发模式,定义接口,开发逻辑,好像也可以,不过我在想,如果我自己要实现一个rpc框架如何实现呢,这让我陷入了深深的思考
测试RPC(dubbo)
这是抽丝剥茧后理解dubbo的最简洁的代码了
@Test
public void export() {
AnnotationService ref = new AnnotationServiceImpl();
JavassistProxyFactory proxyFactory = new JavassistProxyFactory();
//这个地方 会生成AnnotationService 接口代理类
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) AnnotationService.class, URL.valueOf("dubbo://127.0.0.1:9020/" + AnnotationService.class.getName() + "?codec=exchange"));
Protocol protocol = new DubboProtocol();
//导出服务,这个地方底层代码有一个关键是开启了netty,作为通信协议,很关键
protocol.export(invoker);
//客户端接口
AnnotationService clientService = proxyFactory.getProxy(protocol.refer(AnnotationService.class, URL.valueOf("dubbo://127.0.0.1:9020/" + AnnotationService.class.getName() + "?codec=exchange").addParameter("timeout",
3000L)));
System.out.println(clientService.sayHello("aa"));
}
分析服务端代理类
代理类这个玩意老是在各种资料中看到,我倒要看看这个到底是个啥,这是得到的源码
package com.alibaba.dubbo.common.bytecode;
import com.alibaba.dubbo.common.bytecode.ClassGenerator;
import com.alibaba.dubbo.common.bytecode.NoSuchMethodException;
import com.alibaba.dubbo.common.bytecode.NoSuchPropertyException;
import com.alibaba.dubbo.common.bytecode.Wrapper;
import com.dubbo.demo.AnnotationServiceImpl;
import java.lang.reflect.InvocationTargetException;
import java.util.Map;
public class Wrapper1
extends Wrapper
implements ClassGenerator.DC {
public static String[] pns;
public static Map pts;
public static String[] mns;
public static String[] dmns;
public static Class[] mts0;
@Override
public String[] getPropertyNames() {
return pns;
}
@Override
public boolean hasProperty(String string) {
return pts.containsKey(string);
}
public Class getPropertyType(String string) {
return (Class)pts.get(string);
}
@Override
public String[] getMethodNames() {
return mns;
}
@Override
public String[] getDeclaredMethodNames() {
return dmns;
}
@Override
public void setPropertyValue(Object object, String string, Object object2) {
try {
AnnotationServiceImpl annotationServiceImpl = (AnnotationServiceImpl)object;
}
catch (Throwable throwable) {
throw new IllegalArgumentException(throwable);
}
throw new NoSuchPropertyException(new StringBuffer().append("Not found property \"").append(string).append("\" filed or setter method in class com.dubbo.demo.AnnotationServiceImpl.").toString());
}
@Override
public Object getPropertyValue(Object object, String string) {
try {
AnnotationServiceImpl annotationServiceImpl = (AnnotationServiceImpl)object;
}
catch (Throwable throwable) {
throw new IllegalArgumentException(throwable);
}
throw new NoSuchPropertyException(new StringBuffer().append("Not found property \"").append(string).append("\" filed or setter method in class com.dubbo.demo.AnnotationServiceImpl.").toString());
}
这是个重要的方法,客户端传来的请求,实际上最后就是到了这里调用,
// 客户端实际上传来的参数实际上在一个map集合中,通过key,找到value,key实际上就是接口产生URL,value,
// 说到底就是这个接口的实现类,调用后就把结果再返回 去
public Object invokeMethod(Object object, String string, Class[] arrclass, Object[] arrobject) throws InvocationTargetException {
AnnotationServiceImpl annotationServiceImpl;
try {
annotationServiceImpl = (AnnotationServiceImpl)object;
}
catch (Throwable throwable) {
throw new IllegalArgumentException(throwable);
}
try {
if ("sayHello".equals(string) && arrclass.length == 1) {
return annotationServiceImpl.sayHello((String)arrobject[0]);
}
}
catch (Throwable throwable) {
throw new InvocationTargetException(throwable);
}
throw new NoSuchMethodException(new StringBuffer().append("Not found method \"").append(string).append("\" in class com.dubbo.demo.AnnotationServiceImpl.").toString());
}
}
分析客户端代理类
客户端实际上的接口代理类是长这样
ClassLoader:
+-sun.misc.Launcher$AppClassLoader@18b4aac2
+-sun.misc.Launcher$ExtClassLoader@6bf2d08e
Location:
/Users/wending/.m2/repository/com/alibaba/dubbo/2.6.8/dubbo-2.6.8.jar
/*
* Decompiled with CFR.
*
* Could not load the following classes:
* com.dubbo.demo.AnnotationService
*/
package com.alibaba.dubbo.common.bytecode;
import com.alibaba.dubbo.common.bytecode.ClassGenerator;
import com.alibaba.dubbo.rpc.service.EchoService;
import com.dubbo.demo.AnnotationService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class proxy0
implements ClassGenerator.DC,
AnnotationService,
EchoService {
public static Method[] methods;
private InvocationHandler handler;
public proxy0(InvocationHandler invocationHandler) {
this.handler = invocationHandler;
}
public proxy0() {
}
@Override
public Object $echo(Object object) {
Object[] arrobject = new Object[]{object};
Object object2 = this.handler.invoke(this, methods[1], arrobject);
return object2;
}
//实际上调用 方法时走到了这里,通过代理类把消息打包传出去
public String sayHello(String string) {
Object[] arrobject = new Object[]{string};
Object object = this.handler.invoke(this, methods[0], arrobject);
return (String)object;
}
}
再思考
- 客户端,服务端为什么要用动态代理类,代理首先是肯定是很多东西都要实现这个功能 ,用统一的功能来实现,为什么是动态呢,因为如果不用动态的话,你得每个类都去实现相同的代码,变成了静态代理,在rpc中,动态代理屏蔽掉接口以外的东西,里面的细节主要是就把从数据转换,传输
- 之前一直写业务代码,面试的时候问,用没用过代理,没用过,这里不就是活生生的例子嘛 ,看多好用,dubbo里面主要用Javassist 来实现代理,类生成器,可以参考这个 ClassGenerator源码
RPC核心问题
1.客户端用代理屏蔽掉接口的以外的东西,把数据请求发出来
2.服务端根据收到的信息,调用相应的接口类实现类处理请求信息
3.通信问题,可以借助第三方来实现,比如Netty
细节补充
- 查看代理类源码,通过arthas这个工具,阿里开源工具,很好用
- dubbo更多细节,移步官网,查看