背景
現在的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更多細節,移步官網,查看