日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Dubbo是如何进行远程服务调用的?(源码流程跟踪)

發布時間:2025/3/12 编程问答 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Dubbo是如何进行远程服务调用的?(源码流程跟踪) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

首先會分析Dubbo是如何進行遠程服務調用的,如果不了解dubbo的服務暴露和服務調用,請去看前兩篇dubbo的文章,然后后面我還會說一下dubbo的SPI機制

當我們在使用@reference 注解的時候,來調用我們的提供者的Service對象的時候,Dubbo中的服務調用是怎么實現的

Dubbo的遠程服務調用

(1)首選Dubbo是通過Poxy對象來生成一個代理對象的

  • 具體實現是在ReferenceConfig對象中調用的private T createProxy(Map<String, String> map)方法的,這個方法中有三種生成Invoker對象的方式,第一種是通過本地JVM,第二種是通過URL對象是不是為空判斷進行判斷,然后如果為空就從注冊中心獲取這個Invoker對象,否則就是從ReferenceConfig中的URL中拿到
  • 上面那個方法中還會通過獲取到的Invoker這里的【生成Invoker的過程后面補充】的對象去通過ProxyFactory生成Poxy對象,代碼為:return proxyFactory.getProxy(this.invoker);,這里proxyFactory其實就是
  • //ProxyFactory接口的javassist擴展類JavassistProxyFactory的getProxy方法實現public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {return Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));}
  • 然后通過第2步的getPoxy()方法去動態代理生成代理Poxy對象
  • public static Proxy getProxy(Class<?>... ics) {return getProxy(ClassHelper.getClassLoader(Proxy.class), ics);}/*** Get proxy.** @param cl class loader.* @param ics interface class array. 可以實現多個接口* @return Proxy instance.*/public static Proxy getProxy(ClassLoader cl, Class<?>... ics) {if (ics.length > 65535)throw new IllegalArgumentException("interface limit exceeded");StringBuilder sb = new StringBuilder();for (int i = 0; i < ics.length; i++) {String itf = ics[i].getName();if (!ics[i].isInterface())throw new RuntimeException(itf + " is not a interface.");Class<?> tmp = null;try {tmp = Class.forName(itf, false, cl);} catch (ClassNotFoundException e) {}if (tmp != ics[i])throw new IllegalArgumentException(ics[i] + " is not visible from class loader");sb.append(itf).append(';');}// use interface class name list as key.// 用接口類名做key,多個接口以分號分開。String key = sb.toString();// get cache by class loader.// 緩存Map<String, Object> cache;synchronized (ProxyCacheMap) {cache = ProxyCacheMap.get(cl);if (cache == null) {cache = new HashMap<String, Object>();ProxyCacheMap.put(cl, cache);}}Proxy proxy = null;synchronized (cache) {do {Object value = cache.get(key);if (value instanceof Reference<?>) {//如果有存在引用對象,返回緩存對象。proxy = (Proxy) ((Reference<?>) value).get();if (proxy != null)return proxy;}//對象正在生成,線程掛起,等待if (value == PendingGenerationMarker) {try {cache.wait();} catch (InterruptedException e) {}} else {//放入正在生成標識cache.put(key, PendingGenerationMarker);break;}}while (true);}//類名稱后自動加序列號 0,1,2,3...long id = PROXY_CLASS_COUNTER.getAndIncrement();String pkg = null;//ClassGenerator dubbo用javassist實現的工具類ClassGenerator ccp = null, ccm = null;try {ccp = ClassGenerator.newInstance(cl);Set<String> worked = new HashSet<String>();List<Method> methods = new ArrayList<Method>();for (int i = 0; i < ics.length; i++) {//檢查包名稱及不同包的修飾符if (!Modifier.isPublic(ics[i].getModifiers())) {String npkg = ics[i].getPackage().getName();if (pkg == null) {pkg = npkg;} else {if (!pkg.equals(npkg))throw new IllegalArgumentException("non-public interfaces from different packages");}}//代理類添加要實現的接口Class對象ccp.addInterface(ics[i]);for (Method method : ics[i].getMethods()) {//獲取方法描述符,不同接口,同樣的方法,只能被實現一次。String desc = ReflectUtils.getDesc(method);if (worked.contains(desc))continue;worked.add(desc);int ix = methods.size();//方法返回類型Class<?> rt = method.getReturnType();//方法參數類型列表Class<?>[] pts = method.getParameterTypes();//生成接口的實現代碼,每個方法都一樣StringBuilder code = new StringBuilder("Object[] args = new Object[").append(pts.length).append("];");for (int j = 0; j < pts.length; j++)code.append(" args[").append(j).append("] = ($w)$").append(j + 1).append(";");code.append(" Object ret = handler.invoke(this, methods[" + ix + "], args);");if (!Void.TYPE.equals(rt))code.append(" return ").append(asArgument(rt, "ret")).append(";");methods.add(method);ccp.addMethod(method.getName(), method.getModifiers(), rt, pts, method.getExceptionTypes(), code.toString());}}if (pkg == null)pkg = PACKAGE_NAME;// create ProxyInstance class.// 具體代理類名稱,這里是類全名String pcn = pkg + ".proxy" + id;ccp.setClassName(pcn);ccp.addField("public static java.lang.reflect.Method[] methods;");ccp.addField("private " + InvocationHandler.class.getName() + " handler;");//創建構造函數ccp.addConstructor(Modifier.PUBLIC, new Class<?>[]{InvocationHandler.class}, new Class<?>[0], "handler=$1;");ccp.addDefaultConstructor();Class<?> clazz = ccp.toClass();//通過反射,把method數組放入,靜態變量methods中,clazz.getField("methods").set(null, methods.toArray(new Method[0]));// create Proxy class.String fcn = Proxy.class.getName() + id;ccm = ClassGenerator.newInstance(cl);ccm.setClassName(fcn);ccm.addDefaultConstructor();//設置父類為抽象類,Proxy類子類,ccm.setSuperClass(Proxy.class);//生成實現它的抽象方法newInstance代碼//new 的實例對象,是上面生成的代理類 pcnccm.addMethod("public Object newInstance(" + InvocationHandler.class.getName() + " h){ return new " + pcn + "($1); }");Class<?> pc = ccm.toClass();proxy = (Proxy) pc.newInstance();} catch (RuntimeException e) {throw e;} catch (Exception e) {throw new RuntimeException(e.getMessage(), e);} finally {// release ClassGeneratorif (ccp != null)ccp.release();if (ccm != null)ccm.release();synchronized (cache) {if (proxy == null)cache.remove(key);else//放入緩存,key:實現的接口名,value 代理對象,這個用弱引用,//當jvm gc時,會打斷對實例對象的引用,對象接下來就等待被回收。cache.put(key, new WeakReference<Proxy>(proxy));cache.notifyAll();}}return proxy;}

    (2)這里是進行補充上面的那個【Invoker的怎么生成的步驟】,看一下Invoker中都包含了什么信息這么重要,這里需要強調一下這個Invoker生成的過程和Dubbo服務的暴露和導出生成的Invoker不太一樣

  • invoker對象是通過 InvokerInvocationHandler構造方法傳入,而InvokerInvocationHandler對象是由JavassistProxyFactory類getProxy(Invoker invoker, Class<?>[] interfaces)方法創建。
    這還要回到調用proxyFactory.getProxy(invoker);方法的地方,即ReferenceConfig類的createProxy(Map<String, String> map)方法中
  • 所以這個Invoker其實是通過ReferenceConfig 中的createProxy(Map<String, String> map)方法來生成的Invoker對象,這個就是下面中使用到的對象refprotocol ,private static final Protocol refprotocol = (Protocol)ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
  • if (urls.size() == 1) {//只有一個直連地址或一個注冊中心配置地址//這里的urls.get(0)協議,可能是直連地址(默認dubbo協議),也可能是regiter注冊地址(zookeeper協議)//我們這里走的是注冊中心,所以invoker = refprotocol.refer(interfaceClass, urls.get(0));//本例通過配置一個注冊中心的形式(***看這里***)} else {//多個直連地址或者多個注冊中心地址,甚至是兩者的組合。List<Invoker<?>> invokers = new ArrayList<Invoker<?>>();URL registryURL = null;for (URL url : urls) {//創建invoker放入invokersinvokers.add(refprotocol.refer(interfaceClass, url));if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {registryURL = url; // 多個注冊中心,用最后一個registry url}}if (registryURL != null) { //有注冊中心協議的URL,//對多個url,其中存在有注冊中心的,寫死用AvailableCluster集群策略//這其中包括直連和注冊中心混合或者都是注冊中心兩種情況URL u = registryURL.addParameter(Constants.CLUSTER_KEY, AvailableCluster.NAME);invoker = cluster.join(new StaticDirectory(u, invokers));} else { // 多個直連的urlinvoker = cluster.join(new StaticDirectory(invokers));}}
  • 上面的代碼可以看出生成Invoker的有三種方式,
    • 第一種是refprotocol.refer(this.interfaceClass, (URL),這里的接口用的是SPI機制的dubbo對應下面的那個@SPI注解=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol這一句,所以這一種主要是使用的是Dubbo直連的協議都會走這一層
    • 第二種是 cluster.join(new StaticDirectory(u, invokers));,這個是加了路由的負載均衡相關的,這一部分主要都是路由層的東西
    • 第三種是 this.invoker = cluster.join(new StaticDirectory(invokers));也是路由層的東西, 路由層:封裝多個提供者的路由及負載均衡,并橋接注冊中心,以 Invoker 為中心,擴展接口為 Cluster, Directory, Router, LoadBalance
  • @SPI("dubbo") public interface Protocol {int getDefaultPort();@Adaptive<T> Exporter<T> export(Invoker<T> var1) throws RpcException;@Adaptive<T> Invoker<T> refer(Class<T> var1, URL var2) throws RpcException;void destroy(); }

    因為Dubbo中在實現遠程調用的時候其實是通過Poxy對象生成的Invoker對象,那么就先看一下Invoker的怎么生成的,里面都包含了什么信息?

    • 這里的invoker對象,通過InvokerInvocationHandler構造方法傳入,而InvokerInvocationHandler對象是由JavassistProxyFactory類
      getProxy(Invoker invoker, Class<?>[] interfaces)方法創建

    Dubbo中的SPI機制的使用和分析

    參考文章:https://cloud.tencent.com/developer/article/1109459

    總結

    以上是生活随笔為你收集整理的Dubbo是如何进行远程服务调用的?(源码流程跟踪)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。