成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

關于Dubbo隨便問八個問題

開發 前端
互聯網公司的系統有成千上萬個大大小小的服務組成,服務各自部署在不同的機器上,服務間的調用需要用到網絡通信,服務消費方每調用一個服務都要寫一坨網絡通信相關的代碼,不僅復雜而且極易出錯。

[[374925]]

本文轉載自微信公眾號「sowhat1412 」,作者sowhat1412 。轉載本文請聯系sowhat1412 公眾號。   

1、RPC

1.1 RPC 定義

互聯網公司的系統有成千上萬個大大小小的服務組成,服務各自部署在不同的機器上,服務間的調用需要用到網絡通信,服務消費方每調用一個服務都要寫一坨網絡通信相關的代碼,不僅復雜而且極易出錯。還要考慮新服務依賴老服務時如何調用老服務,別的服務依賴新服務的時候新服務如何發布方便他人調用。如何解決這個問題呢?業界一般采用RPC遠程調用的方式來實現。

RPC:

Remote Procedure Call Protocol 既 遠程過程調用,一種能讓我們像調用本地服務一樣調用遠程服務,可以讓調用者對網絡通信這些細節無感知,比如服務消費方在執行 helloWorldService.sayHello("sowhat") 時,實質上調用的是遠端的服務。這種方式其實就是RPC,RPC思想在各大互聯網公司中被廣泛使用,如阿里巴巴的dubbo、當當的Dubbox 、Facebook 的 thrift、Google 的grpc、Twitter的finagle等。

1.2 RPC demo

說了那么多,還是實現一個簡易版的RPC demo吧。

1.2.1 公共接口

  1. public interface SoWhatService { 
  2.     String sayHello(String name);   
  3. }  

1.2.2 服務提供者

接口類實現

  1. public class SoWhatServiceImpl implements SoWhatService 
  2.  @Override 
  3.  public String sayHello(String name
  4.  { 
  5.   return "你好啊 " + name
  6.  } 
  7. }   

服務注冊對外提供者

  1. /** 
  2.  * 服務注冊對外提供者 
  3.  */ 
  4.  
  5. public class ServiceFramework 
  6.  public static void export(Object service, int port) throws Exception 
  7.  { 
  8.   ServerSocket server = new ServerSocket(port); 
  9.   while (true
  10.   { 
  11.    Socket socket = server.accept(); 
  12.    new Thread(() -> 
  13.    { 
  14.     try 
  15.     { 
  16.      //反序列化 
  17.      ObjectInputStream input = new ObjectInputStream(socket.getInputStream()); 
  18.      //讀取方法名 
  19.      String methodName =(String) input.readObject(); 
  20.      //參數類型 
  21.      Class<?>[] parameterTypes = (Class<?>[]) input.readObject(); 
  22.      //參數 
  23.      Object[] arguments = (Object[]) input.readObject(); 
  24.      //找到方法 
  25.      Method method = service.getClass().getMethod(methodName, parameterTypes); 
  26.      //調用方法 
  27.      Object result = method.invoke(service, arguments); 
  28.      // 返回結果 
  29.      ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream()); 
  30.      output.writeObject(result); 
  31.     } catch (Exception e) 
  32.     { 
  33.      e.printStackTrace(); 
  34.     } 
  35.    }).start(); 
  36.   } 
  37.  } 

服務運行

  1. public class ServerMain 
  2.  public static void main(String[] args) 
  3.  { 
  4.   //服務提供者 暴露出接口 
  5.   SoWhatService service = new SoWhatServiceImpl(); 
  6.   try 
  7.   { 
  8.    ServiceFramework.export(service, 1412); 
  9.   } catch (Exception e) 
  10.   { 
  11.    e.printStackTrace(); 
  12.   } 
  13.  } 

1.2.3 服務調用者

