深入淺出RPC,通俗代碼理解dubbo

背景

現在的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;
    }
}

再思考

  1. 客戶端,服務端爲什麼要用動態代理類,代理首先是肯定是很多東西都要實現這個功能 ,用統一的功能來實現,爲什麼是動態呢,因爲如果不用動態的話,你得每個類都去實現相同的代碼,變成了靜態代理,在rpc中,動態代理屏蔽掉接口以外的東西,裏面的細節主要是就把從數據轉換,傳輸
  2. 之前一直寫業務代碼,面試的時候問,用沒用過代理,沒用過,這裏不就是活生生的例子嘛 ,看多好用,dubbo裏面主要用Javassist 來實現代理,類生成器,可以參考這個 ClassGenerator源碼

RPC核心問題

1.客戶端用代理屏蔽掉接口的以外的東西,把數據請求發出來
2.服務端根據收到的信息,調用相應的接口類實現類處理請求信息
3.通信問題,可以藉助第三方來實現,比如Netty

細節補充

  1. 查看代理類源碼,通過arthas這個工具,阿里開源工具,很好用
  2. dubbo更多細節,移步官網,查看
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章