動態代理調用遠程服務

  1. /** 
  2.  * @author sowhat 
  3.  * 動態代理調用遠程服務 
  4.  */ 
  5. public class RpcFunction 
  6.  public static <T> T refer(Class<T> interfaceClass, String host, int port) throws Exception 
  7.  { 
  8.   return (T) Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class<?>[]{interfaceClass}, 
  9.     new InvocationHandler() 
  10.     { 
  11.      @Override 
  12.      public Object invoke(Object proxy, Method method, Object[] arguments) throws Throwable 
  13.      { 
  14.       //指定 provider 的 ip 和端口 
  15.       Socket socket = new Socket(host, port); 
  16.       ObjectOutputStream output = new ObjectOutputStream(socket.getOutputStream()); 
  17.       //傳方法名 
  18.       output.writeObject(method.getName()); 
  19.       //傳參數類型 
  20.       output.writeObject(method.getParameterTypes()); 
  21.       //傳參數值 
  22.       output.writeObject(arguments); 
  23.       ObjectInputStream input = new ObjectInputStream(socket.getInputStream()); 
  24.       //讀取結果 
  25.       Object result = input.readObject(); 
  26.       return result; 
  27.      } 
  28.     }); 
  29.  } 

調用方法

  1. public class RunMain 
  2.  public static void main(String[] args) 
  3.  { 
  4.   try 
  5.   { 
  6.    //服務調用者 需要設置依賴 
  7.    SoWhatService service = RpcFunction.refer(SoWhatService.class, "127.0.0.1", 1412); 
  8.    System.out.println(service.sayHello(" sowhat1412")); 
  9.   } catch (Exception e) 
  10.   { 
  11.    e.printStackTrace(); 
  12.   } 
  13.  } 

2、Dubbo 框架設計

2.1 Dubbo 簡介

Dubbo 是阿里巴巴研發開源工具,主要分為2.6.x 跟 2.7.x 版本。是一款分布式、高性能、透明化的 RPC 服務框架,提供服務自動注冊、自動發現等高效服務治理方案,可以和Spring 框架無縫集成,它提供了6大核心能力:

1. 面向接口代理的高性能RPC調用

2. 智能容錯和負載均衡

3. 服務自動注冊和發現

4. 高度可擴展能力

5. 運行期流量調度

6. 可視化的服務治理與運維

調用過程:

  1. 服務提供者 Provider 啟動然后向 Registry 注冊自己所能提供的服務。
  2. 服務消費者 Consumer 向Registry訂閱所需服務,Consumer 解析Registry提供的元信息,從服務中通過負載均衡選擇 Provider調用。
  3. 服務提供方 Provider 元數據變更的話Registry會把變更推送給Consumer,以此保證Consumer獲得最新可用信息。

注意點:

Provider 跟 Consumer 在內存中記錄調用次數跟時間,定時發送統計數據到Monitor,發送的時候是短連接。

Monitor 跟 Registry 是可選的,可直接在配置文件中寫好,Provider 跟 Consumer進行直連。

Monitor 跟 Registry 掛了也沒事, Consumer 本地緩存了 Provider 信息。

Consumer 直接調用 Provider 不會經過 Registry。Provider、Consumer這倆到 Registry之間是長連接。

2.2 Dubbo框架分層

如上圖,總的而言 Dubbo 分為三層。

  1. Busines層:由用戶自己來提供接口和實現還有一些配置信息。
  2. RPC層:真正的RPC調用的核心層,封裝整個RPC的調用過程、負載均衡、集群容錯、代理。
  3. Remoting層:對網絡傳輸協議和數據轉換的封裝。

如果每一層再細分下去,一共有十層。

  1. 接口服務層(Service):該層與業務邏輯相關,根據 provider 和 consumer 的業務設計對應的接口和實現。
  2. 配置層(Config):對外配置接口,以 ServiceConfig 和 ReferenceConfig 為中心初始化配置。
  3. 服務代理層(Proxy):服務接口透明代理,Provider跟Consumer都生成代理類,使得服務接口透明,代理層實現服務調用跟結果返回。
  4. 服務注冊層(Registry):封裝服務地址的注冊和發現,以服務 URL 為中心。
  5. 路由層(Cluster):封裝多個提供者的路由和負載均衡,并橋接注冊中心,以Invoker 為中心,擴展接口為 Cluster、Directory、Router 和 LoadBlancce。
  6. 監控層(Monitor):RPC 調用次數和調用時間監控,以 Statistics 為中心,擴展接口為 MonitorFactory、Monitor 和 MonitorService。
  7. 遠程調用層(Protocal):封裝 RPC 調用,以 Invocation 和 Result 為中心,擴展接口為 Protocal、Invoker 和 Exporter。
  8. 信息交換層(Exchange):封裝請求響應模式,同步轉異步。以 Request 和Response 為中心,擴展接口為 Exchanger、ExchangeChannel、ExchangeClient 和 ExchangeServer。
  9. 網絡傳輸層(Transport):抽象 mina 和 netty 為統一接口,以 Message 為中心,擴展接口為 Channel、Transporter、Client、Server 和 Codec。
  10. 數據序列化層(Serialize):可復用的一些工具,擴展接口為 Serialization、ObjectInput、ObjectOutput 和 ThreadPool。

他們之間的調用關系直接看下面官網圖即可。

3、Dubbo SPI 機制

Dubbo 采用 微內核設計 + SPI 擴展技術來搭好核心框架,同時滿足用戶定制化需求。這里重點說下SPI。

3.1 微內核

操作系統層面的微內核跟宏內核:

微內核Microkernel:是一種內核的設計架構,由盡可能精簡的程序所組成,以實現一個操作系統所需要的最基本功能,包括了底層的尋址空間管理、線程管理、與進程間通信。成功案例是QNX系統,比如黑莓手機跟車用市場。

宏內核Monolithic :把 進程管理、內存管理、文件系統、進程通信等功能全部作為內核來實現,而微內核則僅保留最基礎的功能,Linux 就是宏內核架構設計。

Dubbo中的廣義微內核:

思想是 核心系統 + 插件,說白了就是把不變的功能抽象出來稱為核心,把變動的功能作為插件來擴展,符合開閉原則,更容易擴展、維護。比如小霸王游戲機中機體本身作為核心系統,游戲片就是插件。vscode、Idea、chrome等都是微內核的產物。

微內核架構其實是一直架構思想,可以是框架層面也可以是某個模塊設計,它的本質就是將變化的部分抽象成插件,使得可以快速簡便地滿足各種需求又不影響整體的穩定性。

3.2 SPI 含義

主流的數據庫有MySQL、Oracle、DB2等,這些數據庫是不同公司開發的,它們的底層協議不大一樣,那怎么約束呢?一般就是定制統一接口,具體實現不管,反正面向相同的接口編程即可。等到真正使用的時候用具體的實現類就好,問題是哪里找用那個實現類呢?這時候就采用約定好的法則將實現類寫到指定位置即可。

SPI 全稱為 Service Provider Interface,是一種服務發現機制。它約定在ClassPath路徑下的META-INF/services文件夾查找文件,自動加載文件里所定義的類。

3.3 SPI demo

接口:

  1. package com.example.demo.spi; 
  2.  
  3. public interface SPIService { 
  4.     void execute(); 

實現類1:

  1. public class SpiImpl1 implements SPIService{ 
  2.  @Override 
  3.     public void execute() { 
  4.         System.out.println("SpiImpl1.execute()"); 
  5.     } 

實現類2:

  1. public class SpiImpl2 implements SPIService{ 
  2.  @Override 
  3.     public void execute() { 
  4.   System.out.println("SpiImpl2.execute()"); 
  5.     } 

配置路徑

調用加載類

  1. package com.example.demo.spi; 
  2. import sun.misc.Service; 
  3. import java.util.Iterator; 
  4. import java.util.ServiceLoader; 
  5.  
  6. public class Test { 
  7.     public static void main(String[] args) {     
  8.         Iterator<SPIService> providers = Service.providers(SPIService.class); 
  9.         ServiceLoader<SPIService> load = ServiceLoader.load(SPIService.class); 
  10.  
  11.         while(providers.hasNext()) { 
  12.             SPIService ser = providers.next(); 
  13.             ser.execute(); 
  14.         } 
  15.         System.out.println("--------------------------------"); 
  16.         Iterator<SPIService> iterator = load.iterator(); 
  17.         while(iterator.hasNext()) { 
  18.             SPIService ser = iterator.next(); 
  19.             ser.execute(); 
  20.         } 
  21.     } 

3.4 SPI源碼追蹤

ServiceLoader.load(SPIService.class) 底層調用大致邏輯如下:圖片iterator.hasNext() 跟 iterator.next()底層調用大致如下:

3.5 Java SPI缺點

不能按需加載,Java SPI在加載擴展點的時候,會一次性加載所有可用的擴展點,很多是不需要的,會浪費系統資源。

獲取某個實現類的方式不夠靈活,只能通過 Iterator 形式獲取,不能根據某個參數來獲取對應的實現類。

不支持AOP與依賴注入,JAVA SPI可能會丟失加載擴展點異常信息,導致追蹤問題很困難。

3.6 Dubbo SPI

JDK自帶的不好用Dubbo 就自己實現了一個 SPI,該SPI 可以通過名字實例化指定的實現類,并且實現了 IOC 、AOP 與 自適應擴展 SPI 。

  1. key = com.sowhat.value 

Dubbo 對配置文件目錄的約定,不同于 Java SPI ,Dubbo 分為了三類目錄。

META-INF/services/ :該目錄下 SPI 配置文件是為了用來兼容 Java SPI 。

META-INF/dubbo/ :該目錄存放用戶自定義的 SPI 配置文件。

META-INF/dubbo/internal/ :該目錄存 Dubbo 內部使用的 SPI 配置文件。

使用的話很簡單 引入依賴,然后百度教程即可。

  1. @Test 
  2.  void sowhat() 
  3.  { 
  4.   ExtensionLoader<SPIService> spiService = ExtensionLoader.getExtensionLoader(SPIService.class);        //按需獲取實現類對象 
  5.   SPIService demo1 = spiService.getExtension("SpiImpl1"); 
  6.   demo1.execute(); 
  7.  } 

3.7 Dubbo SPI源碼追蹤

ExtensionLoader.getExtension 方法的整個思路是 查找緩存是否存在,不存在則讀取SPI文件,通過反射創建類,然后設置依賴注入這些東西,有包裝類就包裝下,執行流程如下圖所示:

說下重要的四個部分:

1.injectExtension IOC

查找 set 方法,根據參數找到依賴對象則注入。

2.WrapperClass AOP

包裝類,Dubbo 幫你自動包裝,只需要某個擴展類的構造函數只有一個參數,并且是擴展接口類型,就會被判定為包裝類。

3.Activate

Active 有三個屬性,group 表示修飾在哪個端,是 provider 還是 consumer,value 表示在 URL參數中出現才會被激活,order 表示實現類的順序。

3.8 Adaptive 自適應擴展

需求:根據配置來進行 SPI 擴展的加載后不想在啟動的時候讓擴展被加載,想根據請求時候的參數來動態選擇對應的擴展。實現:Dubbo用代理機制實現了自適應擴展,為用戶想擴展的接口 通過JDK 或者 Javassist 編譯生成一個代理類,然后通過反射創建實例。實例會根據本來方法的請求參數得知需要的擴展類,然后通過 ExtensionLoader.getExtensionLoader(type.class).getExtension(name)來獲取真正的實例來調用,看個官網樣例。

  1. public interface WheelMaker { 
  2.     Wheel makeWheel(URL url); 
  3. // WheelMaker 接口的自適應實現類 
  4. public class AdaptiveWheelMaker implements WheelMaker { 
  5.     public Wheel makeWheel(URL url) { 
  6.         if (url == null) { 
  7.             throw new IllegalArgumentException("url == null"); 
  8.         } 
  9.      // 1. 調用 url 的 getXXX 方法獲取參數值 
  10.         String wheelMakerName = url.getParameter("Wheel.maker"); 
  11.         if (wheelMakerName == null) { 
  12.             throw new IllegalArgumentException("wheelMakerName == null"); 
  13.         } 
  14.         // 2. 調用 ExtensionLoader 的 getExtensionLoader 獲取加載器 
  15.         // 3. 調用 ExtensionLoader 的 getExtension 根據從url獲取的參數作為類名稱加載實現類 
  16.         WheelMaker wheelMaker = ExtensionLoader.getExtensionLoader(WheelMaker.class).getExtension(wheelMakerName); 
  17.         // 4. 調用實現類的具體方法實現調用。 
  18.         return wheelMaker.makeWheel(URL url); 
  19.     } 

查看Adaptive注解源碼可知該注解可用在類或方法上,Adaptive 注解在類上或者方法上有不同的實現邏輯。

7.8.1 Adaptive 注解在類上

Adaptive 注解在類上時,Dubbo 不會為該類生成代理類,Adaptive 注解在類上的情況很少,在 Dubbo 中,僅有兩個類被 Adaptive 注解了,分別是 AdaptiveCompiler 和 AdaptiveExtensionFactory,表示拓展的加載邏輯由人工編碼完成,這不是我們關注的重點。

7.8.2 Adaptive 注解在方法上

Adaptive 注解在方法上時,Dubbo 則會為該方法生成代理邏輯,表示拓展的加載邏輯需由框架自動生成,大致的實現機制如下:

加載標注有 @Adaptive 注解的接口,如果不存在,則不支持 Adaptive 機制;

為目標接口按照一定的模板生成子類代碼,并且編譯生成的代碼,然后通過反射生成該類的對象;

結合生成的對象實例,通過傳入的URL對象,獲取指定key的配置,然后加載該key對應的類對象,最終將調用委托給該類對象進行。

  1. @SPI("apple"
  2. public interface FruitGranter { 
  3.   Fruit grant(); 
  4.   @Adaptive 
  5.   String watering(URL url); 
  6. --- 
  7. // 蘋果種植者 
  8. public class AppleGranter implements FruitGranter { 
  9.   @Override 
  10.   public Fruit grant() { 
  11.     return new Apple(); 
  12.   } 
  13.   @Override 
  14.   public String watering(URL url) { 
  15.     System.out.println("watering apple"); 
  16.     return "watering finished"
  17.   } 
  18. --- 
  19. // 香蕉種植者 
  20. public class BananaGranter implements FruitGranter { 
  21.   @Override 
  22.   public Fruit grant() { 
  23.     return new Banana(); 
  24.   } 
  25.   @Override 
  26.   public String watering(URL url) { 
  27.     System.out.println("watering banana"); 
  28.     return "watering success"
  29.   } 

調用方法實現:

  1. public class ExtensionLoaderTest { 
  2.   @Test 
  3.   public void testGetExtensionLoader() { 
  4.     // 首先創建一個模擬用的URL對象 
  5.     URL url = URL.valueOf("dubbo://192.168.0.1:1412?fruit.granter=apple"); 
  6.     // 通過ExtensionLoader獲取一個FruitGranter對象 
  7.     FruitGranter granter = ExtensionLoader.getExtensionLoader(FruitGranter.class) 
  8.       .getAdaptiveExtension(); 
  9.     // 使用該FruitGranter調用其"自適應標注的"方法,獲取調用結果 
  10.     String result = granter.watering(url); 
  11.     System.out.println(result); 
  12.   } 

通過如上方式生成一個內部類。大致調用流程如下:

4、Dubbo 服務暴露流程

4.1 服務暴露總覽

Dubbo框架是以URL為總線的模式,運行過程中所有的狀態數據信息都可以通過URL來獲取,比如當前系統采用什么序列化,采用什么通信,采用什么負載均衡等信息,都是通過URL的參數來呈現的,所以在框架運行過程中,運行到某個階段需要相應的數據,都可以通過對應的Key從URL的參數列表中獲取。URL 具體的參數如下:

protocol:指的是 dubbo 中的各種協議,如:dubbo thrift http username/password:用戶名/密碼 host/port:主機/端口 path:接口的名稱 parameters:參數鍵值對

  1. protocol://username:password@host:port/path?k=v 

服務暴露從代碼流程看分為三部分:

  1. 檢查配置,最終組裝成 URL。
  2. 暴露服務到到本地服務跟遠程服務。
  3. 服務注冊至注冊中心。

服務暴露從對象構建轉換看分為兩步:

  1. 將服務封裝成Invoker。
  2. 將Invoker通過協議轉換為Exporter。

4.2 服務暴露源碼追蹤

  1. 容器啟動,Spring IOC 刷新完畢后調用 onApplicationEvent 開啟服務暴露,ServiceBean 。
  2. export 跟 doExport 來進行拼接構建URL,為屏蔽調用的細節,統一暴露出一個可執行體,通過ProxyFactory 獲取到 invoker。
  3. 調用具體 Protocol 將把包裝后的 invoker 轉換成 exporter,此處用到了SPI。
  4. 然后啟動服務器server,監聽端口,使用NettyServer創建監聽服務器。
  5. 通過 RegistryProtocol 將URL注冊到注冊中心,使得consumer可獲得provider信息。圖片

5、Dubbo 服務引用流程

Dubbo中一個可執行體就是一個invoker,所以 provider 跟 consumer 都要向 invoker 靠攏。通過上面demo可知為了無感調用遠程接口,底層需要有個代理類包裝 invoker。

服務的引入時機有兩種:

餓漢式:

通過實現 Spring 的 InitializingBean 接口中的 afterPropertiesSet 方法,容器通過調用 ReferenceBean的 afterPropertiesSet 方法時引入服務。

懶漢式(默認):

懶漢式是只有當服務被注入到其他類中時啟動引入流程。

服務引用的三種方式:

  1. 本地引入:服務暴露時本地暴露,避免網絡調用開銷。
  2. 直接連接引入遠程服務:不啟動注冊中心,直接寫死遠程Provider地址 進行直連。
  3. 通過注冊中心引入遠程服務:通過注冊中心抉擇如何進行負載均衡調用遠程服務。

服務引用流程:

  1. 檢查配置構建map ,map 構建 URL ,通過URL上的協議利用自適應擴展機制調用對應的 protocol.refer 得到相應的 invoker ,此處
  2. 想注冊中心注冊自己,然后訂閱注冊中心相關信息,得到provider的 ip 等信息,再通過共享的netty客戶端進行連接。
  3. 當有多個 URL 時,先遍歷構建出 invoker 然后再由 StaticDirectory 封裝一下,然后通過 cluster 進行合并,只暴露出一個 invoker 。
  4. 然后再構建代理,封裝 invoker 返回服務引用,之后 Comsumer 調用的就是這個代理類。

 

調用方式:

oneway:不關心請求是否發送成功。

Async異步調用:Dubbo天然異步,客戶端調用請求后將返回的 ResponseFuture 存到上下文中,用戶可隨時調用 future.get 獲取結果。異步調用通過唯一ID 標識此次請求。

Sync同步調用:在 Dubbo 源碼中就調用了 future.get,用戶感覺方法被阻塞了,必須等結果后才返回。

6、Dubbo 調用整體流程

調用之前你可能需要考慮這些事:

  1. consumer 跟 provider 約定好通訊協議,dubbo支持多種協議,比如dubbo、rmi、hessian、http、webservice等。默認走dubbo協議,連接屬于單一長連接,NIO異步通信。適用傳輸數據量很小(單次請求在100kb以內),但是并發量很高。
  2. 約定序列化模式,大致分為兩大類,一種是字符型(XML或json 人可看懂 但傳輸效率低),一種是二進制流(數據緊湊,機器友好)。默認使用 hessian2作為序列化協議。
  3. consumer 調用 provider 時提供對應接口、方法名、參數類型、參數值、版本號。
  4. provider列表對外提供服務涉及到負載均衡選擇一個provider提供服務。
  5. consumer 跟 provider 定時向monitor 發送信息。

調用大致流程:

  1. 客戶端發起請求來調用接口,接口調用生成的代理類。代理類生成RpcInvocation 然后調用invoke方法。
  2. ClusterInvoker獲得注冊中心中服務列表,通過負載均衡給出一個可用的invoker。
  3. 序列化跟反序列化網絡傳輸數據。通過NettyServer調用網絡服務。
  4. 服務端業務線程池接受解析數據,從exportMap找到invoker進行invoke。
  5. 調用真正的Impl得到結果然后返回。

調用方式:

oneway:不關心請求是否發送成功,消耗最小。

sync同步調用:在 Dubbo 源碼中就調用了 future.get,用戶感覺方法被阻塞了,必須等結果后才返回。

Async 異步調用:Dubbo天然異步,客戶端調用請求后將返回的 ResponseFuture 存到上下文中,用戶可以隨時調用future.get獲取結果。異步調用通過唯一ID標識此次請求。

7、Dubbo集群容錯負載均衡

Dubbo 引入了Cluster、Directory、Router、LoadBalance、Invoker模塊來保證Dubbo系統的穩健性,它們的關系如下圖:

服務發現時會將多個多個遠程調用放入Directory,然后通過Cluster封裝成一個Invoker,該invoker提供容錯功能。

消費者代用的時候從Directory中通過負載均衡獲得一個可用invoker,最后發起調用。

你可以認為Dubbo中的Cluster對上面進行了大的封裝,自帶各種魯棒性功能。

7.1 集群容錯

集群容錯是在消費者端通過Cluster子類實現的,Cluster接口有10個實現類,每個Cluster實現類都會創建一個對應的ClusterInvoker對象。核心思想是讓用戶選擇性調用這個Cluster中間層,屏蔽后面具體實現細節。

Cluster Cluster Invoker 作用
FailoverCluster FailoverClusterInvoker 失敗自動切換功能,默認
FailfastCluster FailfastClusterInvoker 一次調用,失敗異常
FailsafeCluster FailsafeClusterInvoker 調用出錯則日志記錄
FailbackCluster FailbackClusterInvoker 失敗返空,定時重試2次
ForkingCluster ForkingClusterInvoker 一個任務并發調用,一個OK則OK
BroadcastCluster BroadcastClusterInvoker 逐個調用invoker,全可用才可用
AvailableCluster AvailableClusterInvoker 哪個能用就用那個
MergeableCluster MergeableClusterInvoker 按組合并返回結果

7.2 智能容錯之負載均衡

Dubbo中一般有4種負載均衡策略。

  1. RandomLoadBalance:加權隨機,它的算法思想簡單。假設有一組服務器 servers = [A, B, C],對應權重為 weights = [5, 3, 2],權重總和為10。現把這些權重值平鋪在一維坐標值上,[0, 5) 區間屬于服務器 A,[5, 8) 區間屬于服務器 B,[8, 10) 區間屬于服務器 C。接下來通過隨機數生成器生成一個范圍在 [0, 10) 之間的隨機數,然后計算這個隨機數會落到哪個區間上。默認實現。
  2. LeastActiveLoadBalance:最少活躍數負載均衡,選擇現在活躍調用數最少的提供者進行調用,活躍的調用數少說明它現在很輕松,而且活躍數都是從 0 加起來的,來一個請求活躍數+1,一個請求處理完成活躍數-1,所以活躍數少也能變相的體現處理的快。
  3. RoundRobinLoadBalance:加權輪詢負載均衡,比如現在有兩臺服務器 A、B,輪詢的調用順序就是 A、B、A、B,如果加了權重,A 比B 的權重是2:1,那現在的調用順序就是 A、A、B、A、A、B。
  4. ConsistentHashLoadBalance:一致性 Hash 負載均衡,將服務器的 IP 等信息生成一個 hash 值,將hash 值投射到圓環上作為一個節點,然后當 key 來查找的時候順時針查找第一個大于等于這個 key 的 hash 值的節點。一般而言還會引入虛擬節點,使得數據更加的分散,避免數據傾斜壓垮某個節點。如下圖 Dubbo 默認搞了 160 個虛擬節點。圖片

7.3 智能容錯之服務目錄

關于 服務目錄Directory 你可以理解為是相同服務Invoker的集合,核心是RegistryDirectory類。具有三個功能。

從注冊中心獲得invoker列表。

監控著注冊中心invoker的變化,invoker的上下線。

刷新invokers列表到服務目錄。

7.4 智能容錯之服務路由

服務路由其實就是路由規則,它規定了服務消費者可以調用哪些服務提供者。條件路由規則由兩個條件組成,分別用于對服務消費者和提供者進行匹配。比如有這樣一條規則:

  1. host = 10.20.153.14 => host = 10.20.153.12 

該條規則表示 IP 為 10.20.153.14 的服務消費者只可調用 IP 為 10.20.153.12 機器上的服務,不可調用其他機器上的服務。條件路由規則的格式如下:

  1. [服務消費者匹配條件] => [服務提供者匹配條件] 

如果服務消費者匹配條件為空,表示不對服務消費者進行限制。如果服務提供者匹配條件為空,表示對某些服務消費者禁用服務。

8、設計RPC

通讀下Dubbo的大致實現方式后其實就可以依葫蘆畫瓢了,一個RPC框架大致需要下面這些東西:

服務的注冊跟發現的搞一個吧,你可以用ZooKeeper或者Redis來實現。

接下來consumer發起請求的時候你的面向接口編程啊,用到動態代理來實現調用。

多個provider提供相同服務你的用到LoadBalance啊。

最終選擇一個機器后你的約定好通信協議啊,如何進行序列化跟反序列化呢?

底層就用現成的高性能Netty框架 NIO模式實現唄。

服務開啟后的有monitor啊。

PS :

感覺沒啥特別好寫的,因為人Dubbo官方文檔啥都有,你說你英文看不懂,那中文總該看得懂了吧。

參考

Dubbo面試題:https://sowhat.blog.csdn.net/article/details/71191035

Adaptive講解:https://blog.csdn.net/weixin_33967071/article/details/92608993 Dubbo視頻:https://b23.tv/KVk0xo

Dubbo demo:https://mp.weixin.qq.com/s/FPbu8rFOHyTGROIV8XJeTA doExportUrlsFor1Protocol詳解:https://www.cnblogs.com/hzhuxin/p/7993860.html

 

責任編輯:武曉燕 來源: sowhat1412
相關推薦

2023-02-22 14:50:59

技術AI

2025-02-07 15:01:49

Promise數組前端

2024-03-06 13:56:00

項目awaitpromise

2014-06-17 09:51:57

Docker

2024-01-02 16:16:34

Promise前端

2019-11-27 11:06:30

災難DDoS勒索軟件

2009-03-26 09:39:16

CSS網頁布局

2010-09-27 13:41:49

TCP IP故障問題

2023-08-09 14:01:55

2022-11-04 15:37:04

產品策略開發競爭

2009-12-04 15:33:42

安裝Windows 7

2010-08-31 10:49:16

CSS網頁布局

2019-10-18 15:16:10

Redis數據庫并發

2023-05-18 14:06:51

人工智能AI

2012-10-15 09:36:23

2017-01-05 09:59:45

2012-10-29 11:01:17

2017-04-20 12:51:28

2023-02-27 09:08:10

IT文化步驟

2022-12-01 16:53:27

NPM技巧
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91国内视频在线 | 国产农村妇女毛片精品久久麻豆 | 在线观看日本网站 | 亚洲看片| 成人免费看 | h片在线免费看 | 在线视频中文字幕 | 在线a视频 | 国产精品久久av | 欧美日韩视频在线 | 三极网站 | 男人av在线播放 | 亚洲高清免费视频 | 成人精品国产一区二区4080 | www.av在线| 男女羞羞视频在线 | 草久在线 | 国产一区不卡 | 在线不卡视频 | av网站在线免费观看 | 国产精品呻吟久久av凹凸 | 久久精品成人 | 欧美性生活一区二区三区 | 一区二区在线 | 免费在线观看一区二区三区 | 欧美日韩精品久久久免费观看 | 久久99精品久久久久婷婷 | 欧美在线观看一区 | 午夜成人在线视频 | 午夜影视网| 国产一区二区精品在线观看 | 中文字幕日韩欧美一区二区三区 | 黄色网址在线免费观看 | 日韩一区二区三区在线看 | 自拍偷拍亚洲欧美 | 色综合av | 宅女噜噜66国产精品观看免费 | 欧美日韩在线一区 | 欧美日韩一区二区三区四区 | 日韩色视频 | 久久新 |